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;
...
}
Related
I used Jersey and Webflux with R2DBC. after send the POST via the postman I got this message " Cannot construct instance of reactor.core.publisher.Mono "
This is my JerseyConfiguration:
#Component
public class JerseyConfiguration
extends ResourceConfig {
public JerseyConfiguration() {
register(ProductController.class, 1);
}
}
and this is my Controller:
#Path("/v1")
#Controller
public class ProductController {
#Autowired
private ProductService productService;
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#Path("/product")
public Mono<Product> createProduct(#RequestBody Mono<Product> productMono){
return productMono.flatMap(this.productService::createProduct);
}
}
and this sis my service:
#Service
public class ProductService {
#Autowired
private ProductRepository repository;
public Mono<Product> createProduct(final Product product){
return this.repository.save(product);
}
}
and also this my 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-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Now, this is my problem; I got this message from the postman:
Cannot construct instance of `reactor.core.publisher.Mono` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
at [Source: (org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream); line: 1, column: 1]
Please let me know how to solve that problem.
Thank you
You cannot mix WebFlux and Jersey. You should choose one or the other, not both. They both provide an HTTP server engine, but:
Jersey is a Servlet JAX-RS implementation, it does not know anything about reactive streams, Mono, Flux, etc.
Webflux is the Spring HTTP server engine based on reactive streams and async Netty HTTP server.
If you look at Spring Boot reference documentation, section 3.5: Web, you will see that Jersey is one of the available engines, competing with other possible engines, i.e Web MVC and web reactive (webflux).
So, the answer is : Jersey is incompatible with Webflux, and you must choose between Webflux reactive Web and Spring rest annotation, or Jersey and Jax_RS without using Mono/Flux as return-type.
Note 1 : You should annotate your class with #RestController whe using webflux, so it understand that method return is the HTTP response body (see the last paragraph of reference documentation section 1.4.1: #Controller for details.
Note 2 : If you really want to use jersey, but you still require to consume Mono objects from other parts of your system, you might use one of the conversion functions provided by Reactor to return an object that jersey can work with. For example, on Mono object, you will find a toFuture() method. You could also block(), but it could be dangerous.
I am using "Spring Boot - 2.1.5.RELEASE" and "Spring framework - 5.1.7.RELEASE".
JMS listener annotation is not picking a message from a MQ queue. No error logs are rolling in my IntelliJ IDEA as well.
About my project - I am exposing a REST service which sends and receives a message from MQ (used JMSTemplate annotation). Till now it was working. Now I have to add another class which should listen for a queue. Hence added #JMSListener annotation which is not picking any message. Any insight will be really helpful.
The pom has below specified dependencies.
<dependency>
<groupId>com.ibm.mq</groupId>
<artifactId>mq-jms-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.ibm.mq</groupId>
<artifactId>com.ibm.mq.allclient</artifactId>
<version>9.1.4.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
The class has below specified method.
import org.springframework.jms.annotation.JmsListener;
#Component
public class PickMyMessage {
#JmsListener(destination = "IN",containerFactory = "myFactory")
public void pullMyMessaage(String message){
System.out.println("Message is pulled..");
}
}
My main application has #EnableJMS annotation. In my application.yml file I have given below information.
ibm:
mq:
channel: MY.APP.SVRCONN
connName: 192.168.0.1(1415)
password: Pswd
queueManager: QM01
user: appsrv
My Bean class configs are
My JMS connection factory as below.
#Bean
public JmsListenerContainerFactory<?> myFactory(ConnectionFactory connectionFactory,
DefaultJmsListenerContainerFactoryConfigurer configurer) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
configurer.configure(factory, connectionFactory);
}
Not sure what else I am missing here. Any insight will be really helpful.
Instead of my existing project, if i create any simple demo project it is working as expected.
The class PickMyMessage is not a Spring bean, that's why Spring ignores it. Declare it as a #Bean or as a #Component.
May be there are further problems. But this is the first one that needs to be resolved.
In jersy 2 application, I have the following configuration to setup swagger.
#ApplicationPath("/*")
public class CreativeDeliveryServiceApplication extends ResourceConfig {
/**
* Registers the resources for the application and binds implementations to
* interfaces for dependency injection.
*/
public CreativeDeliveryServiceApplication() {
register(MultiPartFeature.class);
.......
register(io.swagger.jaxrs.listing.ApiListingResource.class);
register(io.swagger.jaxrs.listing.SwaggerSerializers.class);
BeanConfig beanConfig = new BeanConfig();
beanConfig.setVersion("1.0.0");
beanConfig.setSchemes(new String[]{"http"});
beanConfig.setHost("localhost:8080");
beanConfig.setBasePath("/api");
beanConfig.setResourcePackage("com.my.resources");
beanConfig.setScan(true);
packages(true, "com.my.resources");
}
}
I tried to locate swagger.json at localhost:8080/api/swagger.json, but I got 404 resource not found.
I assume I should follow localhost:8080/{ApplicationPath}/{basePath}/swagger.json
because my app path is #ApplicationPath("/*"), so I leave it out as
localhost:8080/api/swagger.json
How can I trouble shot this?
Pom.xml
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-jersey2-jaxrs</artifactId>
<version>1.5.0</version>
</dependency>
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.
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