I tried implementing custom context injection just like in this answer:
#Provider
public class DaoContextProvider extends SingletonTypeInjectableProvider<Context,Bar> {
public DaoContextProvider() {
super(Bar.class, new Bar("haha"));
}
}
And here's my controller class, to witch I want to inject my context:
#Path("foo")
public class Foo {
#Context
private Bar message;
#GET
public String index() {
return String.format("%s", message );
}
}
But in response message is null.
I tried adding my context provider to singletons, as recomended:
#javax.ws.rs.ApplicationPath("webresources")
public class ApplicationConfig extends Application {
public ApplicationConfig() {
getSingletons().add(new DaoContextProvider());
}
//...
But then my artifact does not even deploy, and provides me with this error:
Artifact server:war exploded: java.io.IOException: com.sun.enterprise.admin.remote.RemoteFailureException: Error occurred during deployment: Exception while loading the app : java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: org.apache.catalina.LifecycleException: java.lang.UnsupportedOperationException. Please see server.log for more details.
I would provide server.log, as described in exception, but I dont know where to find this log.
The set returned from getSingletons() is unmodifiable. Instead we need to override the method
#Override
public Set<Object> getSingletons() {
Set<Object> singletons = new HashSet<>();
singletons.add(new new DaoContextProvider());
return singletons;
}
Note though that a direct Application subclass is limited in functionality compared to the Jersey specific way. In Jersey, the preferred approach is to use a ResourceConfig subclass (which is actually a subclass of Application). For example (you can use PackagesResourceConfig to scan packages).
#ApplicationPath("/webresources")
public class AppConfig extends PackagesResourceConfig {
public AppConfig() {
// scans the package and sub-packages.
super("package.where.all.your.resource.and.providers.are");
getProperties().put("some properites", "to set");
getContainerRequestFilters().add(new SomeFiltersToRegister());
getProviderSingletons().add(new SomeProvidersToAdd());
// see the ResourceConfig API for more methods.
}
}
This will scan for #Path and #Provider annotated classes, so we don't need to explicitly register everything. Though some providers will need to be explicitly registered. Your particular provider doesn't need to be registered though. It i picked up in the package scan.
UPDATE
Ok since you say you are using Glassfish 4.1, the first thing you should understand is that Glassfish 4 uses Jersey 2.x. So any Jersey 1.x dependencies/jars you have, you should get rid of them. Use only Jersey 2.x dependencies, and make sure that they are only compile-time dependencies, as you do not want conflicting versions making it's way to Glassfish, that already has a version of them.
What this means is that your current implementation of the DaoContextProvider will not work. The SingletonTypeInjectableProvider is Jersey 1.x class, and the Jersey 2.x runtime will just ignore it.
In Jersey 2.x there are a few ways to configure injectable objects. One way is to create a Factory for the object. For example.
public class DaoContextProvider implements Factory<Bar> {
#Override
public Bar provide() {
return new Bar("boo hoo!");
}
#Override
public void dispose(Bar bar) {}
}
In Jersey 2.x, the API for ResourceConfig has change, and we can extend it directly. For example
#ApplicationPath("/webresources")
public class AppConfig extends ResourceConfig {
public AppConfig() {
packages("com.stackoverflow.jersey");
register(new AbstractBinder(){
#Override
protected void configure() {
bindFactory(DaoContextProvider.class)
.to(Bar.class)
.in(RequestScoped.class);
}
});
}
}
You can see the AbstractBinder. That is how we register the DaoContextProvider with the Bar class. So now Bar can be injected to your resource classes.
The only Maven dependency you need to pull everything else in, is
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>2.19</version>
<scope>provided</scope>
</dependency>
Notice the provided scope, so that it does not get built to the war. If you are not using Maven, then grab all the jars in the Jersey JAX-RS 2.0 RI bundle. Remember that you should make them only compile time dependencies. They should no get built into the war.
See Also:
Custom Injection and Lifecycle Management
Related
Is every rest service starting with extending that application class and defining applicationpath? What is the lifecyce of that application class itself? Here is an example:
import javax.ws.rs.core.Application;
#javax.ws.rs.ApplicationPath("resources")
public class ApplicationConfig extends Application {}
Is this a servlet? Is it always alive? How shall I understand this class? Is it a cdi bean? Does the server creates this class on every request?
What is Application?
Application is a deployment agnostic abstract class provided by JAX-RS for configuring and registering the components of a JAX-RS application and it's also used to supply additional metadata to the application.
Application is one of the types that can be injected using the #Context annotation. For more details, refer to this answer.
Subclasses of Application
Application subclasses can implement methods such as getClasses(), getSingletons() and getProperties() for configuring and registering components and properties.
Application subclasses can be annotated with #ApplicationPath, defining the base URI for the JAX-RS resource classes (classes annotated with #Path). Application subclasses are instantied once when the web application starts and they are managed by the JAX-RS runtime.
The simplest implementation possible is as following:
#ApplicationPath("api")
public SampleApplication extends Application {
}
In the example above no resources classes or providers are registered, so the JAX-RS runtime will scan the classpath for JAX-RS components and will register them automatically.
However, according to this post from Jakub Podlesak, this approach is discouraged in production environments:
The above example works great. When started, the application just scans the actual class-path, and adds every single JAX-RS component class found there to the actual runtime configuration. Isn't is great? Frankly, this kind of configuration could work just fine. Until someone changes either the system configuration (system class-path) or the way how you application is being packaged (a new 3rd party component could be added/removed from the application class-path then). These changes could be out of your control and if one of them happens, you application configuration could break. For this reason, it is not wise to use this kind of configuration in a production environment.
Jersey, the JAX-RS reference implementation, provides the ResourceConfig class. Compared to Application, ResourceConfig provides advanced capabilities to simplify registration of JAX-RS components, such as scanning for root resource and provider classes in a provided classpath or a in a set of package names, etc. For more details, refer to the Jersey documentation.
Working with multiple Application subclasses
Is also worth mentioning that you are not restricted to a single Application subclass per web application. The same WAR can have multiple Application subclasses. For more details, have a look at this post from Adam Bien:
To deploy multiple JAX-RS applications with different URIs in one WAR you will have to create one javax.ws.rs.core.Application subclass per such an application (or use web.xml for this purpose). Obviously the in Java EE ubiquitous Convention over Configuration (or Configuration by Exception) cannot work any more: you will have to explicitly configure resources in each subclass by overriding the method getClasses or getSingletons:
#Path("first")
public class FirstResource {
#GET
public String first() {
return "first";
}
}
#ApplicationPath("one")
public class JAXRSConfigurationOne extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new HashSet<>();
resources.add(FirstResource.class);
return resources;
}
}
#Path("second")
public class SecondResource {
#GET
public String first() {
return "second";
}
}
#ApplicationPath("two")
public class JAXRSConfigurationTwo extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new HashSet<>();
resources.add(SecondResource.class);
return resources;
}
}
Both JAX-RS applications become accessible through distinct URIs: http://localhost:8080/multiple-roots/one/first and http://localhost:8080/multiple-roots/two/second
What if no Application subclass is present?
If no Application subclass is present, the JAX-RS implementations are required to add a servlet and set its name to javax.ws.rs.Application and to automatically discover all resource classes and providers which must be packaged with the application.
For further details, have a look at the chapter 2 of the JAX-RS 2.1 specification.
I need to test my RESTful service and I thought the most appropriate way to do it would be using the Jersey Test Framework since I am already using Jersey for my service.
However, when running a simple test I am getting a UnsatisfiedDependencyException because the implementation of the DAO I am using in the service is not found (it's being set via DI). This error used to happen to me everytime I modified my pom.xml Maven configuration file, mostly when adding or removing dependencies. Supposedly, the IDE I'm using, IntelliJ IDEA is supposed to regenerate the artifacts automatically, but for me to avoid this dependency failure I needed to remove the old artifact from the Tomcat server deployment options as well as from the project structure, and add it again.
The problem here is that the test framework is using a separate server, as the documentation said. I am using the following one:
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>2.23.1</version>
</dependency>
I don't know how to solve the dependency issue if I am using a different server.
The top error message in the exception stack says:
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no
object available for injection at SystemInjecteeImpl
Any ideas?
Edit. I managed to patch it with a dirty solution.
First, let me show you how I was injecting the DAO:
// Binder.java
public class Binder extends AbstractBinder {
#Override
protected void configure() {
bind(TournamentDao.class).to(ITournamentDao.class).in(Singleton.class);
}
}
// DependencyInjectionFeature.java
#Provider
public class DependencyInjectionFeature implements Feature {
#Override
public boolean configure(FeatureContext context) {
context.register(new Binder());
return true;
}
}
// EventSchedulerService.java
#Path("eventscheduler")
public class EventSchedulerService {
private ITournamentDao dao;
#Inject
public EventSchedulerService(ITournamentDao tournamentDao) {
dao = tournamentDao;
}
}
The web configuration file web.xml scans the package where these classes are placed (the package has the same name than the one where the test exists) and perform the dependency injection since it finds the #Provider annotation.
To patch my issue I did the exact same thing in the test class:
public class EventSchedulerServiceTest extends JerseyTest {
protected Application configure() {
ResourceConfig config = new ResourceConfig(EventSchedulerService.class);
config.register(new AbstractBinder() {
#Override
protected void configure() {
bind(TournamentDao.class).to(ITournamentDao.class).in(Singleton.class);
}
});
return config;
}
}
Obviously this is a bad idea because now I have duplicate code; but it did the trick. I'd like to know how to make the test server to use the service configuration correctly.
I remember from a previous discussion in the comments here, you had the problem of not using a ResourceConfig to configure your main application. You we instead using web.xml.
The reason you are using a Feature annotated with #Provider is that you we're using package scanning in your web.xml
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>your.packages.to.scan</param-value>
</init-param>
The package scan picked up the #Provider on the Feature, and automatically registers it. This provider is what is used to register the Binder, which is what configured the dependency injection.
As mentioned, a Jersey application can be configured in a ResourceConfig or in a web.xml. Anything jersey configuration you can do in the web.xml, you can also do in the ResourceConfig. There are a bunch of different ways you can register components with the ResourceConfig
You can just call register
config.register(new DependencyInjectionFeature());
You can also package scan, the same way you are doing in the web.xml, by calling packages
config.packages("your.packages.to.scan");
You should also look at your implementation of the Binder class. It extends AbstractBinder. The look at how you are currently configuring the DI in the test
config.register(new AbstractBinder() {
#Override
protected void configure() {
...
}
});
You are registering an AbstractBinder instance. So you know that calling you can register the DI configuration calling register on the ResourceConfig, so you could instead of registering a different binder, just register an instance of the Binder class
config.register(new Binder());
So you have some options.
The application uses JDK 8, Spring Boot & Spring Boot Jersey starter and is packaged as a WAR (although it is locally run via Spring Boot Maven plugin).
What I would like to do is to get the documentation I generate on the fly (at build time) as a welcome page.
I tried several approaches:
letting Jersey serving the static contents by configuring in application.properties the proper init parameter as described here
introduce a metadata-complete=false web.xml in order to list the generated HTML document as a welcome-file.
None of that worked out.
I would like to avoid having to enable Spring MVC or creating a Jersey resource just for serving a static file.
Any idea?
Here is the Jersey configuration class (I unsuccessfully tried to add a ServletProperties.FILTER_STATIC_CONTENT_REGEX there):
#ApplicationPath("/")
#ExposedApplication
#Component
public class ResourceConfiguration extends ResourceConfig {
public ResourceConfiguration() {
packages("xxx.api");
packages("xxx.config");
property(ServerProperties.BV_DISABLE_VALIDATE_ON_EXECUTABLE_OVERRIDE_CHECK, true);
property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
}
}
And here is Spring Boot application class (I tried adding an application.properties with spring.jersey.init.jersey.config.servlet.filter.staticContentRegex=/.*html but it didn't work, I'm not exactly sure what the property key should be here):
#SpringBootApplication
#ComponentScan
#Import(DataConfiguration.class)
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Let me just first state, that the reason the static content won't be served is because of the default servlet mapping of the Jersey servlet, which is /*, and hogs up all the requests. So the default servlet that serves the static content can't be reached. Beside the below solution, the other solution is to simply change the servlet mapping. You can do that by either annotating your ResourceConfig subclass with #ApplicationPath("/another-mapping") or set the application.properties property spring.jersey.applicationPath.
In regards to your first approach, take a look at the Jersey ServletProperties. The property you are trying to configure is FILTER_STATIC_CONTENT_REGEX. It states:
The property is only applicable when Jersey servlet container is configured to run as a Filter, otherwise this property will be ignored
Spring Boot by default configures the Jersey servlet container as a Servlet (as mentioned here):
By default Jersey will be set up as a Servlet in a #Bean of type ServletRegistrationBean named jerseyServletRegistration. You can disable or override that bean by creating one of your own with the same name. You can also use a Filter instead of a Servlet by setting spring.jersey.type=filter (in which case the #Bean to replace or override is jerseyFilterRegistration).
So just set the property spring.jersey.type=filter in your application.properties, and it should work. I've tested this.
And FYI, whether configured as Servlet Filter or a Servlet, as far as Jersey is concerned, the functionality is the same.
As an aside, rather then using the FILTER_STATIC_CONTENT_REGEX, where you need to set up some complex regex to handle all static files, you can use the FILTER_FORWARD_ON_404. This is actually what I used to test. I just set it up in my ResourceConfig
#Component
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
packages("...");
property(ServletProperties.FILTER_FORWARD_ON_404, true);
}
}
For anyone who still can't get this to work, I followed the answer provided by #peeskillet, and had to make an additional change.
Previously I had created the following method in Application.java.
#Bean
public ServletRegistrationBean jerseyServlet() {
ServletRegistrationBean registration = new ServletRegistrationBean(new ServletContainer(), "/*");
registration.addInitParameter(ServletProperties.JAXRS_APPLICATION_CLASS, JerseyConfig.class.getName());
return registration;
}
The problem is that this registered the servlet for the /* path, and then setup the Jersey ResourceConfig configuration file.
Once I removed the above method, and placed the #Configuration annotation on my ResourceConfig class, I noticed the static resource could be retrieved via Spring Boot.
For completeness, this is a snippet of my ResourceConfig now.
#Configuration
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
// Application specific settings
property(ServletProperties.FILTER_FORWARD_ON_404, true);
}
}
This blog post was helpful in determining the difference approach for the ResourceConfig.
Below setup worked for me
Set
spring .jersey.type: filter
set FILTER_FORWARD_ON_404
#Configuration
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig () {
try {
register(XXX.class);
property(ServletProperties.FILTER_FORWARD_ON_404, true);
} catch (Exception e) {
LOGGER.error("Exception: ", e);
}
}
}
Note: Use #Configuration instead of #component
I am planning to implement the Rest service with help of RestEasy framework (3.0.11) and servlet version 3.
I have used resteasy-scan context to scan my rest service at time of runtime, but since servlet version 3.0 hence It is not scan and throwing exception (deployed in tomcat7).
Could you please suggest me to resolve the issue, how to scan the rest services?
Any example or configuration of web.xml would be good.
One way, without any web.xml (RESTeasy servlet) configuration is to just have an empty Application subclass, annotated with #ApplicationPath (for the url-mapping).
According to the JAX-RS spec, in a Servlet 3.x environment if you have the following
#ApplicationPath("/api")
public class MyAppliation extends Application {
}
this is enough to cause the application to register resource class and provider through classpath scanning. In contrast, we could explicitly register resources class and providers this way
#ApplicationPath("/api")
public class MyAppliation extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<Class<?>>();
classes.add(MyResource.class);
return classes;
}
#Override
public Set<Object> getSingletons() {
Set<Object> singletons = new HashSet<Object>();
singletons.add(new MyFilter());
return singletons;
}
}
According to the spec, once we return a non-empty set from either one of these overridden methods, it is assumed the application takes care of all the registration, and the classpath registration is disabled.
Now this is not what you want, but it is just a background as to why the empty class works.
We have decided to use Dependency Injection with JSR-330 annotations for our future modularization efforts, and have been very pleased with the first deliverable based on Guice 2 SVN.
Now we need to ensure and document through unit tests that the constructions we need, also work in Spring when configured programmatically (we want the same refactoring support as with Guice so no XML files). I have problems with #Provider and #Inject #Named("foo") String but I have made plain #Inject work with:
ApplicationContext ctx = new AnnotationConfigApplicationContext(LIBL_Object.class,
CORE_Provider.class);
this.object = ctx.getBean(LIBL_Object.class);
where LIBL_Object is the base class to be injected into, but the CORE_Provider does not register as I hoped within Spring.
The implementation of CORE_Provider is
package qa.jsr330.core;
import javax.inject.Provider;
public class CORE_Provider implements Provider<ProvidedInterface> {
#Override
public ProvidedInterface get() {
return new CORE_Provided();
}
}
and I want it injected into
package qa.jsr330.core;
import javax.inject.Inject;
public class LIBL_Object {
private ProvidedInterface provided;
public ProvidedInterface getProvided() {
return provided;
}
#Inject
public void setProvided(ProvidedInterface provided) {
this.provided = provided;
}
// Other stuff omitted.
}
Also we have found that we can pass configuration values very clearly using the #Named tag. This code looks like:
String hostname;
#Inject
public void setHostname(#Named("as400.hostname") String hostname) {
this.hostname = hostname;
}
where we can then register this string with Guice using
bindConstant().annotatedWith(Names.named("as400.hostname")).to(value);
So the two questions are:
How do I register the #Provider class with Spring 3 programatically?
How do I register a string constant with Spring 3 so that #Named selects it properly?
The short answer is: there is no such thing as programmatic configuration of Spring.
Despite the fact that both Spring and Guice support JSR-330 API and that Spring can be configured without XML now, their ideologies are still very different. Spring relies on static configuration, either in the form of XML files or annotated Java classes. Therefore straightforward attempt to adapt Guice-style configuration to Spring may produce difficulties.
Regarding the problem with Provider - Spring doesn't support javax.inject.Provider in the same way as toProvider() binding in Guice (by the way, this usage of Provider is not specified in JSR-330 docs). Therefore some Spring-specific annotations may be needed, for example
#Configuration
public class CORE_Provider implements Provider<ProvidedInterface> {
#Override #Bean
public ProvidedInterface get() {
return new CORE_Provided();
}
}
Binding value coming from the outside may be difficult due to static nature of Spring configuration. For example, in your case, it can be done like this:
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(IBL_Object.class);
ctx.register(CORE_Provider.class);
ctx.registerBeanDefinition("as400.hostname",
BeanDefinitionBuilder.rootBeanDefinition(String.class)
.addConstructorArgValue(value).getBeanDefinition());
ctx.refresh();