Specify Custom Application Context - java

We are migrating some of our data services from Jersey 1.x using jersey-spring to Jersey 2.x using jersey-spring3.
We have a few test classes that inherit from JerseyTest. Some of these classes use custom applicationContext.xml files that are not specified in the web.xml file.
In Jersey 1.x the test classes that extended JerseyTest could call the super constructor with a WebappDescriptor.Builder to which a context parameter could be passed to set or override the application context path.
E.g.
public MyTestClassThatExtendsJerseyTest()
{
super(new WebAppDescriptor.Builder("com.helloworld")
.contextParam( "contextConfigLocation", "classpath:helloContext.xml")
.servletClass(SpringServlet.class)
.contextListenerClass(ContextLoaderListener.class)
.requestListenerClass(RequestContextListener.class).build());
}
How can the same be achieved with Jersey 2.x?
I have combed through the API docs, user guides and some of the sources but was unable to find an answer.

This didn't work for me as I was not using the .xml style configuration, I was using #Configuration annotations. So I had to directly provide the application context to the ResourceConfig class.
I defined the configure method in my JerseyTest like so:
#Override
protected Application configure() {
ResourceConfig rc = new ResourceConfig();
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
rc.property("contextConfig", ctx);
}
where SpringConfig.class is my class with the #Configuration annotation and
importing org.springframework.context.annotation.AnnotationConfigApplicationContext

Lets assume your Application looks like:
#ApplicationPath("/")
public class MyApplication extends ResourceConfig {
/**
* Register JAX-RS application components.
*/
public MyApplication () {
// Register RequestContextFilter from Spring integration module.
register(RequestContextFilter.class);
// Register JAX-RS root resource.
register(JerseySpringResource.class);
}
}
Your JAX-RS root resource like:
#Path("spring-hello")
public class JerseySpringResource {
#Autowired
private GreetingService greetingService;
#Inject
private DateTimeService timeService;
#GET
#Produces(MediaType.TEXT_PLAIN)
public String getHello() {
return String.format("%s: %s", timeService.getDateTime(), greetingService.greet("World"));
}
}
And you have spring descriptor named helloContext.xml available directly from your class-path. Now you want to test your getHello resource method using Jersey Test Framework. You can write your test like:
public class JerseySpringResourceTest extends JerseyTest {
#Override
protected Application configure() {
// Enable logging.
enable(TestProperties.LOG_TRAFFIC);
enable(TestProperties.DUMP_ENTITY);
// Create an instance of MyApplication ...
return new MyApplication()
// ... and pass "contextConfigLocation" property to Spring integration.
.property("contextConfigLocation", "classpath:helloContext.xml");
}
#Test
public void testJerseyResource() {
// Make a better test method than simply outputting the result.
System.out.println(target("spring-hello").request().get(String.class));
}
}

Related

How inject Hk2 beans with Guice

There is a dropwizard app, which is jersey based.
I rewrote Hk2 bean definitions into Guice and now I can inject Guice beans into Jersey Resources,
but I noticed that Hk2 beans, defined in dropwizard bundles, which I cannot rewrite, are not
visible by Guice and it fails to inject dependencies defined in Hk2.
Guice doesn't see beans defined in Hk2 bundles and Guice creates new uninitialized beans by default.
I disabled this behavior with requireExplicitBindings.
I experimented with HK2IntoGuiceBridge, but its matcher is not invoked for beans I am interested in.
ConfiguredBundleX is located in external artifact.
I tried to copy and translate bean definitions from bundles and stuck with jersey bean Provider<ContainerRequest>, I have no idea where it comes from.
public class ConfiguredBundleX implements ConfiguredBundle<MyAppConf> {
public void run(T configuration, Environment environment) throws Exception {
environment.jersey().register(new AbstractBinder() {
protected void configure() {
this.bind(new MyHk2Bean()).to(MyHk2Bean.class);
}
});
}
}
public class DependsOnHk2Bean { #Inject public DependsOnHk2Bean(MyHk2Bean b) {} }
public class MainModule extends AbstractModule {
private final ServiceLocator locator;
protected void configure() {
binder().requireExplicitBindings();
install(new HK2IntoGuiceBridge(locator));
bind(DependsOnHk2Bean.class);
}
public class GuiceFeature implements Feature {
public boolean configure(FeatureContext context) {
ServiceLocator locator = ServiceLocatorProvider.getServiceLocator(context);
GuiceBridge.getGuiceBridge().initializeGuiceBridge(locator);
Injector injector = Guice.createInjector(
new HK2IntoGuiceBridge(locator),
new MainModule(locator));
GuiceIntoHK2Bridge guiceBridge = locator.getService(GuiceIntoHK2Bridge.class);
guiceBridge.bridgeGuiceInjector(injector);
return true;
}
}
// ...
public void initialize(Bootstrap<X> bootstrap) {
bootstrap.addBundle(new ConfiguredBundleX());
}
public void run(X config, Environment env) {
env.jersey().register(new GuiceFeature());
}
Unfortunately in Guice beans you have to use #HK2Inject rather than #Inject in order to inject hk2 beans into Guice. So in your code above you would do:
public class DependsOnHk2Bean { #HK2Inject public DependsOnHk2Bean(MyHk2Bean b) {} }
This is because of limitation in guice (it may be fixed by now) such that #Inject behavior could not overwritten
I have not tried the above code myself so I'm not sure it'll work, but that was the deal back when the bridge was written...
See HK2Inject and injecting-hk2-services-into-guice-services
After digging Guice and HK2ToGuiceTypeListenerImpl I figured out that there is bindListener to kind of intercept missing bindings and pull them from somewhere. #HKInject code is there, but I noticed that the listener is not called for some bean including the bean I was interested in. Yes HKInject doesn't support constructor injection (4.2.1 version)
So I decided to manually import HK beans and bind them in Guice.
Dropwizard terminology is horrible there are methods get context something, get admin context is totally something different and beans must be get with getService method!
#RequiredArgsConstructor
public class HkModule extends AbstractModule {
private final ServiceLocator locator;
#Override
protected void configure() {
binder().requireExplicitBindings();
Provider<Bar> barProvider = locator.getService(
new TypeLiteral<Provider<Bar>>(){}.getType());
bind(Bar.class).toProvider(barProvider);
bind(Foo.class).toInstance(locator.getService(Foo.class));
}
}

Java Spring-boot - How to use #Autowired with #ServerEndpoint?

I know there are lot of questions on this topic. I have read the spring boot doc and all of the solutions here. According spring boot doc, #ServerEndpoint is a Javax annotation and #Autowired components are spring-boot managed. These two cannot be used together. The solution to this would be to add SpringConfigurator as configurator of the ServerEndpoint. When I tried this I do get the following error:
Failed to find the root WebApplicationContext. Was ContextLoaderListener not used?
There is no example in the spring-boot websocket page to use ContextLoaderListener. How can use ContextLoaderListener so that components can be injected into #ServerEndpoint annotated controllers?
The following is my code.
Websocket controller
#ServerEndpoint(value = "/call-stream", configurator = SpringConfigurator.class)
public class CallStreamWebSocketController
{
#Autowired
private IntelligentResponseService responseServiceFacade;
// Other methods
}
Websocket configurations
#Configuration
public class WebSocketConfiguration
{
#Bean
public CallStreamWebSocketController callStreamWebSocketController()
{
return new CallStreamWebSocketController();
}
#Bean
public ServerEndpointExporter serverEndpointExporter()
{
return new ServerEndpointExporter();
}
}
Edit:
This has been tagged as a duplicate of this question. I have tried the solution specified in the answers. The solution is to add SpringConfigurator as configurator of the #ServerEndpoint. After adding this I still do get the error mentioned in the details.
After some research I found a way to force spring-boot to inject a component into an externally managed/instantiated class.
1) Add a generic method to your class extending ApplicationContextAware to return a bean.
#Component
public class SpringContext implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
SpringContext.context = context;
}
public ApplicationContext getApplicationContext() {
return context;
}
// Generic method to return a beanClass
public static <T> T getBean(Class<T> beanClass)
{
return context.getBean(beanClass);
}
}
2) Use this method to initialize the class object you want to be injected
private IntelligentResponseService responseServiceFacade = SpringContext.getBean(IntelligentResponseService.class);
So after the above changes my websocket controller would look like this
#ServerEndpoint(value = "/call-stream", configurator = SpringConfigurator.class)
public class CallStreamWebSocketController
{
private IntelligentResponseService responseServiceFacade = SpringContext.getBean(IntelligentResponseService.class);
// Other methods
}

Java Guice DI error: UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl

I have a simple REST API project using Jersey 2.x. I tried using Google Guice to inject my dependencies, but it doesn't seem to work. I get this error:
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=AccountService,parent=AccountsResource,qualifiers={},position=0,optional=false,self=false,unqualified=null,1658198405)
I have this simple resource class
#Path("/accounts")
#Produces(MediaType.APPLICATION_JSON)
public class AccountsResource {
private final AccountService accountService;
#Inject
public AccountsResource(AccountService accountService) {
this.accountService = accountService;
}
#GET
#Path("test")
public String test() {
return this.accountService.test();
}
I want to inject this service into my resource class
public class AccountService {
public AccountService() {}
public String test() {
return "test";
}
}
So, following Guice's guide, I created this module class
import com.google.inject.*;
public class AccountsResourceModule extends AbstractModule {
#Override
protected void configure() {
bind(AccountService.class);
}
}
Finally, I added the injector in my main method
public class TradingServer implements Runnable {
private static final int PORT = 8181;
public static void main(String[] args) {
Injector injector = Guice.createInjector(new AccountsResourceModule());
AccountsResource accountsResource = injector.getInstance(AccountsResource.class);
new TradingServer().run();
}
public void run() {
Server server = new Server(PORT);
ServletContextHandler contextHandler = new ServletContextHandler(server, "/");
ResourceConfig packageConfig = new ResourceConfig().packages("ca.ulaval.glo4002.trading");
ServletContainer container = new ServletContainer(packageConfig);
ServletHolder servletHolder = new ServletHolder(container);
contextHandler.addServlet(servletHolder, "/*");
try {
server.start();
server.join();
} catch (Exception e) {
e.printStackTrace();
} finally {
server.destroy();
}
}
}
When I call my server, I get the error mentioned above. It seems like the dependency injection didn't work. Please help
So Jersey knows nothing about Guice. It already uses it's own DI framework, HK2. There are a couple things you can do. You can either tie Guice together with HK2 so that HK2 can find services that are bound inside Guice, or another way is to just bind your resource classes inside Guice and and register instances of those resources with Jersey.
Tie Guice with HK2
To tie Guice with HK2, you need to use the Guice HK2 Bridge. First you need to add the following dependency
<dependency>
<groupId>org.glassfish.hk2</groupId>
<artifactId>guice-bridge</artifactId>
<version>${hk2.version}</version>
</dependency>
To get the hk2.version look at your Jersey dependencies (you can run mvn dependency:tree and see what version of HK2 Jersey is pulling in). You want to make sure you are using the exact same version.
Next thing you need to do is to programmatically link the two systems. One way to do this is inside a Feature.
public class GuiceFeature implements Feature {
#Override
public boolean configure(FeatureContext context) {
// This is the way in Jersey 2.26+ to get the ServiceLocator.
// In earlier versions, use
// ServiceLocatorProvider.getServiceLocator(context);
ServiceLocator locator = InjectionManagerProvider.getInjectionManager(context)
.getInstance(ServiceLocator.class);
Injector injector = Guice.createInjector(new AccountResourceModule());
GuiceBridge.getGuiceBridge().initializeGuiceBridge(locator);
GuiceIntoHK2Bridge guiceBridge = locator.getService(GuiceIntoHK2Bridge.class);
guiceBridge.bridgeGuiceInjector(injector);
return true;
}
}
Then just register the feature with Jersey.
ResourceConfig packageConfig = new ResourceConfig()
.packages("ca.ulaval.glo4002.trading")
.register(GuiceFeature.class);
And that's it. It should work, as I have tested.
Bind resources with Guice
With the above configuration, Jersey will be creating instances of your resource classes (#Path annotated classes). The reason we need the bridge is that Jersey is tightly coupled with HK2, so when we inject our resources classes, when creating the instance, Jersey will call HK2 to try to find all the dependencies for the resource.
In this case though, we will not rely on Jersey to create the instance of the resource. We will bind the resource to Guice and let Guice create the instance when we request it. We will use that instance to register with Jersey.
First bind the resource
public class AccountResourceModule extends AbstractModule {
#Override
protected void configure() {
bind(AccountService.class);
bind(AccountResource.class);
}
}
Also make sure that the #Inject annotation in the resource class is com.google.inject.Inject.
Get instance of resource and register it
Injector injector = Guice.createInjector(new AccountResourceModule());
AccountResource accountResource = injector.getInstance(AccountResource.class);
ResourceConfig config = new ResourceConfig()
.register(accountResource);
You probably have to figure out a cleaner way to do this as you don't want to have to do this for every resource you have. But this is the gist if what you need to do.
Update
So here's a quick implementation to clean up the second solution. What we can do is scan a package recursively to get all the #Path annotated classes and then bind them in Guice and register them with Jersey.
From this SO post, we can use the Reflections library to easily get all the classes. Just add the following dependency
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.11</version>
</dependency>
Then make a little helper classes
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.Path;
import org.reflections.Reflections;
public class ResourceClassHelper {
private static Set<Class<?>> resourceClasses;
public static Set<Class<?>> getResourceClasses() {
if (resourceClasses != null) {
return resourceClasses;
}
// the package to scan for #Path classes "com.example"
Reflections reflections = new Reflections("com.example");
resourceClasses = reflections.getTypesAnnotatedWith(Path.class);
resourceClasses = Collections.unmodifiableSet(resourceClasses);
return resourceClasses;
}
}
Then in your Guice module
public class AccountResourceModule extends AbstractModule {
#Override
protected void configure() {
bind(AccountService.class);
ResourceClassHelper.getResourceClasses().forEach(this::bind);
}
}
And your resource registration
Injector injector = Guice.createInjector(new AccountResourceModule());
ResourceConfig config = new ResourceConfig();
ResourceClassHelper.getResourceClasses()
.forEach(cls -> config.register(injector.getInstance(cls)));

Jersey : use #Inject in ResourceConfig

I've a Configuration.java singleton class (with many properties loaded by a file) that I would like to inject in some classes in my Application.
So, I bind my injection in the ResourceConfig class with an AbstractBinder.
Now, I need to use this Configuration class in this ResourceConfig.
For example, in my Configuration class there is a property named "packages", that I have to use in ResourceConfig class in order to register package.
The issue is that the injection is not starting in the ResourceConfig class.
class Configuration {
//many properties
String packages = "";
}
class MyResourceConfig extends ResourceConfig {
#Inject
Configuration configuration;
MyResourceConfig() {
...
register(MyBinder.class); //with many injection
...
packages(configuration.packages);
}
}
So could you please advice me how to have this lifecycle ? (maybe I have to use another jersey class ?)
I realize it is a sort of a workaround, but you can achieve it by using static access to the CDI singleton object.
Something like:
#ApplicationPath("/rest")
public class RestApplication extends ResourceConfig {
public RestApplication() {
super();
super.packages(true, "com.package.name");
register(JacksonFeature.class);
register(JacksonObjectMapperProvider.class);
ModelConverters.getInstance().addConverter(
new CustomObjectMapperModelResolver(getObjectMapperProvider()));
// get the instance here ^
OpenApiResource openApiResource = new OpenApiResource();
register(openApiResource);
AcceptHeaderOpenApiResource ahoar = new AcceptHeaderOpenApiResource();
register(ahoar);
}
private static JacksonObjectMapperProvider getObjectMapperProvider() {
return CDI.current().select(JacksonObjectMapperProvider.class).get();
}
}

How to pass parameters to REST resource using Jersey 2.5

I have a Java server which serves my clients (Not application server).
Now I'm interested to add REST support. I've initialized a Jetty server and created few REST resources.
My question is: How can I pass parameters at the creation of the REST resources?
Normally I would prefer in the constructor of each resource, but I don't control it.
I understand there is a way to inject dependencies. How to do it using Jersey 2.5??
Thank you!
Define your Application
public class MyApplication extends ResourceConfig {
public MyApplication() {
register(new FacadeBinder());
register(JacksonFeature.class);
register(MyEndpoint.class);
}
Configure injection
public class FacadeBinder extends AbstractBinder {
#Override
protected void configure() {
bind(MyManager.class).to(MyManager.class);
}
}
Inject configured classes in your endpoint
#Path("/jersey")
public class MyEndpoint {
#Inject
MyManager myManager;
...
}
I'm not sure to understand what do you mean with dependencies.
You should check this: https://jersey.java.net/documentation/latest/user-guide.html#d0e1810
Another option besides using dependency injection is to instantiate and register the REST endpoint yourself. Jersey allows you to do this in a very similar fashion as dependency injection as shown in Dymtro's example. Borrowing liberally from Dymtro, define your endpoint:
#Path("/jersey")
public class MyEndpoint {
private MyManager myManager;
public MyEndpoint(MyManager myManager) {
this.myManager = myManager;
}
....
}
Define your application:
public class MyApplication extends ResourceConfig {
public MyApplication(MyManager myManager) {
register(JacksonFeature.class);
register(new MyEndpoint(myManager));
....
}
}

Categories

Resources