I want to use Guice + Jersey 2.0 on Grizzly. According to this How to use guice-servlet with Jersey 2.0? discussion there is no direct Guice integration for Jersey2 at present but it can be achieved using HK2 as a bridge. I also checked the sample project in Github https://github.com/piersy/jersey2-guice-example-with-test . This project is implemented using Jetty.
But my problem is to implement it in Grizzly. On Jetty it is used like this
#Inject
public MyApplication(ServiceLocator serviceLocator) {
// Set package to look for resources in
packages("example.jersey");
System.out.println("Registering injectables...");
GuiceBridge.getGuiceBridge().initializeGuiceBridge(serviceLocator);
GuiceIntoHK2Bridge guiceBridge = serviceLocator.getService(GuiceIntoHK2Bridge.class);
guiceBridge.bridgeGuiceInjector(Main.injector);
}
My problem on grizzly is , how to get this serviceLocator object?
Thank you.
I have created the sample here
https://github.com/oleksiys/samples/tree/master/jersey2-guice-example-with-test
The Grizzly initialization code looks like this:
final URI uri = UriBuilder.fromUri("http://127.0.0.1/")
.port(8080).build();
// Create HttpServer
final HttpServer serverLocal = GrizzlyHttpServerFactory.createHttpServer(uri, false);
// Create Web application context
final WebappContext context = new WebappContext("Guice Webapp sample", "");
context.addListener(example.jersey.Main.class);
// Initialize and register Jersey ServletContainer
final ServletRegistration servletRegistration =
context.addServlet("ServletContainer", ServletContainer.class);
servletRegistration.addMapping("/*");
servletRegistration.setInitParameter("javax.ws.rs.Application",
"example.jersey.MyApplication");
// Initialize and register GuiceFilter
final FilterRegistration registration =
context.addFilter("GuiceFilter", GuiceFilter.class);
registration.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), "/*");
context.deploy(serverLocal);
serverLocal.start();
add dependecy
compile group: "org.glassfish.hk2", name: "guice-bridge", version: "2.4.0"
create feature
public class GuiceFeature implements Feature {
#Override
public boolean configure(FeatureContext context) {
ServiceLocator serviceLocator = ServiceLocatorProvider.getServiceLocator(context);
GuiceBridge.getGuiceBridge().initializeGuiceBridge(serviceLocator);
GuiceIntoHK2Bridge guiceBridge = serviceLocator.getService(GuiceIntoHK2Bridge.class);
Injector injector = Guice.createInjector(new AbstractModule() {
#Override
protected void configure() {
bind(YYY.class).to(ZZZ.class);
}
});
guiceBridge.bridgeGuiceInjector(injector);
return true;
}
}
register feature
ResourceConfig resourceConfig = new ResourceConfig();
resourceConfig.register(GuiceFeature.class);
Related
Problem with connection to Neo4j test container using Spring boot 2 and JUnit5
int test context. Container started successfully but spring.data.neo4j.uri property has a wrong default port:7687, I guess this URI must be the same when I call neo4jContainer.getBoltUrl().
Everything works fine in this case:
#Testcontainers
public class ExampleTest {
#Container
private static Neo4jContainer neo4jContainer = new Neo4jContainer()
.withAdminPassword(null); // Disable password
#Test
void testSomethingUsingBolt() {
// Retrieve the Bolt URL from the container
String boltUrl = neo4jContainer.getBoltUrl();
try (
Driver driver = GraphDatabase.driver(boltUrl, AuthTokens.none());
Session session = driver.session()
) {
long one = session.run("RETURN 1",
Collections.emptyMap()).next().get(0).asLong();
assertThat(one, is(1L));
} catch (Exception e) {
fail(e.getMessage());
}
}
}
But SessionFactory is not created for the application using autoconfiguration following to these recommendations - https://www.testcontainers.org/modules/databases/neo4j/
When I try to create own primary bean - SessionFactory in test context I get the message like this - "URI cannot be returned before the container is not loaded"
But Application runs and works perfect using autoconfiguration and neo4j started in a container, the same cannot be told about the test context
You cannot rely 100% on Spring Boot's auto configuration (for production) in this case because it will read the application.properties or use the default values for the connection.
To achieve what you want to, the key part is to create a custom (Neo4j-OGM) Configuration bean. The #DataNeo4jTest annotation is provided by the spring-boot-test-autoconfigure module.
#Testcontainers
#DataNeo4jTest
public class TestClass {
#TestConfiguration
static class Config {
#Bean
public org.neo4j.ogm.config.Configuration configuration() {
return new Configuration.Builder()
.uri(databaseServer.getBoltUrl())
.credentials("neo4j", databaseServer.getAdminPassword())
.build();
}
}
// your tests
}
For a broader explanation have a look at this blog post. Esp. the section Using with Neo4j-OGM and SDN.
I configure my JAXRS Server in Spring Boot like so:
JAXRSServerFactoryBean factoryBean = new JAXRSServerFactoryBean();
factoryBean.setBus(this.bus);
factoryBean.setFeatures(singletonList(swagger2Feature()));
factoryBean.setServiceBeans(Arrays.asList(blah(), blah2(), blah3()));
factoryBean.setAddress("/api/v1/"); // HERE
List<Object> providers = new ArrayList<>();
providers.add(new JacksonJaxbJsonProvider());
factoryBean.setProviders(providers);
BindingFactoryManager manager = factoryBean.getBus().getExtension(BindingFactoryManager.class);
JAXRSBindingFactory restFactory = new JAXRSBindingFactory();
restFactory.setBus(factoryBean.getBus());
manager.registerBindingFactory(JAXRSBindingFactory.JAXRS_BINDING_ID, restFactory);
return factoryBean.create();
However, the URLs always require /services in front, which is a nuisance (but not the end of the world). Is there any way I can remove /services and just get it deployed to the root context?
If you have not created your own CxfServlet bean you can set the path by setting cxf.path property in your application.properties file
cxf.path=/
Another way is to override ServletRegistrationBean.
#Bean
public ServletRegistrationBean cxfServletRegistration() {
String urlMapping = "/*";
ServletRegistrationBean registration = new ServletRegistrationBean(
new CXFServlet(), urlMapping);
registration.setLoadOnStartup(-1);
return registration;
}
I was trying to setup Feign to work with RibbonClient, something like MyService api = Feign.builder().client(RibbonClient.create()).target(MyService.class, "https://myAppProd");, where myAppProd is an application which I can see in Consul. Now, if I use Spring annotations for the Feign client (#FeignClient("myAppProd"), #RequestMapping), everything works as Spring Cloud module will take care of everything.
If I want to use Feign.builder() and #RequestLine, I get the error:
com.netflix.client.ClientException: Load balancer does not have available server for client: myAppProd.
My first initial thought was that Feign was built to work with Eureka and only Spring Cloud makes the integration with Consul, but I am unsure about this.
So, is there a way to make Feign work with Consul without Spring Cloud?
Thanks in advance.
In my opinion, it's not feign work with consul, its feign -> ribbon -> consul.
RibbonClient needs to find myAppProd's serverList from its LoadBalancer.
Without ServerList, error: 'does not have available server for client'.
This job has been done by SpringCloudConsul and SpringCloudRibbon project, of course you can write another adaptor, it's just some glue code. IMHO, you can import this spring dependency into your project, but use it in non-spring way . Demo code:
just write a new feign.ribbon.LBClientFactory, that generate LBClient with ConsulServerList(Spring's class).
public class ConsulLBFactory implements LBClientFactory {
private ConsulClient client;
private ConsulDiscoveryProperties properties;
public ConsulLBFactory(ConsulClient client, ConsulDiscoveryProperties consulDiscoveryProperties) {
this.client = client;
this.properties = consulDiscoveryProperties;
}
#Override
public LBClient create(String clientName) {
IClientConfig config =
ClientFactory.getNamedConfig(clientName, DisableAutoRetriesByDefaultClientConfig.class);
ConsulServerList consulServerList = new ConsulServerList(this.client, properties);
consulServerList.initWithNiwsConfig(config);
ZoneAwareLoadBalancer<ConsulServer> lb = new ZoneAwareLoadBalancer<>(config);
lb.setServersList(consulServerList.getInitialListOfServers());
lb.setServerListImpl(consulServerList);
return LBClient.create(lb, config);
}
}
and then use it in feign:
public class Demo {
public static void main(String[] args) {
ConsulLBFactory consulLBFactory = new ConsulLBFactory(
new ConsulClient(),
new ConsulDiscoveryProperties(new InetUtils(new InetUtilsProperties()))
);
RibbonClient ribbonClient = RibbonClient.builder()
.lbClientFactory(consulLBFactory)
.build();
GitHub github = Feign.builder()
.client(ribbonClient)
.decoder(new GsonDecoder())
.target(GitHub.class, "https://api.github.com");
List<Contributor> contributors = github.contributors("OpenFeign", "feign");
for (Contributor contributor : contributors) {
System.out.println(contributor.login + " (" + contributor.contributions + ")");
}
}
interface GitHub {
#RequestLine("GET /repos/{owner}/{repo}/contributors")
List<Contributor> contributors(#Param("owner") String owner, #Param("repo") String repo);
}
public static class Contributor {
String login;
int contributions;
}
}
you can find this demo code here, add api.github.com to your local consul before running this demo.
Can i run my CustomFilter extended with ContainerResponseFilter in jersey1.17.
I am using GrizzlyWebServer. Please suggest . Given below is my sample server code to add the filter.
GrizzlyWebServer webServer = new GrizzlyWebServer(.............);
....
....
ServletAdapter adapter3 = new ServletAdapter();
adapter3.addInitParameter("com.sun.jersey.config.property.packages", "com.motilink.server.services");
adapter3.setContextPath("/");
adapter3.setServletInstance(new ServletContainer());
adapter3.addContextParameter(ResourceConfig.PROPERTY_CONTAINER_RESPONSE_FILTERS, PoweredbyResponseFilter.class.getName());
webServer.addGrizzlyAdapter(adapter3, new String[]{"/"});
...
.....
MY Filter:
#FrontierResponse
#Provider
public class PoweredbyResponseFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
throws IOException {
System.out.println("hell");
responseContext.getHeaders().add("X-Powered-By", "Jersey :-)");
}
}
Resource Class:
#NameBinding
#Retention(value = RetentionPolicy.RUNTIME)
public #interface FrontierResponse {
}
#GET
#Produces("text/plain")
#Path("plain")
//#FrontierResponse
public String getMessage() {
System.out.println("hello world called");
return "Hello World";
}
and finally i call it from a browser
http:// localhost:4464/plain
Add the ResourceConfig.PROPERTY_CONTAINER_RESPONSE_FILTERS property as a init-param and not as a context-param:
...
adapter3.addInitParameter(ResourceConfig.PROPERTY_CONTAINER_RESPONSE_FILTERS, PoweredbyResponseFilter.class.getName());
...
EDIT 1
From your answer it seems that you're actually trying to use Jersey 1.x (1.17) runtime with implemented JAX-RS 2.0 providers (ContainerRequestContext and ContainerResponseContext have been introduced in JAX-RS 2.0 and Jersey 1.x doesn't know how to use them).
So my advice would be - drop all your Jersey 1.17 dependencies and replace them with Jersey 2.x dependencies. Take a look at our helloworld-webapp example (particularly at App class) to see how to create a Grizzly server instance with JAX-RS application.
Note that it is sufficient to add just ServerProperties.PROVIDER_PACKAGES property to init-params and your Resources and Providers (incl. response filters) will be scanned and registered in the application.
I want to search for some annotations in a Spring based web application, like #Entity. Therefore I need the same functionality like Spring involves when the server starts up and it looks for all classes that are annotated with #Component. In my case I don't create singleton's, it's just important for me to collect all those classes annotated with #Entity.
Is there any possibility to use existing Spring tools for that? I want to search exactly in the same namespace as Spring does for the #Component annotations.
Sure, look at parse() method in org.springframework.context.annotation.ComponentScanBeanDefinitionParser. This method is called when Spring encounters <context:component-scan/> in the XML configuration. Probably You can strip it a bit to better suit your needs, but it should serve as a comprehensive example.
The class You should be particularly interested in is org.springframework.context.annotation.ClassPathBeanDefinitionScanner. From JavaDoc:
Candidate classes are detected through configurable type filters. The default filters include classes that are annotated with Spring's #Component, #Repository, #Service, or #Controller stereotype.
BTW if you need less general solution, maybe your persistence provider has some API to fetch all entity classes?
Spring's built-in classpath scanning infrastructure (ClassPathBeanDefinitionScanner/ ComponentScanBeanDefinitionParser) is geared up for registering classes as BeanDefinitions within an Spring appcontext.
If you're just looking to obtain a list of classes annotated with a given annotation (rather than actually register them in Spring as bean definitions) take a look at the Google Reflections library.
Reflections allows you to scan your classpath using various filters, including an annotation filter.
Reflections reflections = new Reflections("my.project.prefix");
Set<Class<? extends SomeClassOrInterface>> subTypes = reflections.getSubTypesOf(SomeClassOrInterface.class);
Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(SomeAnnotation.class);
Spring based solution
Use spring AnnotationTypeFilter and pass Entity.class as annotationType
using ResourcePatternResolver load all resouces(.class) under given pacakage
Use SimpleMetadataReaderFactory to get MetadataReader
for each resource you can call match on AnnotationTypeFilter using MetadataReader
metadataReader.getAnnotationMetadata().getClassName() will provide FQN of class
usage
AnnotatedClassFinder entityScanner = new AnnotatedClassFinder(Entity.class);
entityScanner.setPackages(Arrays.asList("org.myapp.domain"));
Collection<Class<?>> entities = entityScanner.findMarkedClassOfType();
public class AnnotatedClassFinder {
private static final String CLASS_RESOURCE_PATTERN = "**/*.class";
private List<String> packages;
private final ResourceLoader resourceLoader = new DefaultResourceLoader();
private final ResourcePatternResolver resourcePatternResolver = ResourcePatternUtils
.getResourcePatternResolver(resourceLoader);
private final MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();
private final TypeFilter annotationFilter;
public AnnotatedClassFinder(final Class<? extends Annotation> annotationToScanFor) {
annotationFilter = new AnnotationTypeFilter(annotationToScanFor);
}
public Set<Class<?>> findMarkedClassOfType() {
if (packages == null) {
return new HashSet<Class<?>>();
}
final Set<Class<?>> annotatedClasses = new HashSet<Class<?>>();
try {
for (final String p : packages) {
final String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
+ ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(p)) + "/"
+ CLASS_RESOURCE_PATTERN;
final Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);
for (final Resource resource : resources) {
if (resource.isReadable()) {
final MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
if (annotationFilter.match(metadataReader, metadataReaderFactory)) {
annotatedClasses.add(Class.forName(metadataReader.getAnnotationMetadata().getClassName()));
}
}
}
}
return annotatedClasses;
} catch (final IOException ex) {
throw new RuntimeException("I/O failure during classpath scanning", ex);
} catch (final ClassNotFoundException ex) {
throw new RuntimeException("Class loading failure during classpath scanning", ex);
}
}
public void setPackages(final List<String> packages) {
this.packages = packages;
}
}