I'm building a web application based on an embeded Tomcat - 7.0.55, Spring-Boot-1.1.6, Spring-webmvc/core - 4.0.7 and Spring-Security - 3.2.5.
My configuration looks like this:
#Configuration
public class ServletCtxConfig {
#Bean
#Profile({ Profiles.PRODUCTION, Profiles.QA, Profiles.DEV })
EmbeddedServletContainerFactory servletContainerFactory() {
TomcatEmbeddedServletContainerFactory retVal = new TomcatEmbeddedServletContainerFactory();
retVal.setContextPath("contextPath");
retVal.setTomcatContextCustomizers(Arrays.asList(contextCustomizer()));
retVal.setPort(111);
Connector httpConnector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
httpConnector.setPort(123);
httpConnector.setRedirectPort(456);
retVal.addAdditionalTomcatConnectors(httpConnector);
return retVal;
}
#Bean
CustomCustomizer contextCustomizer() {
return new CustomCustomizer();
}
}
class CustomCustomizer implements TomcatContextCustomizer {
#Value("${session.timeout:10080}")
Integer sessionTimeOut;
#Override
public void customize(Context context) {
context.setSessionCookieName("comilion-fw");
context.setSessionTimeout(sessionTimeOut);
context.setUseHttpOnly(false);
}
}
I am able to set the session expiration time but it is not reflected on the cookie expiration time on the browser.
Can some one please instruct me how to set the cookie expiration time?
Try to access the servlet context during a web app init stage and set the value like this:
servletContext.getSessionCookieConfig().setMaxAge(600);
Have a look at WebApplicationInitializer and SpringServletContainerInitializer
And if you still somehow run web app using web.xml here you go jsessionid-cookie-with-expiration-date-in-tomcat
What I'v done eventually, is customizing the EmbeddedServletContainerFactory as follow:
#Bean
EmbeddedServletContainerFactory servletContainerFactory() {
logger.debug("Raising Embedded servlet container with port: ", port, " and context path: ", contextPath);
TomcatEmbeddedServletContainerFactory retVal = new TomcatEmbeddedServletContainerFactory() {
#Override
protected void postProcessContext(Context context) {
SecurityConstraint securityConstraint = new SecurityConstraint();
securityConstraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/*");
securityConstraint.addCollection(collection);
context.addConstraint(securityConstraint);
}
};
retVal.setContextPath(contextPath);
retVal.setTomcatContextCustomizers(Arrays.asList(contextCustomizer()));
retVal.addAdditionalTomcatConnectors(this.createConnection());
return retVal;
}
#Bean
CustomCustomizer contextCustomizer() {
return new CustomCustomizer();
}
class CustomCustomizer implements TomcatContextCustomizer {
#Value(Properties.$_SESSION_TIMEOUT)
Integer sessionTimeOut;
#Override
public void customize(Context context) {
context.setSessionCookieName("XXX");
context.setSessionTimeout(sessionTimeOut);
}
}
Related
I am attempting to authorize against an external identity provider. Everything seems setup fine, but I keep getting a validation error with my identity provider because the state parameter automatically tacked onto my authorization request is not long enough:
For example:
&state=uYG5DC
The requirements of my IDP say that this state param must be at least 32-characters long. How can I programmatically increase the size of this auto-generated number?
Even if I could generate this number myself, it is not possible to override with other methods I have seen suggested. The following attempt fails because my manual setting of ?state=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz is superceded by the autogenerated param placed after it during the actual request:
#Bean
public OAuth2ProtectedResourceDetails loginGovOpenId() {
AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails() {
#Override
public String getUserAuthorizationUri() {
return super.getUserAuthorizationUri() + "?state=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz";
}
};
details.setClientId(clientId);
details.setAccessTokenUri(accessTokenUri);
details.setUserAuthorizationUri(userAuthorizationUri);
details.setScope(Arrays.asList("openid", "email"));
details.setPreEstablishedRedirectUri(redirectUri);
details.setUseCurrentUri(true);
return details;
}
The 6-character setting seems to be set here, is there a way to override this?
https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/common/util/RandomValueStringGenerator.java
With the help of this post:
spring security StateKeyGenerator custom instance
I was able to come up with a working solution.
In my configuration class marked with these annotations:
#Configuration
#EnableOAuth2Client
I configured the following beans:
#Bean
public OAuth2ProtectedResourceDetails loginGovOpenId() {
AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
AuthorizationCodeResourceDetails details = new
details.setClientId(clientId);
details.setClientSecret(clientSecret);
details.setAccessTokenUri(accessTokenUri);
details.setUserAuthorizationUri(userAuthorizationUri);
details.setScope(Arrays.asList("openid", "email"));
details.setPreEstablishedRedirectUri(redirectUri);
details.setUseCurrentUri(true);
return details;
}
#Bean
public StateKeyGenerator stateKeyGenerator() {
return new CustomStateKeyGenerator();
}
#Bean
public AccessTokenProvider accessTokenProvider() {
AuthorizationCodeAccessTokenProvider accessTokenProvider = new AuthorizationCodeAccessTokenProvider();
accessTokenProvider.setStateKeyGenerator(stateKeyGenerator());
return accessTokenProvider;
}
#Bean
public OAuth2RestTemplate loginGovOpenIdTemplate(final OAuth2ClientContext clientContext) {
final OAuth2RestTemplate template = new OAuth2RestTemplate(loginGovOpenId(), clientContext);
template.setAccessTokenProvider(accessTokenProvider());
return template;
}
Where my CustomStateKeyGenerator implementation class looks as follows:
public class CustomStateKeyGenerator implements StateKeyGenerator {
// login.gov requires state to be at least 32-characters long
private static int length = 32;
private RandomValueStringGenerator generator = new RandomValueStringGenerator(length);
#Override
public String generateKey(OAuth2ProtectedResourceDetails resource) {
return generator.generate();
}
}
We are looking at implementing a few services using the new Spring 5 "Reactive" API.
We currently use, somewhat dependent on MVC, Apache CXF and Apache Shiro for our REST services and security. All of this runs in Undertow now.
We can get one or the other to work but not both together. It appears when we switch over to the reactive application it knocks out the servlets, filters, etc. Conversely, when we use the MVC-style application it does not see the reactive handlers.
Is it possible to run the Spring 5 Reactive services alongside REST/servlet/filter components or customize the SpringBoot startup to run REST and Reactive services on different ports?
Update:
I "seem" to be able to get the reactive handlers working doing this but I don't know if this is the right approach.
#Bean
RouterFunction<ServerResponse> routeGoodbye(TrackingHandler endpoint)
{
RouterFunction<ServerResponse> route = RouterFunctions
.route(GET("/api/rx/goodbye")
.and(accept(MediaType.TEXT_PLAIN)), endpoint::trackRedirect2);
return route;
}
#Bean
RouterFunction<ServerResponse> routeHello(TrackingHandler endpoint)
{
RouterFunction<ServerResponse> route = RouterFunctions
.route(GET("/api/rx/hello")
.and(accept(MediaType.TEXT_PLAIN)), endpoint::trackRedirect);
return route;
}
#Bean
ContextPathCompositeHandler servletReactiveRouteHandler(TrackingHandler handler)
{
final Map<String, HttpHandler> handlers = new HashMap<>();
handlers.put("/hello", toHttpHandler((this.routeHello(handler))));
handlers.put("/goodbye", toHttpHandler(this.routeGoodbye(handler)));
return new ContextPathCompositeHandler(handlers);
}
#Bean
public ServletRegistrationBean servletRegistrationBean(final ContextPathCompositeHandler handlers)
{
ServletRegistrationBean registrationBean = new ServletRegistrationBean<>(
new ReactiveServlet(handlers),
"/api/rx/*");
registrationBean.setLoadOnStartup(1);
registrationBean.setAsyncSupported(true);
return registrationBean;
}
#Bean
TrackingHandler trackingEndpoint(final TrackingService trackingService)
{
return new TrackingHandler(trackingService,
null,
false);
}
public class ReactiveServlet extends ServletHttpHandlerAdapter
{
ReactiveServlet(final HttpHandler httpHandler)
{
super(httpHandler);
}
}
Ok, after playing around with this for too long I finally seemed to be able to cobble together a solution that works for me. Hopefully this is the right way to do what I need to do.
Now, executing normal CXF RESTful routes shows me Undertow using a blocking task and executing my Reactive routes shows me undertow using NIO directly. When I tried using the ServletHttpHandler it looked like it was just invoking the service as a Servlet 3 async call.
The handlers are running completely separate from each other and allows me to run my REST services beside my reactive services.
1) Create an annotation that will be used to map the RouterFunction to an Undertow Handler
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Target({ElementType.METHOD, ElementType.TYPE})
public #interface ReactiveHandler
{
String value();
}
2) Create an UndertowReactiveHandler "Provider" so that I can lazily get the injected RouterFunction and return the UndertowHttpHandler when I configure Undertow.
final class UndertowReactiveHandlerProvider implements Provider<UndertowHttpHandlerAdapter>
{
#Inject
private ApplicationContext context;
private String path;
private String beanName;
#Override
public UndertowHttpHandlerAdapter get()
{
final RouterFunction router = context.getBean(beanName, RouterFunction.class);
return new UndertowHttpHandlerAdapter(toHttpHandler(router));
}
public String getPath()
{
return path;
}
public void setPath(final String path)
{
this.path = path;
}
public void setBeanName(final String beanName)
{
this.beanName = beanName;
}
}
3) Create the NonBLockingHandlerFactory (implements BeanFactoryPostProcessor). This looks for any of my #Bean methods that have been annotated with "ReactiveHandler" and then dynamically creates a "UndertowReactiveHandlerProvider" bean for each annotated router function which is used later to provide the handlers to Undertow.
#Override
public void postProcessBeanFactory(final ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException
{
final BeanDefinitionRegistry registry = (BeanDefinitionRegistry)configurableListableBeanFactory;
final String[] beanDefinitions = registry.getBeanDefinitionNames();
for (String name : beanDefinitions)
{
final BeanDefinition beanDefinition = registry.getBeanDefinition(name);
if (beanDefinition instanceof AnnotatedBeanDefinition
&& beanDefinition.getSource() instanceof MethodMetadata)
{
final MethodMetadata beanMethod = (MethodMetadata)beanDefinition.getSource();
final String annotationType = ReactiveHandler.class.getName();
if (beanMethod.isAnnotated(annotationType))
{
//Get the current bean details
final String beanName = beanMethod.getMethodName();
final Map<String, Object> attributes = beanMethod.getAnnotationAttributes(annotationType);
//Create the new bean definition
final GenericBeanDefinition rxHandler = new GenericBeanDefinition();
rxHandler.setBeanClass(UndertowReactiveHandlerProvider.class);
//Set the new bean properties
MutablePropertyValues mpv = new MutablePropertyValues();
mpv.add("beanName", beanName);
mpv.add("path", attributes.get("value"));
rxHandler.setPropertyValues(mpv);
//Register the new bean (Undertow handler) with a matching route suffix
registry.registerBeanDefinition(beanName + "RxHandler", rxHandler);
}
}
}
}
4) Create the Undertow ServletExtension. This looks for any UndertowReactiveHandlerProviders and adds it as an UndertowHttpHandler.
public class NonBlockingHandlerExtension implements ServletExtension
{
#Override
public void handleDeployment(DeploymentInfo deploymentInfo, final ServletContext servletContext)
{
deploymentInfo.addInitialHandlerChainWrapper(handler -> {
final WebApplicationContext ctx = getWebApplicationContext(servletContext);
//Get all of the reactive handler providers
final Map<String, UndertowReactiveHandlerProvider> providers =
ctx.getBeansOfType(UndertowReactiveHandlerProvider.class);
//Create the root handler
final PathHandler rootHandler = new PathHandler();
rootHandler.addPrefixPath("/", handler);
//Iterate the providers and add to the root handler
for (Map.Entry<String, UndertowReactiveHandlerProvider> p : providers.entrySet())
{
final UndertowReactiveHandlerProvider provider = p.getValue();
//Append the HttpHandler to the root
rootHandler.addPrefixPath(
provider.getPath(),
provider.get());
}
//Return the root handler
return rootHandler;
});
}
}
5) Under META-INF/services create a "io.undertow.servlet.ServletExtension" file.
com.mypackage.NonBlockingHandlerExtension
6) Create a SpringBoot AutoConfiguration that loads the post processor if Undertow is on the classpath.
#Configuration
#ConditionalOnClass(Undertow.class)
public class UndertowAutoConfiguration
{
#Bean
BeanFactoryPostProcessor nonBlockingHandlerFactoryPostProcessor()
{
return new NonBlockingHandlerFactoryPostProcessor();
}
}
7) Annotate any RouterFunctions that I want to map to an UndertowHandler.
#Bean
#ReactiveHandler("/api/rx/service")
RouterFunction<ServerResponse> routeTracking(TrackingHandler handler)
{
RouterFunction<ServerResponse> route = RouterFunctions
.nest(path("/api/rx/service"), route(
GET("/{cid}.gif"), handler::trackGif).andRoute(
GET("/{cid}"), handler::trackAll));
return route;
}
With this I can call my REST services (and Shiro works with them), use Swagger2 with my REST services, and call my Reactive services (and they do not use Shiro) in the same SpringBoot application.
In my logs, the REST call shows Undertow using the blocking (task-#) handler. The Reactive call shows Undertow using the non-blocking (I/O-# and nioEventLoopGroup) handler
I have configure Solr 6.2.1 as SolrCloud. Later I have Configured Basic Authentication.
I am going to configure Spring data solr 2.0.4.RELEASE with Solrj 6.2 and this is my code:
#Configuration
#EnableSolrRepositories(basePackages = { "ir.saeed.server.solr" }, multicoreSupport = true)
public class SearchContext {
#Value("${solr.host}")
private String host;
#Value("${solr.port}")
private Integer port;
#Value("${solr.username}")
private String username;
#Value("${solr.password}")
private String password;
#Value("${zkHost}")
private String zkHost;
#Value("${solr.coreName}")
private String collectionName;
#Bean
public SolrTemplate solrTemplate() {
return new SolrTemplate(solrClientFactory());
}
#Bean
public BasicCredentialsProvider credentialsProvider() {
BasicCredentialsProvider provider = new BasicCredentialsProvider();
provider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
return provider;
}
#Bean
public SolrClientFactory solrClientFactory() {
return new HttpSolrClientFactory(solrClient(), "", credentialsProvider().getCredentials(AuthScope.ANY), "BASIC");
}
#Bean
public SolrClient solrClient() {
return new CloudSolrClient.Builder().withZkHost(zkHost).build();
}
}
But when i run my web application this Exception occures:
10:51:48,110 org.springframework.data.solr.UncategorizedSolrException: nested exception is java.lang.NullPointerException
10:51:48,111 at org.springframework.data.solr.core.SolrTemplate.execute(SolrTemplate.java:172)
10:51:48,111 at org.springframework.data.solr.core.SolrTemplate.executeSolrQuery(SolrTemplate.java:509)
10:51:48,111 at org.springframework.data.solr.core.SolrTemplate.query(SolrTemplate.java:504)
10:51:48,111 at org.springframework.data.solr.core.SolrTemplate.doQueryForPage(SolrTemplate.java:338)
10:51:48,111 at org.springframework.data.solr.core.SolrTemplate.queryForPage(SolrTemplate.java:350)
How can I resolve the issue?
I think my Configuration is incorrect
I found a workaround for those who has same issue
Extends your own HttpSolrClientFactory. the problem caused by LBHttpSolrClient httpClient not being setup properly. correct setup should be like the following block if (solrClient instanceof LBHttpSolrClient) {...}
AuthHttpSolrClientFactory.java
#SuppressWarnings("deprecation")
public class AuthHttpSolrClientFactory extends HttpSolrClientFactory {
public AuthHttpSolrClientFactory(SolrClient solrClient, String core, Credentials credentials, String authPolicy) {
super(solrClient, core, credentials, authPolicy);
Assert.notNull(solrClient, "solrClient must not be null");
if (authPolicy != null) {
Assert.hasText(authPolicy);
}
appendBasicAuthentication(credentials, authPolicy, this.getSolrClient());
}
private void appendBasicAuthentication(Credentials credentials, String authPolicy, SolrClient solrClient) {
if( credentials != null) {
if (solrClient instanceof HttpSolrClient) {
HttpSolrClient httpSolrClient = (HttpSolrClient) solrClient;
if (assertHttpClientInstance(httpSolrClient.getHttpClient())) {
AbstractHttpClient httpClient = (AbstractHttpClient) httpSolrClient.getHttpClient();
httpClient.getCredentialsProvider().setCredentials(new AuthScope(AuthScope.ANY), credentials);
httpClient.getParams().setParameter(AuthPNames.TARGET_AUTH_PREF, Arrays.asList(authPolicy));
}
}
else if (solrClient instanceof LBHttpSolrClient) {
LBHttpSolrClient lbhttpSolrClient = (LBHttpSolrClient) solrClient;
if (assertHttpClientInstance(lbhttpSolrClient.getHttpClient())) {
AbstractHttpClient httpClient = (AbstractHttpClient) lbhttpSolrClient.getHttpClient();
httpClient.getCredentialsProvider().setCredentials(new AuthScope(AuthScope.ANY), credentials);
httpClient.getParams().setParameter(AuthPNames.TARGET_AUTH_PREF, Arrays.asList(authPolicy));
}
}
}
}
private boolean assertHttpClientInstance(HttpClient httpClient) {
Assert.isInstanceOf(AbstractHttpClient.class, httpClient,
"HttpClient has to be derivate of AbstractHttpClient in order to allow authentication.");
return true;
}
}
beans-solr.xml
<solr:solr-client id="solrClient" url="${solr.host}" />
<bean id="credentials" class="org.apache.http.auth.UsernamePasswordCredentials">
<constructor-arg type="java.lang.String" value="${solr.credentials}"/>
</bean>
<bean id="solrClientFactory" class="com.example.solr.AuthHttpSolrClientFactory" scope="singleton">
<constructor-arg ref="solrClient" />
<constructor-arg name="core">
<null />
</constructor-arg>
<constructor-arg ref="credentials" />
<constructor-arg type="java.lang.String" value="BASIC"/>
</bean>
<bean id="solrTemplate" class="org.springframework.data.solr.core.SolrTemplate" scope="singleton">
<constructor-arg ref="solrClientFactory" />
<constructor-arg name="requestMethod">
<value type="org.springframework.data.solr.core.RequestMethod">POST</value>
</constructor-arg>
</bean>
Yes, your configuration seems incorrect. I had same issue like yours
I would like to use apache solr version 6.6.0 and spring data solr version 2.0.8 (bought by spring boot starter). It turned out that the version from spring data solr doesn't have support for apache solr version > 5, because when you traced
at org.springframework.data.solr.core.SolrTemplate.execute(SolrTemplate.java:172), it's very clear that when the solrTemplate is going to createClientForCore, it will clone from the cloudSolrClient that we have been configured
the problem is in String zkHost = (String)readField(solrClient, "zkHost");
* it will return null since in apache solr version > 5, the zkHost is stored in "clusterStateProvider" not in the same level as "cloudSolrClient"
Solved:
If you want keep using spring data solr version 2, you need to downgrade the apache solr version
There are several reasons why your code is not working, but they are lied mainly to spring-data-solr missing features.
First of all spring-data-solr version 2.0.4 does not have support for Solr 5 (cloud) features.
So this is the reason why you are getting the NullPointerException in the method org.springframework.data.solr.server.support.SolrClientUtils#cloneLBHttpSolrClient
I've tried to see whether it the scenario exposed by you works with the latest SNAPSHOT (2.1.0-SNAPSHOT) of spring-data-solr and after a few modifications on the SolrContext spring configuration class :
#Configuration
#EnableSolrRepositories(basePackages = {"com.acme.solr"})
// notice that the multicoresupport is left false
// see org.springframework.data.solr.repository.config.SolrRepositoryConfigExtension#postProcess(org.springframework.beans.factory.support.BeanDefinitionBuilder, org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource) for details
public class SolrContext {
#Bean
public Credentials credentials(#Value("${solr.username}") String username, #Value("${solr.password}") String
password) {
return new UsernamePasswordCredentials(username, password);
}
#Bean
public BasicCredentialsProvider credentialsProvider(Credentials credentials) {
BasicCredentialsProvider provider = new BasicCredentialsProvider();
provider.setCredentials(AuthScope.ANY, credentials);
return provider;
}
#Bean
public SolrClientFactory solrClientFactory(SolrClient solrClient, Credentials credentials) {
return new HttpSolrClientFactory(solrClient, "", credentials, "BASIC");
}
// create a solrtemplate bean, so that it is used in
// org.springframework.data.solr.repository.support.SolrRepositoryFactoryBean#doCreateRepositoryFactory method
// for using org.springframework.data.solr.repository.support.SolrRepositoryFactory#SolrRepositoryFactory(org.springframework.data.solr.core.SolrOperations) constructor
#Bean
public SolrTemplate solrTemplate(SolrClientFactory solrClientFactory){
return new SolrTemplate(solrClientFactory);
}
#Bean
public CloudSolrClient solrClient(#Value("${zkHost}") String zkHost) {
CloudSolrClient solrClient = new CloudSolrClient.Builder().withZkHost(zkHost).build();
solrClient.setDefaultCollection("gettingstarted");
return solrClient;
}
}
I still received a 401 authentication issue when performing a solr request (when the basic authentication was enabled on solr).
In a vanilla solrj application, this is how you'd make an authenticated request:
CloudSolrClient solr = new CloudSolrClient.Builder()
.withZkHost("localhost:9983")
.build();
SolrQuery query = new SolrQuery();
query.setQuery("*:*");
SolrRequest<QueryResponse> req = new QueryRequest(query);
req.setBasicAuthCredentials("solr", "SolrRocks");
QueryResponse rsp = req.process(solr, "gettingstarted");
System.out.println("numFound: " + rsp.getResults().getNumFound());
When looking whether the method SolrRequest#setBasicAuthCredentials(String, String) is used in the code of spring-data-solr I didn't notice anywhere this method being used. So, it is very likely, that this feature is not implemented yet not even on the SNAPSHOT build of the spring-data-solr.
I've created a feature request on spring-data-solr project to add support for this functionality.
Had the same issue usind Solr 7.7 and spring-boot-starter-data-solr 2.1.14 (uses spring-data-solr-4.0.17.RELEASE)
Tried a few ways, including creating custom HttpSolrClientFactory. It works but actually makes 2 calls to Solr and the first one returns
401 Unauthorized.
I fix the problem extending the CloudSolrClient (trying to do correct auth as it described in Basic Auth with SolrJ)
It makes only one call to Solr and uses Basic auth
public class BasicAuthCloudSolrClient extends CloudSolrClient {
private final Credentials credentials;
/**
* Create a new client object that connects to Zookeeper using BASIC Authentication and is always aware
* of the SolrCloud state. If there is a fully redundant Zookeeper quorum and
* SolrCloud has enough replicas for every shard in a collection, there is no
* single point of failure. Updates will be sent to shard leaders by default.
*
* #param builder a {#link BasicAuthCloudSolrClient.Builder} with the options used to create the client.
*/
protected BasicAuthCloudSolrClient(Builder builder) {
super(builder);
this.credentials = builder.credentials;
}
#Override
public QueryResponse query(String collection, SolrParams params, SolrRequest.METHOD method)
throws
SolrServerException, IOException {
QueryRequest request = new QueryRequest(params, method);
request.setBasicAuthCredentials(credentials.getUserPrincipal().getName(),
credentials.getPassword());
return request.process(this, collection);
}
/**
* Constructs {#link BasicAuthCloudSolrClient} instances from provided configuration.
*/
public static class Builder extends CloudSolrClient.Builder {
protected Credentials credentials;
/**
* #deprecated use other constructors instead. This constructor will be changing visibility in an upcoming release.
*/
#Deprecated
public Builder() {
}
/**
* Provide a series of ZK hosts which will be used when configuring {#link CloudSolrClient} instances.
*
* #param zkHosts a List of at least one ZooKeeper host and port (e.g. "zookeeper1:2181")
* #param credentials a credentials to connect to Solr.
*/
public Builder(List<String> zkHosts, Credentials credentials) {
super(zkHosts, empty());
this.credentials = credentials;
}
/**
* Create a {#link CloudSolrClient} based on the provided configuration.
*/
public BasicAuthCloudSolrClient build() {
if (Objects.isNull(credentials)) {
throw new IllegalArgumentException(
"Credentials must be provided to initialize BasicAuthCloudSolrClient");
}
if (stateProvider == null) {
if (!zkHosts.isEmpty()) {
stateProvider = new ZkClientClusterStateProvider(zkHosts, zkChroot);
} else if (!this.solrUrls.isEmpty()) {
try {
stateProvider = new HttpClusterStateProvider(solrUrls, httpClient);
} catch (Exception e) {
throw new RuntimeException(
"Couldn't initialize a HttpClusterStateProvider (is/are the "
+ "Solr server(s), " + solrUrls + ", down?)", e);
}
} else {
throw new IllegalArgumentException("Both zkHosts and solrUrl cannot be null.");
}
}
return new BasicAuthCloudSolrClient(this);
}
#Override
public BasicAuthCloudSolrClient.Builder getThis() {
return this;
}
}
Configuration looks like this:
#Bean
public Credentials solrCredentials(#Value("${solr.username}") String username, #Value("${solr.password}") String
password) {
return new UsernamePasswordCredentials(username, password);
}
#Bean
public SolrClientFactory solrClientFactory(SolrClient solrClient,
Credentials solrCredentials) {
return new HttpSolrClientFactory(solrClient, solrCredentials, AuthSchemes.BASIC);
}
#Bean
public SolrTemplate solrTemplate(SolrClientFactory solrClientFactory){
return new SolrTemplate(solrClientFactory);
}
#Bean
public SolrClient solrClient(Credentials solrCredentials) {
if (isNotEmpty(properties.getZkHosts())) {
BasicAuthCloudSolrClient solrClient =
new BasicAuthCloudSolrClient.Builder(properties.getZkHosts(), solrCredentials).build();
solrClient.setDefaultCollection(properties.getCollection());
return solrClient;
} else {
throw new IllegalStateException("ZkHosts is required for application startup.");
}
}
I'm playing around with Spark (the Java web framework, not Apache Spark).
I find it really nice and easy to define routes and filters, however I'm looking to apply a native servlet filter to my routes and can't seem to find a way to do that.
More specifically, I would like to use Jetty's DoSFilter which is a servlet filter (contrast with the Spark Filter definition). Since Spark is using embedded Jetty, I don't have a web.xml to register the DoSFilter. However, Spark doesn't expose the server instance so I can't find an elegant way of registering the filter programatically either.
Is there a way to apply a native servlet filter to my routes?
I thought of wrapping the DoSFilter in my own Spark Filter, but it seemed like a weird idea.
You can do it like this:
public class App {
private static Logger LOG = LoggerFactory.getLogger(App.class);
public static void main(String[] args) throws Exception {
ServletContextHandler mainHandler = new ServletContextHandler();
mainHandler.setContextPath("/base/path");
Stream.of(
new FilterHolder(new MyServletFilter()),
new FilterHolder(new SparkFilter()) {{
this.setInitParameter("applicationClass", SparkApp.class.getName());
}}
).forEach(h -> mainHandler.addFilter(h, "*", null));
GzipHandler compression = new GzipHandler();
compression.setIncludedMethods("GET");
compression.setMinGzipSize(512);
compression.setHandler(mainHandler);
Server server = new Server(new ExecutorThreadPool(new ThreadPoolExecutor(10,200,60000,TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(200),
new CustomizableThreadFactory("jetty-pool-"))));
final ServerConnector serverConnector = new ServerConnector(server);
serverConnector.setPort(9290);
server.setConnectors(new Connector[] { serverConnector });
server.setHandler(compression);
server.start();
hookToShutdownEvents(server);
server.join();
}
private static void hookToShutdownEvents(final Server server) {
LOG.debug("Hooking to JVM shutdown events");
server.addLifeCycleListener(new AbstractLifeCycle.AbstractLifeCycleListener() {
#Override
public void lifeCycleStopped(LifeCycle event) {
LOG.info("Jetty Server has been stopped");
super.lifeCycleStopped(event);
}
});
Runtime.getRuntime().addShutdownHook(new Thread() {
#Override
public void run() {
LOG.info("About to stop Jetty Server due to JVM shutdown");
try {
server.stop();
} catch (Exception e) {
LOG.error("Could not stop Jetty Server properly", e);
}
}
});
}
/**
* #implNote {#link SparkFilter} needs to access a public class
*/
#SuppressWarnings("WeakerAccess")
public static class SparkApp implements SparkApplication {
#Override
public void init() {
System.setProperty("spring.profiles.active", ApplicationProfile.readProfilesOrDefault("dev").stream().collect(Collectors.joining()));
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ModocContext.class);
ctx.registerShutdownHook();
}
}}
I'm face a problem since few days and I can't get solution. below is my app structure:
I have ejbapp.jar inside MyearDeployedOnJboss7.ear at the same level of equinox-server-side-app.war (built using warproduct) and I want to load class from MyJarToLaoadForEjbapp.jar which is in iModernizeWebClient_1.0.0.jar which is in plugins folder of equinox-server-side-app.war (I want show image of app structure but I cannot send image because forum rules need 10 score to be able to do that)
My question is how to allow ejbapp.jar load classes from "MyJarToLaoadForEjbapp.jar" inside MyWebClient_1.0.0.jar's plugin folder which is in the equinox-server-side-app.war.
I think using servletbridge classloader but no idea how to use it.
in my launch.ini I've:
osgi.*=#null org.osgi.*=#null eclipse.*=#null osgi.parentClassloader=app osgi.contextClassLoaderParent=app
I resolved my proble using Servlet HttpServiceTracker from the OSGI spec. how to do it : write HttpServiceTracker liket that :
public class HttpServiceTracker extends ServiceTracker {
private static final Logger logger = Logger
.getLogger(HttpServiceTracker.class.getName());
public HttpServiceTracker(BundleContext context) {
super(context, HttpService.class.getName(), null);
}
public Object addingService(ServiceReference reference) {
HttpService httpService = (HttpService) context.getService(reference);
logger.info("default context path : "
+ org.eclipse.rap.ui.internal.servlet.HttpServiceTracker.ID_HTTP_CONTEXT);
try {
logger.info("will register servlet ");
httpService.registerServlet("/programLauncherServlet",
new ProgramLauncherServlet(), null, null);
logger.info("servlet has been registred with http context ");
// httpService.registerResources( "/", "/html", null );
} catch (Exception e) {
//e.printStackTrace();
logger.info("The alias '/programLauncherServlet' is already in use");
}
return httpService;
}
public void removedService(ServiceReference reference, Object service) {
logger.info("will unregister servlet ");
HttpService httpService = (HttpService) service;
httpService.unregister("/programLauncher");
super.removedService(reference, service);
logger.info("servlet has been unregistred");
}
in your plugin activator class at method start :
#Override
public void start(BundleContext context) throws Exception {
super.start(context);
Activator.plugin = this;
BundleContext osgiContext = BundleReference.class
.cast(AnyClassOfYourProject.class.getClassLoader()).getBundle()
.getBundleContext();
serviceTracker = new HttpServiceTracker(osgiContext);
serviceTracker.open();
LOGGER.info("servlet published !!");
LOGGER.info("Bundle started.");
}
and for unregister the servlet at the stop method :
public void stop(BundleContext context) throws Exception {
Activator.plugin = null;
serviceTracker.close();
serviceTracker = null;
LOGGER.info("servlet unregistered from context !!");
super.stop(context);
}
that's all. your servlet is accessible outside your eclipse bundle and you can call methods inside the bundle.