Jersey on embedded Jetty with hk2 locator not loading ResourceConfig - jersey

I am using Jetty as an embedded server for Jersey.
ServletHolder jerseyServletHolder = new ServletHolder(ServletContainer.class);
jerseyServletHolder.setInitOrder(1);
jerseyServletHolder.setInitParameter(
org.glassfish.jersey.server.ServerProperties.PROVIDER_PACKAGES,
"com.my.package");
webAppContext.addServlet(jerseyServletHolder, "/rest/*");
I have a ResourceConfig implementation:
#ApplicationPath("/rest")
public class MyResourceConfig extends ResourceConfig {
static{
System.out.println("ResourceConfig loaded");
// this never gets calls
}
#Inject
public MyResourceConfig(ServiceLocator serviceLocator, Properties serverProps) {
packages("com.my.package");
}
}
The problem is that when I launch, the MyResourceConfig class is never loaded.
If I add:
jerseyServletHolder.setInitParameter(
ServletProperties.JAXRS_APPLICATION_CLASS,
MyResourceConfig.class.getName());
then the ResourceConfig does get loaded.
Why isn't MyResoureConfig getting picked up based on the #ApplicationPath annotation?

You just need annotation / bytecode scanning enabled.
Start by putting jetty-annotations-<version>.jar (and transitive dependencies) into your project.
Then, in your code, after you create your org.eclipse.jetty.server.Server object, do this.
Server server = new Server(8080);
// Enable parsing of jndi-related parts of web.xml and jetty-env.xml
Configuration.ClassList classlist = Configuration.ClassList
.setServerDefault(server);
classlist.addAfter("org.eclipse.jetty.webapp.FragmentConfiguration",
"org.eclipse.jetty.plus.webapp.EnvConfiguration",
"org.eclipse.jetty.plus.webapp.PlusConfiguration");
// Enable annotation/bytecode scanning and ServletContainerInitializer usages
classlist.addBefore(
"org.eclipse.jetty.webapp.JettyWebXmlConfiguration",
"org.eclipse.jetty.annotations.AnnotationConfiguration");
WebAppContext webAppContext = createWebAppContext();
// ....
server.start();
That will enable the configurations needed to perform bytecode scanning and annotation scanning, along with enabling the ability to load any javax.servlet.ServletContainerInitializer found within your webapp, including the critical one from Jersey (org.glassfish.jersey.servlet.init.JerseyServletContainerInitializer)

Related

Problem with connection to Neo4j test container using Spring boot 2 and JUnit5

Problem with connection to Neo4j test container using Spring boot 2 and JUnit5
int test context. Container started successfully but spring.data.neo4j.uri property has a wrong default port:7687, I guess this URI must be the same when I call neo4jContainer.getBoltUrl().
Everything works fine in this case:
#Testcontainers
public class ExampleTest {
#Container
private static Neo4jContainer neo4jContainer = new Neo4jContainer()
.withAdminPassword(null); // Disable password
#Test
void testSomethingUsingBolt() {
// Retrieve the Bolt URL from the container
String boltUrl = neo4jContainer.getBoltUrl();
try (
Driver driver = GraphDatabase.driver(boltUrl, AuthTokens.none());
Session session = driver.session()
) {
long one = session.run("RETURN 1",
Collections.emptyMap()).next().get(0).asLong();
assertThat(one, is(1L));
} catch (Exception e) {
fail(e.getMessage());
}
}
}
But SessionFactory is not created for the application using autoconfiguration following to these recommendations - https://www.testcontainers.org/modules/databases/neo4j/
When I try to create own primary bean - SessionFactory in test context I get the message like this - "URI cannot be returned before the container is not loaded"
But Application runs and works perfect using autoconfiguration and neo4j started in a container, the same cannot be told about the test context
You cannot rely 100% on Spring Boot's auto configuration (for production) in this case because it will read the application.properties or use the default values for the connection.
To achieve what you want to, the key part is to create a custom (Neo4j-OGM) Configuration bean. The #DataNeo4jTest annotation is provided by the spring-boot-test-autoconfigure module.
#Testcontainers
#DataNeo4jTest
public class TestClass {
#TestConfiguration
static class Config {
#Bean
public org.neo4j.ogm.config.Configuration configuration() {
return new Configuration.Builder()
.uri(databaseServer.getBoltUrl())
.credentials("neo4j", databaseServer.getAdminPassword())
.build();
}
}
// your tests
}
For a broader explanation have a look at this blog post. Esp. the section Using with Neo4j-OGM and SDN.

Unable to connect to Locator via GFSH

I have started a GemFire Server and Locator via Spring Boot and when I try to connect to the Locator from GFSH, I getting the following issue:
gfsh> connect
Connecting to Locator at [host=localhost, port=10334] ..
Connection refused: connect
Below, is the Spring (Java) configuration:
#Configuration
#ComponentScan
#EnableGemfireRepositories(basePackages= "com.gemfire.demo")
#CacheServerApplication(locators = "localhost[10334]")
#EnableManager
public class GemfireConfiguration {
#Bean
Properties gemfireProperties() {
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty("name", "SpringDataGemFireApplication");
gemfireProperties.setProperty("mcast-port", "0");
gemfireProperties.setProperty("log-level", "info");
return gemfireProperties;
}
#Bean
#Autowired
CacheFactoryBean gemfireCache() {
CacheFactoryBean gemfireCache = new CacheFactoryBean();
gemfireCache.setClose(true);
gemfireCache.setProperties(gemfireProperties());
return gemfireCache;
}
#Bean(name="employee")
#Autowired
LocalRegionFactoryBean<String, Employee> getEmployee(final GemFireCache cache) {
LocalRegionFactoryBean<String, Employee> employeeRegion = new LocalRegionFactoryBean<String, Employee>();
employeeRegion.setCache(cache);
employeeRegion.setClose(false);
employeeRegion.setName("employee");
employeeRegion.setPersistent(false);
employeeRegion.setDataPolicy(DataPolicy.PRELOADED);
return employeeRegion;
}
}
Ref:Spring Data Gemfire locator
as per John's advice, I have enabled the Manager though I am still unable to connect.
You are not able to connect to the Locator (using Gfsh) because you don't have a Locator (service, either standalone or embedded) running with just the Spring (Java) config shown above.
Note that the #CacheServerApplication(locators = "localhost[10334]") annotation, specifically with the locators attribute as you have configured above, does NOT start an embedded Locator. It simply allows this Spring Boot configured and bootstrapped Apache Geode or Pivotal GemFire peer Cache node to join an existing distributed system (cluster) with an "existing" Locator running on localhost, listening on port 10334.
For instance, you could have started a Locator using Gfsh (e.g. start locator --name=X ...), then started your Spring Boot application with the Spring (Java) config shown above and you would see the Spring Boot app as part of the cluster formed by the Gfsh started Locator.
It is simply a shortcut (convenience) to configure and start an "embedded" Locator, but to do so, you need to use the #EnableLocator annotation.
Therefore, to configure and start an (embedded) Locator service in the same Spring Boot application as the CacheServer (and Manager), you must also include the #EnableLocator annotation, like so:
#SpringBootApplicaton
#CacheServerApplication
#EnableLocator
#EnableManager(start = true)
public class GemFireServerApplication {
...
}
I have plenty of examples of this here, for instance here, and talk about this generally here, etc.
As a side note, your whole configuration (class) is confused and it is clear you don't quite understand what you are doing. For instance, declaring the gemfireProperties and gemfireCache beans in JavaConfig is redundant and unnecessary since you are using the #CacheServerApplication annotation. Your whole configuration could be simplified to:
#CacheServerApplication(
name = "SpringDataGemFireApplication",
locators = "localhost[10334]",
logLevel = "info"
)
#EnableLocator
#EnableManager(start = true)
#EnableGemfireRepositories(basePackages= "com.gemfire.demo")
#ComponentScan
public class GemfireConfiguration {
#Bean(name="employee")
LocalRegionFactoryBean<String, Employee> getEmployee(GemFireCache cache) {
LocalRegionFactoryBean<String, Employee> employeeRegion =
new LocalRegionFactoryBean<String, Employee>();
employeeRegion.setCache(cache);
employeeRegion.setClose(false);
employeeRegion.setName("employee");
employeeRegion.setPersistent(false);
employeeRegion.setDataPolicy(DataPolicy.PRELOADED);
return employeeRegion;
}
}
Two things:
1) First, I would be highly careful about using classpath component scanning (#ComponentScan). I am not a fan of this configuration approach, especially in production where things should be explicit.
2) I would encourage your to considering using the type-safe basePackageClasses attribute on the #EnableGemFireRepositorities annotation rather than the basePackages attribute. With basePackageClasses, you only need to refer to a single interface/class in the desired package (e.g. com.gemfire.demo) rather than every interface/class. The referenced interface/class serves as a pointer to identify the package to scan from, including all sub-packages. It is type-safe and when your interfaces/classes in that package are re-located, then your attribute is still valid after the refactoring.
Anyway...
Hope this helps.
-j

Using a custom classloader with Spring's ComponentScan and PropertyPlaceholderConfigurer

I want to create multiple application contexts in my Tomcat application.
Some of these application contexts have the same package and class names, but they all refer to different jars.
For example:
application0 use service.jar, model.jar
application1 use service-a.jar, model-a.jar
application2 use service-b.jar, model-b.jar
application0 context is OK because is in orign project.
I reference some web page to custom application1, I use my custom classloader to start applicationContext.
File file0 = new File("D://git/project1/service-a.jar");
File file1 = new File("D://git/project1/modele-a.jar");
// convert the file to URL format
URL url0 = file0.toURI().toURL();
URL url1 = file1.toURI().toURL();
List<URL> urls = new LinkedList<>();
List<File> libs = listFilesForFolder(new File("D://protal//apache-tomcat-8.0.39//lib"));
for(File lib : libs) {
urls.add(lib.toURI().toURL());
}
urls.add(url1);
urls.add(url0);
final URLClassLoader customClassLoader = new URLClassLoader(urls.toArray(new URL[urls.size()]));
ClassPathXmlApplicationContext context1 = new ClassPathXmlApplicationContext("applicationContext.xml") {
protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader)
{
super.initBeanDefinitionReader(reader);
reader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_NONE);
reader.setBeanClassLoader(customClassLoader);
}
};
allApplicationContexts.add(context1);
The Spring contexts start OK, but they fail to create the component-scan bean, and PropertyPlaceholderConfigurer isn't working. Everything else seems correct.
I sure my config is correct because it works without the custom classloader. Libs contains all spring lib.
Is it possible to get this working with multiple Spring contexts?

Behavior differ between running application in IntelliJ and from jar

I have written an application using Jetty as a server. I wish to package my application into a .jar, so I may put it in a Docker container later on.
When I run my application through IntelliJ, everything seems to be working as it should, and I can reach my index.html from localhost:2222. However, when I package my application (using mvn clean compile assembly:single) and run my jar by typing java -jar myapplication.jar in the shell, my index.html on localhost:2222 returns HTTP Error 404.
My App.java:
public class App {
public static void main( String[] args ) throws Exception {
ResourceConfig config = new ResourceConfig();
config.packages("mypackage");
ServletHolder servlet = new ServletHolder(new ServletContainer(config));
Server server = new Server(2222);
ResourceHandler handler = new ResourceHandler();
handler.setBaseResource(org.eclipse.jetty.util.resource.Resource
.newClassPathResource("index.html"));
ServletContextHandler context = new ServletContextHandler(server, "/*");
context.addServlet(servlet, "/*");
HandlerList handlers = new HandlerList();
handlers.addHandler(handler);
handlers.addHandler(context);
server.setHandler(handlers);
try {
server.start();
server.join();
} finally {
server.destroy();
}
}
}
When running the application in IntelliJ and as jar, I get the following output (may be of interest?):
2015-11-06 09:25:00.991:INFO::main: Logging initialized #279ms
2015-11-06 09:25:01.238:WARN:oejsh.ContextHandler:main: o.e.j.s.ServletContextHandler#490d6c15{/,null,null} contextPath ends with /*
2015-11-06 09:25:01.238:WARN:oejsh.ContextHandler:main: Empty contextPath
2015-11-06 09:25:01.250:INFO:oejs.Server:main: jetty-9.2.3.v20140905
Nov 06, 2015 9:25:01 AM org.glassfish.jersey.server.ApplicationHandler initialize
INFO: Initiating Jersey application, version Jersey: 2.7 2014-03-12 18:11:31...
2015-11-06 09:25:02.294:INFO:oejsh.ContextHandler:main: Started o.e.j.s.ServletContextHandler#490d6c15{/,null,AVAILABLE}
2015-11-06 09:25:02.306:INFO:oejs.ServerConnector:main: Started ServerConnector#268f106e{HTTP/1.1}{0.0.0.0:2222}
2015-11-06 09:25:02.306:INFO:oejs.Server:main: Started #1688ms
I am not posting my file of Jersey-methods, since I don't think it is relevant for my question.
So, what is the magic of packaging this up to a jar that can recognize my index.html at localhost:2222? Is it some environment settings in IntelliJ?
Cheers
First of all, don't mix ResourceHandler and ServletContextHandler, you are duplicating static content effort (3 times over with Jersey in play) and will get unreliable results.
Drop the ResourceHandler, set the required ServletContextHandler.setBaseResource(), and add a DefaultServlet to your context.
// Figure out what path to serve content from
ClassLoader cl = App.class.getClassLoader();
// We look for a file, as ClassLoader.getResource() is not
// designed to look for directories (we resolve the directory later)
URL f = cl.getResource("static-root/index.html");
if (f == null)
{
throw new RuntimeException("Unable to find resource directory");
}
// Resolve file to directory
URI webRootUri = f.toURI().resolve("./").normalize();
System.err.println("Main Base Resource is " + webRootUri);
// Setup the basic application "context" for this application at "/"
// This is also known as the handler tree (in jetty speak)
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
context.setBaseResource(Resource.newResource(webRootUri));
server.setHandler(context);
// Jersey setup
ResourceConfig config = new ResourceConfig();
config.packages("mypackage");
context.addServlet(new ServletHolder(new ServletContainer(config)), "/*");
// Lastly, the default servlet for root content (always needed, to satisfy servlet spec)
// It is important that this is last.
ServletHolder holderDef = new ServletHolder("default",DefaultServlet.class);
holderDef.setInitParameter("dirAllowed","true");
context.addServlet(holderDef,"/");
Note: since you setup Jersey at the url-pattern /* you should know that Jersey is responsible for serving all static content, not Jetty.

Embedded Jetty to test the spring application startup

What I am trying to do is, to have a test to startup the whole application to see if there's any error. But I want to use the applicationContext.xml from the /src/resource folder and not form test/resource. How can I do that in JUnit?
My application is big and a lot of people share the same codebase. So, I just wanted to have a quick test to see if the checkin can start up the application.
This is my simple code but it looks like it's missing some of the autowire stuff, that's why I want to use the xml files from /src/resource, so I don't have to maintain two locations.
My application is plain Spring MVC 3.0
#Test(enabled = false)
public void shouldStartupTheApp() throws Exception {
Server server = new Server();
SelectChannelConnector connector = new SelectChannelConnector();
connector.setPort(9999);
server.setConnectors(new Connector[] {connector});
Context context = new Context(server, "/", Context.SESSIONS);
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setContextConfigLocation("classpath:/test-applicationContext.xml");
ServletHolder servletHolder = new ServletHolder(dispatcherServlet);
context.addServlet(servletHolder, "/*");
server.start();
}
You can import your src/resources/filename.xml in other xml-file using
<import resource="classpath:/filename.xml" />

Resources