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.
Related
Importing custom spring-boot library jar to my application, and autowiring show the following error when I run the application
Parameter 0 of constructor in com.dilla.de.orca.addresssvc.service.TestScheduler required a bean of type 'com.dilla.de.orca.flowersvc.service.FlowerServiceImpl' that could not be found.
The Library module has following packages
Configuration
FlowerServiceConfiguration - create beans for Jaxb2Marshaller, WebServiceTemplate,
webserviceMessageSender
Model
Service
FlowerService (an interface no annotation)
FlowerServiceImp implements the interface and calls FlowerAdapter
FlowerAdapter (call external webservice)
Src/main/resources
Application.properties define external webservice url, and related properties
FlowerSvcLibApplication.java
public static void main(final String[] args) {
SpringApplication.run(FlowerSvcLibApplication.class, args);
}
I was autowiring the Flower Service interface as follows, in my application to test functionality
of library jar
#Component
public class MyFlowerService {
private FlowerService service;
#Autowired
public MyFlowerService(final FlowerService service) {
this.service = service;
}
I got the error I posted earlier. I did do more research, and one suggestion was creating “own auto-configuration”, but I still did not understand. How do I create autoConfiguration class to handle to autowire my library class, and also how does client using my library provide application property values. Currently, I hard coded actual values for example a webservice url, and now client can change this to be test or prod, and to do that how does my library module setup should be?
Please check #ComponenetScan & make sure that it has package path something like this “com.dilla.de.orca”
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 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
For a monitoring software written in Java I consider using Google Guice as DI provider. The project needs to load its configuration from an external resource (file or database). The application is designed to run in standalone mode or in a servlet container.
At the moment the configuration does not contain bindings or parameters for dependency injection, only some global application settings (JDBC connection definitions and associated database management/monitoring objects).
I see two options:
to use another library, for example Apache Commons Configuration, which supports file and JDBC configuration sources (and many other)
or
to use a file based addon for Guice like guice-xml-config to store the application options (this would allow to configure the DI part later if it becomes necessary).
Would you recommend to use Guice for both tasks, or keep the general application configuration separate from the dependency injection? Which advantages and disadvantages would you consider the most important ones?
It's straightforward to slurp a property file in a Guice module:
public class MyModule extends AbstractModule {
#Override
protected void configure() {
try {
Properties properties = new Properties();
properties.load(new FileReader("my.properties"));
Names.bindProperties(binder(), properties);
} catch (IOException ex) {
//...
}
}
}
Later it's easy to switch from Properties to other config sources.
[Edit]
BTW, you can get the injected properties by annotating it with #Named("myKey").
Check the governator library:
https://github.com/Netflix/governator/wiki/Configuration-Mapping
You will get a #Configuration annotation and several configuration providers. In code it helps to see where is You configuration parameters used:
#Configuration("configs.qty.things")
private int numberOfThings = 10;
Also, You will get a nice configuration report on startup:
https://github.com/Netflix/governator/wiki/Configuration-Mapping#configuration-documentation
Try Guice configuration available on maven central, it's support Properties, HOCON and JSON format.
You can inject properties from file application.conf to your service as :
#BindConfig(value = "application")
public class Service {
#InjectConfig
private int port;
#InjectConfig
private String url;
#InjectConfig
private Optional<Integer> timeout;
#InjectConfig("services")
private ServiceConfiguration services;
}
You must install the modules ConfigurationModule as
public class GuiceModule extends AbstractModule {
#Override
protected void configure() {
install(ConfigurationModule.create());
requestInjection(Service.class);
}
}
I ran into the same problem in my own project. We had already chosen Guice as DI-framework and to keep things simple wanted to use it also with configuration.
We ended up reading the configuration from properties file using Apache Commons Configuration and binding them to Guice injector like suggested in Guice FAQ How do I inject configuration parameters?.
#Override public void configure() {
bindConstant().annotatedWith(ConfigurationAnnotation.class)
.to(configuration.getString("configurationValue"));
}
Reloading of configuration supported by Commons Configuration is also quite easy implement into Guice injection.
#Override public void configure() {
bind(String.class).annotatedWith(ConfigurationAnnotation.class)
.toProvider(new Provider<String>() {
public String get() {
return configuration.getString("configurationValue");
}
});
}
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();