How to use guice-servlet with Jersey 2.0? - guice-servlet

Is there any sample code demonstrating how to use guice-servlet with Jersey 2.0?

https://github.com/Squarespace/jersey2-guice seems to be the first genuine Guice integration for Jersey 2 but it requires version 2.11+.
NOTE: I haven't tested this, but the idea is sound.

Yes, I've adapted an example and it's available here - https://github.com/piersy/jersey2-guice-example-with-test
I've updated the example code now, its got a test using jetty and another using tomcat.

There is a page at HK2 official about correct guice implementation: https://javaee.github.io/hk2/guice-bridge.html
You should create your Injector something like this:
public class GuiceConfig extends ResourceConfig {
#Inject
public GuiceConfig(ServiceLocator serviceLocator) {
this();
GuiceBridge.getGuiceBridge().initializeGuiceBridge(serviceLocator);
GuiceIntoHK2Bridge guiceBridge = serviceLocator.getService(GuiceIntoHK2Bridge.class);
guiceBridge.bridgeGuiceInjector(GuiceListener.createBiDirectionalGuiceBridge(serviceLocator));
}
public GuiceConfig() {
packages(Injections.packages);
addProperties(Injections.propertiesMap);
}
}
And code from the doc should be upgraded like:
#WebListener
public class GuiceListener extends GuiceServletContextListener {
#Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
Locale.setDefault(Locale.ENGLISH);
super.contextInitialized(servletContextEvent);
}
public static volatile Injector injector = null;
#Override
protected Injector getInjector() {
return injector;
}
#SuppressWarnings("unchecked")
private static Module getModule() {
return binder -> {
Injections.singletonInterfaces.forEach((i, c) -> binder.bind(i).to(c).in(Scopes.SINGLETON));
Injections.singletonClasses.forEach(c -> binder.bind(c).in(Scopes.SINGLETON));
};
}
static synchronized Injector createBiDirectionalGuiceBridge(ServiceLocator serviceLocator) {
return GuiceListener.injector = createBiDirectionalGuiceBridge(serviceLocator, getModule());
}
}
Using the maven dependency at your pom.xml
<dependency>
<groupId>org.glassfish.hk2</groupId>
<artifactId>guice-bridge</artifactId>
<version>2.3.0</version>
</dependency>
https://github.com/phxql/jersey2-guice doesn't work with jersey 2.22 and guice 4.0.

This is a minimum working PoC which wires Jersey 2 and Guice together:
https://github.com/phxql/jersey2-guice

I've already done in this sample:
https://github.com/jbescos/tododev
You have to register the class https://github.com/jbescos/tododev/blob/master/jersey2-guice/src/main/java/es/tododev/rest/ApplyGuiceContextFilter.java in your ResourceConfig, and the guice injector binded in an AbstractModule.
#Provider
#PreMatching
public class ApplyGuiceContextFilter implements ContainerRequestFilter, ContainerResponseFilter {
#Inject
public ApplyGuiceContextFilter(ServiceLocator serviceLocator, Injector injector) {
GuiceBridge.getGuiceBridge().initializeGuiceBridge(serviceLocator);
GuiceIntoHK2Bridge guiceBridge = serviceLocator.getService(GuiceIntoHK2Bridge.class);
guiceBridge.bridgeGuiceInjector(injector);
}
#Override
public void filter(final ContainerRequestContext requestContext) throws IOException {
}
#Override
public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext) throws IOException {}
}
This is the ResouceConfig:
public class RestConfig extends ResourceConfig {
#Inject
public RestConfig() {
this(Guice.createInjector(new Module(){
#Override
public void configure(Binder arg0) {
// TODO Auto-generated method stub
}
}));
}
// Test
public RestConfig(Injector injector) {
packages(ResourceSample.class.getPackage().getName());
register(ApplyGuiceContextFilter.class);
register(new LoggingFilter(Logger.getLogger(LoggingFilter.class.getName()), true));
property(ServerProperties.TRACING, "ALL");
register(new RestBinder(injector));
}
private static class RestBinder extends AbstractBinder{
private final Injector injector;
private RestBinder(Injector injector){
this.injector = injector;
}
#Override
protected void configure() {
bind(injector).to(Injector.class);
}
}
}

GWizard includes a module that gives you out-of-the-box integration between Jersey2 and Guice. Here's an example of a complete JAX-RS service:
public class Main {
#Path("/hello")
public static class HelloResource {
#GET
public String hello() {
return "hello, world";
}
}
public static class MyModule extends AbstractModule {
#Override
protected void configure() {
bind(HelloResource.class);
}
}
public static void main(String[] args) throws Exception {
Guice.createInjector(new MyModule(), new JerseyModule()).getInstance(WebServer.class).startJoin();
}
}
Note that this is based on the Squarespace jersey2-guice adapter, which may not function properly with future point releases of Jersey. GWizard also offers a RESTEasy JAX-RS module, which is preferred.
Here is a blog entry about this that might help: http://blorn.com/post/107397841765/guice-and-jersey-2-the-easy-way

For those interested, there is a sample of guice/jersey integration available at https://github.com/mycom-int/jersey-guice-aop.

Here is an example using Embedded Jetty (it should probably work for Jetty server too)
jetty-jersey-HK2-Guice-boilerplate
If you are planning to use Guice for your application, all Guice components injected into Jersey need to be declared as a binding in the Guice config.
If you don't want to declare every binding in Guice config, there is an adapter here:
guice-bridge-jit-injector

Related

Comparison of Guice and(move to) Spring

Could someone give me advice, please, how to re-write some method using simple Spring (w/o Boot)?
Here I have some code methods:
1. createInjector
private Injector injector;
someMethod(){
injector = Guice.createInjector(new ExampleClass1(), new ExampleClass2());}
2 setModules(Modules.override
setModules(Modules.override(new ExampleClass3()).with(new ExampleClass4()));
//////////////////////////////////////////////////////////////////
public static void setModules(Module... modules) {
initInjector(modules);
}
private static void initInjector(Module... modules) {
injector = Guice.createInjector(modules);
}
}
Taking the risk that my answer is too general.
Roughly saying you can think Guice modules as equivalent a configuration class with #Configuration annotation, that contains #Bean etc.
The Guice injector can be considered as equivalent to the Spring ApplicationContext.
So for example if we have two configuration files:
#Configuration
public class ConfigA {
#Bean
ExampleClass1 exampleClass1(){
return new ExampleClass1();
}
#Bean
ExampleClass2 exampleClass2(){
return new ExampleClass2();
}
}
#Configuration
public class ConfigB {
#Bean
ExampleClass1 exampleClass1(){
return new ExampleClass1();
}
#Bean
ExampleClass3 exampleClass2(){
return new ExampleClass3();
}
}
And Services ExampleClass4 that you want as alternative of ExampleClass3.
You may use the #Primary annotation
public class ExampleClass4 extends ExampleClass3 {
#Override
public String toString() {
return "ExampleClass4{}";
}
}
#Configuration
public class ConfigC {
#Bean
#Primary
ExampleClass3 exampleClass3(){
return new ExampleClass4();
}
}
So rewriting the app to Spring (core 5.2, not Spring boot) will be:
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext ap = initAppContext();
overrideBinding(ap);
System.out.println(ap.getBean(ExampleClass3.class));
//prints ExampleClass4{}
}
private static AnnotationConfigApplicationContext initAppContext() {
AnnotationConfigApplicationContext ap = new AnnotationConfigApplicationContext();
ap.register(ConfigA.class, ConfigB.class);
return ap;
}
private static void overrideBinding(AnnotationConfigApplicationContext ap) {
ap.register(ConfigC.class);
ap.refresh();
}
}
This technic of overriding a binding will work only because ExampleClass3 wasn't defined as primary, if it doesn't that would not work and you need to consider a different approach.
For more information:
https://www.baeldung.com/spring-application-context
https://docs.spring.io/spring-javaconfig/docs/1.0.0.m3/reference/html/modularizing-configurations.html
Override bean definition in java config

How to access properties in BeanFactoryPostProcessor with spring boot 1.5.x

I am trying to get properties from application.yml in BeanFactoryPostProcessor with spring boot 1.5.x:
The application.yml:
prong:
nfcloan:
jackson:
json-sub-types-package:
- com.shuweicloud.starter.acc.dto.request
#ConfigurationProperties(prefix = "prong.nfcloan.jackson")
public class JacksonProperties {
private List<String> jsonSubTypesPackage;
public List<String> getJsonSubTypesPackage() {
return jsonSubTypesPackage;
}
public void setJsonSubTypesPackage(List<String> jsonSubTypesPackage) {
this.jsonSubTypesPackage = jsonSubTypesPackage;
}
}
#Component
public class AccBeanFactoryPostProcessor implements BeanFactoryPostProcessor{
#Autowired
private JacksonProperties jacksonProperties;
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
List<String> packages = jacksonProperties.getJsonSubTypesPackage();
// do something
}
}
The main class:
#SpringBootApplication
#EnableConfigurationProperties({JacksonProperties.class})
public class AccountingApplication {
public static void main(String[] args) {
SpringApplication.run(AccountingApplication.class, args);
}
}
But the packages variable is null. How to solve it?
I found a solution:
#Component
public class AccBeanFactoryPostProcessor implements BeanFactoryPostProcessor, EnvironmentAware {
private Environment environment;
#Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
#SuppressWarnings("unchecked")
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
List<String> packages = environment.getProperty("prong.nfcloan.jackson.json-sub-types-package", List.class);
// do something
}
}
Spring boot internally uses Binder APIs to "map" the resolved properties into the #ConfigurationProperties beans.
Indeed, this resolution happens during the spring boot startup process after the BeanFactoryPostProcessors get created.
Now your solution will clearly work, because you kind of "bypass" this resolution.
However if you want to still have the Configuration as an instance of JacksonProperties (might be relevant if you have a lot of properties to resolve, or in general prefer to work more in a more spring-ish manner), you can use this binder API:
// inside the "postProcessBeanFactory" method, using the injected environment
BindResult<ExampleProperties> bindResult = Binder.get(environment)
.bind("prong.nfcloan.jackson", JacksonProperties.class);
JacksonProperties properties = bindResult.get();

Springs "#MessagingGateway" annotation on external interfaces

I am about to migrate a project created with an old version of Spring (using XML config) to Spring Boot (using Java config).
The project is using Spring Integration for communication via JMS and AMQP. As far as I understood, I have to replace the
<int:gateway id="someID" service-interface="MyMessageGateway"
default-request-channel="myRequestChannel"
default-reply-channel="myResponseChannel"
default-reply-timeout="20000" />
with
#MessagingGateway(name="someID", defaultRequestChannel = "myRequestChannel",
defaultReplyChannel = "myResponseChannel", defaultReplyTimeout = "20000")
public interface MyMessageGateway{ ...... }
My problem is, that the interface, that is in use now, is placed in a library I can't access.
How can I define this Interface as my MessagingGateway?
Thanks in advance!
I have just tested this trick:
interface IControlBusGateway {
void send(String command);
}
#MessagingGateway(defaultRequestChannel = "controlBus")
interface ControlBusGateway extends IControlBusGateway {
}
...
#Autowired
private IControlBusGateway controlBus;
...
try {
this.bridgeFlow2Input.send(message);
fail("Expected MessageDispatchingException");
}
catch (Exception e) {
assertThat(e, instanceOf(MessageDeliveryException.class));
assertThat(e.getCause(), instanceOf(MessageDispatchingException.class));
assertThat(e.getMessage(), containsString("Dispatcher has no subscribers"));
}
this.controlBus.send("#bridge.start()");
this.bridgeFlow2Input.send(message);
reply = this.bridgeFlow2Output.receive(5000);
assertNotNull(reply);
In other words you can just extends that external interface to your local one. The GatewayProxyFactoryBean will do the proxy magic for you underneath.
Also we have this JIRA for similar use-case: https://jira.spring.io/browse/INT-4134
Use a GatewayProxyFactoryBean; here's a simple example:
#SpringBootApplication
public class So41162166Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(So41162166Application.class, args);
context.getBean(NoAnnotationsAllowed.class).foo("foo");
context.close();
}
#Bean
public GatewayProxyFactoryBean gateway() {
GatewayProxyFactoryBean gateway = new GatewayProxyFactoryBean(NoAnnotationsAllowed.class);
gateway.setDefaultRequestChannel(channel());
return gateway;
}
#Bean
public MessageChannel channel() {
return new DirectChannel();
}
#ServiceActivator(inputChannel = "channel")
public void out(String foo) {
System.out.println(foo);
}
public static interface NoAnnotationsAllowed {
public void foo(String out);
}
}

WebSockets JSR 356 Spring integration #ServerEndpoint

Problem: #Autowired beans in #ServerEndpoint class are null
How can I make sure that this WebSocketController class below will be injected with beans, that is how can I make it managed by Spring? I can connect to the websocket so it works but gameService is always null inside the WebSocketController class instance, so I think that it is created by tomcat somehow and not Spring.
I'm using Spring boot. I just need to figure out how to inject beans into this websocket controller class.
WebSocketController class
#Component
#ServerEndpoint("/sock")
public class WebSocketController {
#Autowired
private GameService gameService;
private static Set<Session> clients = Collections.synchronizedSet(new HashSet<Session>());
#OnMessage
public void handleMessage(Session session, String message) throws IOException {
session.getBasicRemote().sendText(
"Reversed: " + new StringBuilder(message).reverse());
}
#OnOpen
public void onOpen(Session session) {
clients.add(session);
System.out.println("New client #"+session.getId());
if (gameService == null) System.out.println("game service null");
}
#OnClose
public void onClose(Session session) {
clients.remove(session);
System.out.println("Client disconnected #" + session.getId());
}
}
GameService interface and implementation
public interface GameService {
List<Character> getCharacters();
}
#Service
public class GameServiceMockImpl implements GameService {
#Override
public List<Character> getCharacters() {
List<Character> list = new ArrayList<>();
list.add(new Character("aaa","1.png",100));
list.add(new Character("aaa","2.jpg",100));
list.add(new Character("aaa","3.jpg",100));
return list;
}
}
Application class
#SpringBootApplication
public class App {
public static void main(String args[]){
SpringApplication.run(App.class,args);
}
#Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
EDIT:
Using Spring 4 WebSockets doesn't work at all, I can't even connect via a browser.
#Configuration
#EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler(), "/myHandler");
}
#Bean
public WebSocketHandler myHandler() {
return new MyHandler();
}
}
public class MyHandler extends TextWebSocketHandler {
#Override
public void handleTextMessage(WebSocketSession session, TextMessage message) {
System.out.println(message.getPayload());
}
}
You are trying to integrate Spring and Java WebSocket API. A class annotated by #Component is registered to a spring bean and its instance is managed by spring but if a class is annotated by #ServerEndpoint it is registered to a server-side WebSocket endpoint and every time the corresponding endpoint's WebSocket is connected to the server, its instance is created and managed by JWA implementation. We you can't use both annotations together.
Either you can use CDI injection(your server should also support)
#ServerEndpoint("/sock")
public class WebSocketController {
#Inject
private GameService gameService;
Or have a look on this doc, Spring 4 has support for WebSocket
Maybe this article can help:
https://spring.io/blog/2013/05/23/spring-framework-4-0-m1-websocket-support
You can use dependency (with spring version > 4)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>${spring.version}</version>
</dependency>
And then simply
#ServerEndpoint(value = "/echo", configurator = SpringConfigurator.class)
public class WebSocketEndpoint {
#Inject
private BroadcastService broadcaster;

Using test application.properties file with CamelSpringTestSupport in Spring Boot

Prerequisites
Apache Tomcat 7
Spring 4.1.5.RELEASE
Spring Boot 1.2.2.RELEASE
Apache Camel 2.15.1
Problem
I am Using Spring Boot with a configuration class which is also used by EndpointSetup.
#SpringBootApplication
#Import({MyConfiguration.class, EndpointSetup.class})
public class MyFatJarRouter extends FatJarRouter { ... }
#Configuration
#ConfigurationProperties(prefix = "camel.route", ignoreUnknownFields = false)
public class MyConfiguration {
private List<String> brokerUrl = new ArrayList<>();
public List<String> getBrokerUrl() {return brokerUrl;}
public void setBrokerUrl(List<String> brokerUrl) {this.brokerUrl = brokerUrl;}
}
In production properties will be read from conf/application.properties by default.
I want to test my routes via CamelSpringTestSupport
So I have tried following:
I have placed a application.properties under test/resources/config/application.properties (--> in classpath of test)
then wrote following:
public class MyJmsTest extends CamelSpringTestSupport {
#Override
protected AbstractApplicationContext createApplicationContext() {
return new AnnotationConfigApplicationContext(MyFatJarRouter.class);
}
#Test
public void myTest() throws Exception {
...
}
}
In the example above the configuration is not read from the application.properties placed in test folder.
How can I read a test specific config file in my CamelSpringTestSupport Unit-Test?
I may be little late in answering, but there is a better way than hacking endpoints. The following solution uses toD introduced in Camel 2.16. I wrote a custom component "github" (there's an official one as well), and the following is how I test it. Note that I'm not using a single Camel proprietary annotation. To inject properties, I can either use the properties attribute in #SpringBootTest, or any of the other standard techniques available in Spring Boot.
Note that I'm using $simple{...} to avoid clash with Spring property resolution.
<rant>
And yes, Camel documentation sucks! They write it like release notes, with a section dedicated to each release, and don't seem to update the doc to keep up with the latest versions (the following technique is not documented). Imagine going to a restaurant and asking for the special, only to be told by the server about the special for the day before, and the week before, and so on. How about versioning the doc instead?
</rant>
#RunWith(CamelSpringBootRunner.class)
#SpringBootTest
#DirtiesContext(classMode = AFTER_EACH_TEST_METHOD)
public class GitHubRouteTest {
#Autowired
private CamelContext camelContext;
#Autowired
private ProducerTemplate template;
#Autowired
private GitHubClient gitHubClient;
#Test
public void testGitHubClientInvoked() throws InterruptedException {
template.sendBodyAndHeader("direct:start", "whatever",
"endpoint", "commits/test/test?username=test&password=test");
verify(gitHubClient).getCommitsForARepo(eq("test"), eq("master"), eq("test"), eq(20));
}
#SpringBootApplication
public static class TestApplication {
public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(TestApplication.class)
.web(false)
.run(args);
}
#Bean
public RouteBuilder testRoute() {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("direct:start")
.toD("github:$simple{in.header.endpoint}");
}
};
}
#Bean
public GitHubClient mockGitHubClient() {
GitHubClient mock = Mockito.mock(GitHubClient.class);
return mock;
}
}
}
I solved it by using standard spring unit-tests like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#ActiveProfiles("test") // Load applicaton-test.properties in test/resources/config/application-test.properties
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) // cleanup spring context because jms broker does not exit properly
public class MyJmsTest {
private static final String MOCK_MY_ENDPOINT = "mock:myEndpoint";
#Autowired
CamelContext context;
#Autowired
ApplicationContext applicationContext;
#Autowired
ProducerTemplate producerTemplate;
#Before
public void configureMocks() throws Exception {
context.getRouteDefinition("MyRoute")
.adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
weaveByToString(".*myEndPointId.*")
.replace()
.to(MOCK_MY_ENDPOINT);
}
});
final MockEndpoint endpoint = context.getEndpoint(MOCK_MY_ENDPOINT, MockEndpoint.class);
endpoint.whenAnyExchangeReceived(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
InputStream inStream = getClass().getClassLoader().getResourceAsStream("xml/my.xml");
String in = context.getTypeConverter().convertTo(String.class, inStream);
exchange.getIn().setBody(in);
}
});
}
#Test
public void synchronousCallBasic_1() throws Exception {
final MyConfiguration MyConfiguration = applicationContext.getBean(MyConfiguration.class);
final String myMessageBody =
context.getTypeConverter().convertTo(String.class, getClass().getClassLoader()
.getResourceAsStream("xml/0010_example.xml"));
final Object myResult = producerTemplate.requestBody(MyConfiguration.getActiveMqSynchronousEndpointUri(), myMessageBody);
assertThat(myResult, notNullValue());
assertThat((String)myResult, is("<example>1</example>"));
}
}
I solved this issue, with a lot of annotation which I found here, and now the test properties are correctly injected:
#RunWith(CamelSpringBootRunner.class)
#SpringBootTest
#ActiveProfiles("test")
#EnableAutoConfiguration
#ComponentScan
#ContextConfiguration()
public class MessageDeliveryTest{
}
Also, the test properties file needs to be named application-{env}.properties, where "env" is the profile used here. For eg. for test the properties file should be application-test.properties

Resources