Getting 404 error in Jersey with no web.xml - java

I am following Jersey tutorial to develop simple Jersey web application.
By following Section - Example 2.9. Deployment of a JAX-RS application using #ApplicationPath with Servlet 3.0
I have created created below program:
#ApplicationPath("resources")
public class MyApplication extends PackagesResourceConfig {
public MyApplication() {
super("com.examples");
}
}
and I have below basic Resource class:
#Path("/helloworld")
public class HelloWorldResource {
#GET
#Produces("text/plain")
public String getClichedMessage() {
return "Hello World";
}
}
I am using Jersey-1.19 version, I am not having any web.xml file in my web application. Now I am deploying my application on Tomcat 7 server.
When I try to access the URL as : http://localhost:8080/myapp/resources/helloworld I am getting error as
HTTP Status 404 - /myapp/resources/helloworld
type Status report
message: /myapp/resources/helloworld
description: The requested resource is not available.

You need the jersey-servlet dependency
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-servlet</artifactId>
<version>1.19</version>
</dependency>
if you want to go with no web.xml. It has the JerseyServletContainerInitializer required to load the application.
And just for any future readers that come across this looking for a Jersey 2.x solution, you need the following dependency to work with no web.xml
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>${jersey2.version}</version>
</dependency>
for the same reason - it has the JerseyServletContainerInitializer

Related

How to display Webservice list (Apache CXF)

I am trying to display the Service list served by a simple jax-ws service using CXF.
Using these dependencies:
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>3.5.2</version>
</dependency>
And this class:
#WebService
public class Foo {
#WebMethod
public String helloWorld() {
return "hello";
}
}
I can publish the service with this line:
Endpoint.publish("http://0.0.0.0:1234/foo", new Foo());
Then I can display the wsdl browsing to the url: http://127.0.0.1:1234/foo?wsdl
But I don't find any way to obtain the service list html page. I tried the /services url ( from cxf By default, Apache CXF creates a /services page containing a listing of the available endpoints ), but nothing. Is there a way to enable it ?
Can it be done programmatically (not using xml file) ?

JSP in Spring Boot application yields 404 Error no matter what I do

I'll start off by saying I've looked at and tried the solutions in every question regarding this that I can find. The biggest problem is that most of these solutions are very old, and Spring Boot has changed a lot in the last several years. To be clear, I've tried this, this, this, this, and more. I've also read numerous tutorials. Nothing works.
I have a brand new Spring Boot application and I'm trying to get JSP rendering working with it. These are my dependencies:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>[2.3.4.RELEASE,3)</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>[2.3.4.RELEASE,3)</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>[2.3.4.RELEASE,3)</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>[2.8.0,3)</version>
</dependency>
<dependency>
<groupId>de.mkammerer</groupId>
<artifactId>argon2-jvm</artifactId>
<version>[2.7,3)</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>[8.0.21,9)</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>[2.3.2,)</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>[2.3.4.RELEASE,3)</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>[9.0.38,)</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>[2.3.4.RELEASE,3)</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
My project is laid out as follows:
- source
- production
- java
- [my source code packages]
- resources
- WEB-APP
- jsp
- initialization
- begin.jsp
- [my resource packages]
- test
- java
- resources
"WEB-APP/jsp" is just the latest iteration I've tried. I've tried "WEB-INF/jsp", "META-INF/jsp", "webapp/jsp", no parent (just "jsp"), etc., all with the same results.
I know the parent directories are a bit non-standard, but it's configured correctly in Maven and I've confirmed it's not the source of my problems:
<build>
<sourceDirectory>source/production/java</sourceDirectory>
<resources>
<resource>
<directory>source/production/resources</directory>
</resource>
</resources>
...
</build>
My Application class is as follows:
#SpringBootApplication(scanBasePackages="com.my.project")
#EnableWebMvc
#EnableJpaRepositories("com.my.project.repository")
#EntityScan("com.my.project.model")
public class Application
{
private static final Logger LOGGER = LogManager.getLogger(Application.class);
public Application()
{
}
#Bean
public ViewResolver viewResolver()
{
LOGGER.info("Constructing InternalResourceViewResolver[JstlView]");
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-APP/jsp/");
resolver.setSuffix(".jsp");
resolver.setViewClass(JstlView.class);
resolver.setRedirectContextRelative(true);
resolver.setRedirectHttp10Compatible(false);
return resolver;
}
public static void main(final String[] args)
{
SpringApplication.run(Application.class, args);
}
}
And my Controller:
#Controller
public class InitializationController
{
private static final Logger LOGGER = LogManager.getLogger(InitializationController.class);
#GetMapping("/initialize_application")
public String beginInitialization(ModelMap model)
{
LOGGER.info("Beginning initialization");
...
LOGGER.info("Returning view");
return "initialization/begin";
}
...
}
On startup I see the "Constructing InternalResourceViewResolver" log entry (my view resolver bean is created). When I go to /initialize_application, I get the following error:
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Sun Oct 18 21:45:26 CDT 2020
There was an unexpected error (type=Not Found, status=404).
Looking in the log again, I see "Beginning initialization" and "Returning view," so I know that the 404 is for my JSP and not my controller. My controller is working.
Other things I've tried:
Initially I did not have #EnableWebMvc on my application. Without it, the log was empty except my log statements. When I added #EnableWebMvc, this is now logged with the 404: No mapping for GET /WEB-APP/jsp/initialization/begin.jsp (or whatever other directory I've tried other than "WEB-APP").
I've tried running this directly on the pure command line with mvn spring-boot:run
I've tried running this in IntelliJ IDEA with a Maven run configuration and command spring-boot:run (same result)
I've tried both <packaging>jar</packaging> and <packaging>war</packaging>, but neither make a difference, because neither a JAR nor a WAR are ever made. Maven runs the application directly out of the target/classes directory instead of creating an artifact.
When I've tried WEB-INF or META-INF instead of WEB-APP or webapp or something else, I've seen a logged warning: Path with "WEB-INF" or "META-INF": [WEB-INF/jsp/initialization/begin.jsp]
I have also confirmed that my JSPs are present in target/classes/WEB-APP/jsp (or whatever other directory I've tried other than "WEB-APP"), so they do exist.
I'm at a loss how to proceed. I'm beginning to think I need to ditch Spring Boot and stick with a traditional boilerplate Spring Web MVC application with a Servlet config and a Tomcat installation, but I was really excited about the "just runs" aspect of Spring Boot. Any help would be appreciated.
UPDATE 1
After reading this Spring documentation about JSP limitations, I now know that I have to use <packaging>war</packaging>, and I'm using that now, but it hasn't made a difference. I'm starting to suspect that the underlying problem here is that maven spring-boot:run doesn't create a WAR and run it, it just builds everything to target/classes and runs it from there.
Also, after finding this old, official Spring boot samples application, I've changed my project structure a little:
- source
- production
- java
- [my source code packages]
- resources
- [my resource packages]
- webapp
- META-INF
- WEB-INF
- jsp
- initialization
- begin.jsp
- test
- java
- resources
Updated my view resolver configuration:
resolver.setPrefix("/jsp/");
resolver.setSuffix(".jsp");
And added this to my POM:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.1</version>
<configuration>
<warSourceDirectory>source/production/webapp</warSourceDirectory>
</configuration>
</plugin>
If I run mvn package, my WAR gets created correctly (classes and JSPs all where they should be), but neither mvn spring-boot:run nor mvn package spring-boot:run work—I still get 404 errors resolving my JSPs.
The old Spring Boot sample application linked to above puts the JSPs in WEB-INF/jsp, but I can't do that, because that results in the warning Path with "WEB-INF" or "META-INF": [WEB-INF/jsp/initialization/begin.jsp] (and still 404). What's frustrating is that this sample application doesn't exist anymore, nor does any new variation of it. I can't find any updated version that works with the newest version of Spring Boot. The sample application was deleted in 2.2.x.
Can you try by changing the scope of tomcat-embed-jasper to provided as this dependency is needed to compile JSPs.
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>[9.0.38,)</version>
<scope>provided</scope>
</dependency>
Edit:
I looked for various spring-boot + jsp projects over internet. I noticed that they have they also have spring-boot-starter-tomcat with provided scope. Can you try this.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>[2.3.4.RELEASE,3)</version>
<scope>provided</scope>
</dependency>
References :
https://mkyong.com/spring-boot/spring-boot-hello-world-example-jsp/
https://dzone.com/articles/spring-boot-2-with-jsp-view
Edit-2 :
So this time i created a new springboot project. Did bare minimum setup to get jsp rendered. So basically i followed this tutorial and my project was running fine.
Then I replaced the pom.xml with yours and the i got the same error you mentioned in the question.
Then while doing trial and error i removed the <version>[9.0.38,)</version> from <artifactId>tomcat-embed-jasper</artifactId> and it started working for me.
<!--I have removed version here and it started working for me-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<!-- <version>[9.0.38,)</version>-->
<scope>provided</scope>
</dependency>
Although i have different directory structure. But as you mentioned that is not the cause of issue.
I have uploaded the project to github. Feel free to pull it run it locally.
Github
Assuming the following location for your web content (which should be outside the classpath AFAIK) source/production/webapp. Spring Boot will ignore this due to a hardcoded path in DocumentRoot for detection of directories when running from the command-line or IDE (it will work when building a war and running that).
As a workaround you can add a TomcatContextCustomizer as a bean to detect the path and set it as the correct base.
package com.my.project;
#SpringBootApplication
public class Application extends SpringBootServletInitializer
{
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application)
{
return application.sources(DemoApplication.class);
}
public static void main(final String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public TomcatContextCustomizer docBaseCustomizer()
{
return new TomcatContextCustomizer()
{
public void customize(Context context)
{
File root = new File("source/production/webapp");
if (root.exists() && root.isDirectory())
{
context.setDocBase(root.getAbsolutePath());
}
}
}
}
}
Now add the following to your application.properties
spring.mvc.view.prefix=/jsp/
spring.mvc.view.suffix=.jsp
NOTE: The removal of the other annotations can only be done if your #SpringBootApplication annotated class is in the com.my.project package. It will then automatically detect the other classes (like entities and repositories).

JAX-RS does not work with Spring Boot 1.4.1

I am trying to develop a simple JAX-RS based web service using Spring Boot version 1.4.1.RELEASE. However getting this exception -
java.lang.IllegalStateException: No generator was provided and there is no default generator registered
at org.glassfish.hk2.internal.ServiceLocatorFactoryImpl.internalCreate(ServiceLocatorFactoryImpl.java:308) ~[hk2-api-2.5.0-b05.jar:na]
at org.glassfish.hk2.internal.ServiceLocatorFactoryImpl.create(ServiceLocatorFactoryImpl.java:268) ~[hk2-api-2.5.0-b05.jar:na]
at org.glassfish.jersey.internal.inject.Injections._createLocator(Injections.java:138) ~[jersey-common-2.23.2.jar:na]
at org.glassfish.jersey.internal.inject.Injections.createLocator(Injections.java:123) ~[jersey-common-2.23.2.jar:na]
at org.glassfish.jersey.server.ApplicationHandler.<init>(ApplicationHandler.java:330) ~[jersey-server-2.23.2.jar:na]
at org.glassfish.jersey.servlet.WebComponent.<init>(WebComponent.java:392) ~[jersey-container-servlet-core-2.23.2.jar:na]
at org.glassfish.jersey.servlet.ServletContainer.init(ServletContainer.java:177) ~[jersey-container-servlet-core-2.23.2.jar:na]
at org.glassfish.jersey.servlet.ServletContainer.init(ServletContainer.java:369) ~[jersey-container-servlet-core-2.23.2.jar:na]
Here are my program details -
Dependencies included in POM.xml -
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
And here is JerseyConfig file -
package com.test.main;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.stereotype.Component;
import com.test.resources.TutorialResource;
#Component
public class JerseyConfig extends ResourceConfig{
public JerseyConfig() {
register(TutorialResource.class);
packages("com.test.resources");
}
}
Important: Looks like this issue is not present in most recent versions of Spring Boot. However the content of this answer can still be used as a guide when you want to create an application with Spring Boot and Jersey.
The layout of the JAR has changed in Spring Boot 1.4.1
The layout of executable jars has changed in Spring Boot 1.4.1: application’s dependencies are now packaged in BOOT-INF/lib rather than lib, and application’s own classes are now packaged in BOOT-INF/classes rather than the root of the jar. And it affects Jersey:
Jersey classpath scanning limitations
The change to the layout of executable jars means that a limitation in Jersey’s classpath scanning now affects executable jar files as well as executable war files. To work around the problem, classes that you wish to be scanned by Jersey should be packaged in a jar and included as a dependency in BOOT-INF/lib. The Spring Boot launcher should then be configured to unpack those jars on start up so that Jersey can scan their contents.
I've found that registering classes instead of packages works. See below the steps to create an application with Spring Boot and Jersey.
Creating a web application with Spring Boot and Jersey
Ensure your pom.xml file declares spring-boot-starter-parent as the parent project:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.1.RELEASE</version>
</parent>
You also need the following dependencies:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
And the Spring Boot Maven plugin:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
For example purposes, create a Jersey resource class annotated with #Path and define a resource method to handle GET requests, producing text/plain:
#Path("/greetings")
public class GreetingResource {
#GET
#Produces(MediaType.TEXT_PLAIN)
public Response getGreeting() {
return Response.ok("Hello, World!").build();
}
}
Then create a class that extends ResourceConfig or Application to register the Jersey resources and annotated it with #ApplicationPath. Registering classes instead of registering packages works with Spring Boot 1.4.1:
#Component
#ApplicationPath("api")
public class JerseyConfig extends ResourceConfig {
#PostConstruct
private void init() {
registerClasses(GreetingResource.class);
}
}
And finally create a Spring Boot class to execute the application:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
If you want to test this web service, you can use the JAX-RS Client API:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class GreetingResourceTest {
#LocalServerPort
private int port;
private URI uri;
#Before
public void setUp() throws Exception {
this.uri = new URI("http://localhost:" + port);
}
#Test
public void testGreeting() {
Client client = ClientBuilder.newClient();
Response response = client.target(uri).path("api").path("greetings")
.request(MediaType.TEXT_PLAIN).get();
String entity = response.readEntity(String.class);
assertEquals("Hello, World!", entity);
}
}
To compile and run the application, follow these steps:
Open a command line window or terminal.
Navigate to the root directory of the project, where the pom.xml resides.
Compile the project: mvn clean compile.
Package the application: mvn package.
Look in the target directory. You should see a file with the following or a similar name: spring-jersey-1.0-SNAPSHOT.jar.
Change into the target directory.
Execute the JAR: java -jar spring-jersey-1.0-SNAPSHOT.jar.
The application should be available at http://localhost:8080/api/greetings.
Note 1: Have a look at the Spring Boot documentation. There's a section dedicated to Jersey.
Note 2: When producing JSON, ensure you have a JSON provider registered. ResourceConfig should take care of that though (just ensure that the dependencies are on the classpath).
Although Jersey cannot scan your classes inside the new version of the fat boot jar, you can achieve the same effect using Spring classpath scanning facilities. This way you can scan a package similarly to ResourceConfig.packages():
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new AnnotationTypeFilter(Provider.class));
scanner.addIncludeFilter(new AnnotationTypeFilter(Path.class));
config.registerClasses(scanner.findCandidateComponents("your.package.to.scan").stream()
.map(beanDefinition -> ClassUtils.resolveClassName(beanDefinition.getBeanClassName(), config.getClassLoader()))
.collect(Collectors.toSet()));
Note: please have a look at the source of org.glassfish.jersey.server.internal.scanning.AnnotationAcceptingListener. This is the stock solution and you can see that it does the same: it scans for classes annotated with #Path or #Provider (but doesn't manage to find anything because of the broken scanning mechanism).
Update:
I had a custom config which didn't extend ResourceConfig but returned an instance of it as a bean.
If you look at the official Spring example, you can insert the code above into the JerseyConfig() constructor (instead of the two register(...) calls). The only difference is that instead of calling config.registerClasses(...) you simply call registerClasses(...) in the constructor.
I think you should annotate your JerseyConfig with #Configuration and not #Component.

Spring Boot Admin, client registration results in bad request

I am going to register my spring boot application to spring-boot-admin server.
Here is my SpringBootAdminApplication.java
#Configuration
#EnableAutoConfiguration
#EnableAdminServer
public class SpringBootAdminApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootAdminApplication.class, args);
}
}
And pom.xml:
<dependencies>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-example</artifactId>
<version>1.0.5</version>
</dependency>
</dependencies>
and application.properties
server.port = 8080
Server is running now:
Now, in client side:
properties:
spring.boot.admin.url=http://localhost:8080
info.version=#project.version#
spring.application.name=rest-module
pom.xml:
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>1.3.0</version>
</dependency>
But, when i run the spring boot from client, i get this error:
Created POST request for "http://localhost:8080/api/applications"
Setting request Accept header to [application/json, application/json, application/*+json, application/*+json]
Writing [Application [id=null, name=rest-module, managementUrl=http://Hayatloo-PC:8082, healthUrl=http://Hayatloo-PC:8082/health, serviceUrl=http://Hayatloo-PC:8082]] as "application/json" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter#1637320b]
Failed to register application as Application [id=null, name=rest-module, managementUrl=http://Hayatloo-PC:8082, healthUrl=http://Hayatloo-PC:8082/health, serviceUrl=http://Hayatloo-PC:8082] at spring-boot-admin (http://localhost:8080/api/applications): 400 Bad Request
Why id=null ?
You messed up the versions. Try to update the client and server to the same versions. We try too keep em compatible but from 1.0.x to 1.3.x you have no chance.
Btw current version is 1.3.2.
Additional you are using the sample as dependency. This indeed works, but I wouldn't recommend it. You better setup your server as described in the guide. http://codecentric.github.io/spring-boot-admin/1.3.2/

Jersey Hk2 injecting #Service annotated classes

For the life of me I cannot get Jersey with hk2 to automatically discover #Service annotated classes and inject them. I have tried to follow every advice on stack overflow, jersey and hk2 documentation and still no luck. I am trying to inject a simple echo service into a Jersey resource. The skeleton is generated from the simple webapp maven archetype for Jersey, which I tried to extend. This is what I have so far:
pom.xml
<build>
<finalName>sandbox</finalName>
<plugins>
<plugin>
<groupId>org.glassfish.hk2</groupId>
<artifactId>hk2-inhabitant-generator</artifactId>
<version>2.3.0</version>
<executions>
<execution>
<configuration>
<verbose>true</verbose>
</configuration>
<goals>
<goal>generate-inhabitants</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.glassfish.jersey</groupId>
<artifactId>jersey-bom</artifactId>
<version>${jersey.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet-core</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.hk2</groupId>
<artifactId>hk2</artifactId>
<version>2.3.0</version>
</dependency>
</dependencies>
web.xml
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>my.package.jerseytest</param-value>
</init-param>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>my.package.jerseytest.application.Application</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
my.package.jerseytest.application.Application
public class Application extends ResourceConfig {
public Application() {
ServiceLocator locator = ServiceLocatorUtilities.createAndPopulateServiceLocator();
}
}
my.package.jerseytest.service.EchoService
#Service
public class EchoService {
public String generateResponse(String echo) {
return echo;
}
}
my.package.jerseytest.resource.MyResource
#Path("myresource")
public class MyResource {
#Inject
EchoService echoService;
#GET
#Produces(MediaType.TEXT_PLAIN)
public String getIt() {
return echoService.generateResponse("Got it!");
}
}
I have checked that the inhibitant-generator does in fact run and produce its output, yet when running the Tomcat server GETting http://localhost:8080/sandbox/webapi/myresource I get
SEVERE: Servlet.service() for servlet [Jersey Web Application] in context with path [/sandbox] threw exception [A MultiException has 3 exceptions. They are:
1. org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=EchoService,parent=MyResource,qualifiers={},position=-1,optional=false,self=false,unqualified=null,932014249)
2. java.lang.IllegalArgumentException: While attempting to resolve the dependencies of my.package.jerseytest.resource.MyResource errors were found
3. java.lang.IllegalStateException: Unable to perform operation: resolve on my.package.jerseytest.resource.MyResource
] with root cause
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=EchoService,parent=MyResource,qualifiers={},position=-1,optional=false,self=false,unqualified=null,932014249)
Any ideas what I am missing? I would appreciate any help :(
NB! I know about
How does ServiceLocator find #Service and #Contact automatically in HK2?
HK2 annotations not being handled
https://hk2.java.net/inhabitant-generator.html
https://hk2.java.net/2.2.0-b25/getting-started.html
https://java.net/jira/browse/HK2-165
but they did not help me...
I'm combining the insight I gained from these two questions:
How does ServiceLocator find #Service and #Contact automatically in HK2?
jersey + grizzly + hk2: Dependency injection, but not into resource
Firstly, use the HK2 Metadata Generator (or the Inhabitant Generator) in your build chain (as you do already). This will scan your source and create META-INF/hk2-locator/default.
Secondly, create a new ServiceLocator, populated with the services from the metadata:
ServiceLocator locator = ServiceLocatorUtilities.createAndPopulateServiceLocator();
Now pass it to Grizzly. Quoting #peeskillet:
Jersey has it's own ServiceLocator, and it's not easy to try a obtain a reference to it. We could give Jersey our ServiceLocator, but Jersey ultimately still creates it's own locator and will populate it with our locator.
ResourceConfig config = new MyApplicationConfig();
HttpServer server = GrizzlyHttpServerFactory.createHttpServer(
URI.create(BASE_URI),
config,
serviceLocator
);
I solved my problem quite like this one by using a class that extends AbstractBinder, instantiating it and registering it with the application.
resourceConfig.register(new DependencyBinder());
Also,
/**
* dependency injection bindings.
* Jersey requires that service implementations are bound to their contracts this way.
*/
public final class DependencyBinder extends AbstractBinder {
#Override
protected final void configure() {
bind(StatusServiceImpl.class).to(StatusService.class);
}
}
Try adding the packages that need scanned in your Application constructor. The "true" parameter on packages means to scan the package recursively:
public class Application extends ResourceConfig {
public Application() {
packages(true, "my.package.jerseytest");
ServiceLocator locator = ServiceLocatorUtilities.createAndPopulateServiceLocator();
}
}
Use packages(true, "my.package.jerseytest");
And use org.glassfish.jersey.spi.Contract not org.jvnet.hk2.annotations.Contract annotation.
And use simple interfaces without generics.
Try adding #Stateless
#Path("myresource")
#Stateless
public class MyResource {
#Inject
EchoService echoService;
...
}

Categories

Resources