Inject based on the package of the class - java

I have 2 modules containing classes:
blog.model.ArticleDAO
blog.model.CategoryDAO
users.model.UserDAO
users.model.UserGroupDAO
All these DAOs have a dependency on the same service, but I need to inject a different instance based on the package.
I mean the module blog should have a specific instance of MyService, and the module users should have another instance of MyService.
I don't want to create 2 named services because some day I may want to use the same service for all DAOs. Or I could also want to inject another specific instance for a specific class...
Is there a way to inject a service based on the package of a class?
A way to say:
inject foo (instance of MyService) into classes that are in blog.*
inject bar (instance of MyService) into classes that are in users.*
but keeping all my classes unaware of that! Their configuration should only state "Inject an instance of MyService".

First I want to say, I find this a strange requirement. I am also wondering why your DAOs need a Service. In a normal layered design, this is the opposite (the Service uses the DAO).
However I find the challenge interesting, I tried to use a FactoryBean to create a Java Proxy class which would redirect at runtime to the correct instance of MyService depending of the caller package. Here is the code:
public class CallerPackageAwareProxyFactoryBean implements
FactoryBean<MyService>, ApplicationContextAware {
private Class<?> targetServiceType;
private ApplicationContext applicationContext;
private InvocationHandler invocationHandler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if (ReflectionUtils.isEqualsMethod(method)) {
// Only consider equal when proxies are identical.
return (proxy == args[0]);
} else if (ReflectionUtils.isHashCodeMethod(method)) {
// Use hashCode of service locator proxy.
return System.identityHashCode(proxy);
} else if (ReflectionUtils.isToStringMethod(method)) {
return "Service dispatcher: " + targetServiceType.getName();
} else {
String callerPackageFirstLevel = getCallerPackageFirstLevel();
Map<String, ?> beans = applicationContext
.getBeansOfType(targetServiceType);
for (Map.Entry<String, ?> beanEntry : beans.entrySet()) {
if (beanEntry.getKey().startsWith(callerPackageFirstLevel)) {
return method.invoke(beanEntry.getValue(), args);
}
}
throw new IllegalArgumentException(
String.format(
"Could not find any valid bean to forward call for method %s.",
method.getName()));
}
}
private String getCallerPackageFirstLevel() {
Throwable t = new Throwable();
StackTraceElement[] elements = t.getStackTrace();
String callerClassName = elements[3].getClassName();
return callerClassName.split("\\.")[0];
}
};
public MyService getObject() throws Exception {
return (MyService) Proxy.newProxyInstance(Thread.currentThread()
.getContextClassLoader(), new Class<?>[] { MyService.class },
invocationHandler);
}
public Class<?> getObjectType() {
return MyService.class;
}
public boolean isSingleton() {
return true;
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public void setTargetServiceType(Class<?> targetServiceType) {
this.targetServiceType = targetServiceType;
}
}
I didn't had to change anything to the Dao or Service configuration. I just had to add the creation of the FactoryBean in the Spring context:
<bean id="myService" class="stackoverflow.CallerPackageAwareProxyFactoryBean">
<property name="targetServiceType" value="a.b.c.MyService" />
</bean>
Maybe a few comments:
The caller package can only be get by creating an exception and looking at the stacktrace.
The code of the InvocationHandler is inspired from ServiceLocatorFactoryBean.
I am still wondering if there is an easier way but I think there is not.
You could replace part of the InvocationHandler to use a configuration Map (package => MyService bean name)
I would not recommend using such code in a productive environment.

Related

Spring use different property files depending on request params

Background:
I am working on a java Spring REST microservice that needs to work with multiple identical back-end systems and multiple identical databases depending on the request parameters.
Basically I have 3 "brands". For each brand there is a set of downstream services and a database. I have no control over those.
My spring service will receive brand as a part of request and will need to call the right downstream services and use the correct database.
Previously I would deal with this by having a separate instance of the spring service for each of the brands. There would be a single property file for each brand and spring would use it to wire up beans. I would have separate URL's for each brand and there was no problem.
Some of my beans need to know about "brand" during creation as they are wrappers around connections downstream services. I.e. once the bean is created there won't be a way to switch it to be a "different brand".
Problem:
I would like to change this so that a single instance of my service can handle requests for any brand.
Requirements:
I was thinking about the following solution:
Have a general property file for non-branded stuff. Spring would wire any non-branded beans and keep them as singleton beans.
Have a property file with brand specific urls etc for each of the brands
Spring would create set of singleton beans for each of the brand using appropriate property file.
Next when the request comes in spring would read the request params and use bean specific for that brand.
Performance is important to me so I would like to reuse the beans as much as possible.
I would like to make this thing as transparent as possible so that people creating new beans don't have to worry about doing anything outside standard configuration/context class.
Does anyone know what would be the best solution to achieve this?
I think you can solve the problem injecting the service in every request with the right set of configurations and beans; possibly already existing in your Application Context.
Given:
$ curl http://localhost:8080/greetings/rodo && echo
Hi from brand1, rodo
$ curl -H "x-brand-name: brand1" http://localhost:8080/greetings/rodo
Hi from brand1, rodo
$ curl -H "x-brand-name: brand2" http://localhost:8080/greetings/rodo && echo
Hi from brand2, rodo
The following code would work:
-- application.yml --
brand1:
greetingPrefix: Hi from brand1,
brand2:
greetingPrefix: Hi from brand2,
-- DemoApplication.java --
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Configuration
class ServiceConfig {
#Bean
public GreetingService greetingServiceBrand1(Brand1Config config) {
return new GreetingService(config);
}
#Bean
public GreetingService greetingServiceBrand2(Brand2Config config) {
return new GreetingService(config);
}
}
#Configuration
class WebConfig implements WebMvcConfigurer {
#Autowired
private ApplicationContext applicationContext;
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(greetingServiceResolver());
}
private GreetingServiceResolver greetingServiceResolver() {
GreetingService greetingServiceBrand1 = applicationContext.getBean("greetingServiceBrand1", GreetingService.class);
GreetingService greetingServiceBrand2 = applicationContext.getBean("greetingServiceBrand2", GreetingService.class);
return new GreetingServiceResolver(greetingServiceBrand1, greetingServiceBrand2);
}
}
}
#RestController
#RequestMapping("/greetings")
class GreetingController {
#GetMapping("/{name}")
public String get(GreetingService greetingService, #PathVariable String name) {
return greetingService.sayHi(name);
}
}
class GreetingServiceResolver implements HandlerMethodArgumentResolver {
private final GreetingService greetingServiceBrand1;
private final GreetingService greetingServiceBrand2;
public GreetingServiceResolver(GreetingService greetingServiceBrand1, GreetingService greetingServiceBrand2) {
this.greetingServiceBrand1 = greetingServiceBrand1;
this.greetingServiceBrand2 = greetingServiceBrand2;
}
#Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().equals(GreetingService.class);
}
#Override
public Object resolveArgument(
MethodParameter methodParameter,
ModelAndViewContainer modelAndViewContainer,
NativeWebRequest nativeWebRequest,
WebDataBinderFactory webDataBinderFactory
) throws Exception {
String brand = nativeWebRequest.getHeader("x-brand-name");
return resolveGreetingService(brand);
}
private GreetingService resolveGreetingService(String brand) {
if ("brand2".equals(brand)) {
return greetingServiceBrand2;
}
return greetingServiceBrand1; // default
}
}
class GreetingService {
private BaseConfig config;
public GreetingService(BaseConfig config) {
this.config = config;
}
public String sayHi(String name) {
return config.getGreetingPrefix() + " " + name;
}
}
abstract class BaseConfig {
private String greetingPrefix;
public String getGreetingPrefix() {
return greetingPrefix;
}
public void setGreetingPrefix(String greetingPrefix) {
this.greetingPrefix = greetingPrefix;
}
}
#Configuration
#ConfigurationProperties("brand1")
class Brand1Config extends BaseConfig {
}
#Configuration
#ConfigurationProperties("brand2")
class Brand2Config extends BaseConfig {
}
As you can see, it's fundamental to pass the service to each controller method, write a resolver and inject the right set of dependencies depending on a parameter passed to the request, in this case via header.
Since your property files need to be declared statically anyway, you can just write all your different brand stuff in the same property file, like in a key-value format, that Spring can pick up as a list of configurations.
brandConfigs:
- brand: foo
property: foos
- brand2: bar
porperty: bars
Load all your connection beans to your downstream services on startup and just route to them according to your request param. Imo this seems to be the most straight forward and performant way. If some of these downstreams are used very rarely you can lazy load the beans on-demand, but probably this wouldn't make a sense unless you have thousands of different downstream routes.

Jersey: Detect when Controller Class is Created

I've implemented a JAX-RS server application using Jersey 2.24.
I use the Guice-HK2 bridge so that the controller classes (those annotated with #Path) are injected with dependencies from Guice, not Jersey/HK2.
However, HK2 still creates instances of the #Path annotated classes itself.
Is there a way I can plug into Jersey/HK2 so that I'm notified when a #Path annotated class is created? Like some sort of lifecycle listener? Every time a #Path annotated class is created by Jersey/HK2 I want to do some registering/logging of that class.
If Guice were doing the actual creation of the #Path annotated class I think I could do it using a generic Provider but that's not available in this case, since Jersey/HK2 is creating the actual instance.
Thank you!!
I think the least intrusive way would be to just use AOP. HK2 offers AOP. What you can do is create a ConstructorInterceptor. Something like
public class LoggingConstructorInterceptor implements ConstructorInterceptor {
private static final Logger LOG
= Logger.getLogger(LoggingConstructorInterceptor.class.getName());
#Override
public Object construct(ConstructorInvocation invocation) throws Throwable {
Constructor ctor = invocation.getConstructor();
LOG.log(Level.INFO, "Creating: {0}", ctor.getDeclaringClass().getName());
// returned instance from constructor invocation.
Object instance = invocation.proceed();
LOG.log(Level.INFO, "Created Instance: {0}", instance.toString());
return instance;
}
}
Then create a InterceptorService to only use the interceptor for classes annotated with #Path
public class PathInterceptionService implements InterceptionService {
private static final ConstructorInterceptor CTOR_INTERCEPTOR
= new LoggingConstructorInterceptor();
private final static List<ConstructorInterceptor> CTOR_LIST
= Collections.singletonList(CTOR_INTERCEPTOR);
#Override
public Filter getDescriptorFilter() {
return BuilderHelper.allFilter();
}
#Override
public List<MethodInterceptor> getMethodInterceptors(Method method) {
return null;
}
#Override
public List<ConstructorInterceptor> getConstructorInterceptors(Constructor<?> ctor) {
if (ctor.getDeclaringClass().isAnnotationPresent(Path.class)) {
return CTOR_LIST;
}
return null;
}
}
Then just register the InterceptionService and ConstructorInterceptor with the DI system
new ResourceConfig()
.register(new AbstractBinder(){
#Override
public void configure() {
bind(PathInterceptionService.class)
.to(InterceptionService.class)
.in(Singleton.class);
bind(LoggingConstructorInterceptor.class)
.to(ConstructorInterceptor.class)
.in(Singleton.class);
}
});
See complete example in this Gist
See Also:
HK2 documentation on AOP

Is it possible to have Spring inject only already-initialized instances of a type? [duplicate]

I have several beans in my Spring context that have state, so I'd like to reset that state before/after unit tests.
My idea was to add a method to a helper class which just goes through all beans in the Spring context, checks for methods that are annotated with #Before or #After and invoke them.
How do I get a list of instantiated beans from the ApplicationContext?
Note: Solutions which simply iterate over all defined beans are useless because I have many lazy beans and some of them must not be instantiated because that would fail for some tests (i.e. I have a beans that need a java.sql.DataSource but the tests work because they don't need that bean).
For example:
public static List<Object> getInstantiatedSigletons(ApplicationContext ctx) {
List<Object> singletons = new ArrayList<Object>();
String[] all = ctx.getBeanDefinitionNames();
ConfigurableListableBeanFactory clbf = ((AbstractApplicationContext) ctx).getBeanFactory();
for (String name : all) {
Object s = clbf.getSingleton(name);
if (s != null)
singletons.add(s);
}
return singletons;
}
I had to improve it a little
#Resource
AbstractApplicationContext context;
#After
public void cleanup() {
resetAllMocks();
}
private void resetAllMocks() {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
for (String name : context.getBeanDefinitionNames()) {
Object bean = beanFactory.getSingleton(name);
if (Mockito.mockingDetails(bean).isMock()) {
Mockito.reset(bean);
}
}
}
I am not sure whether this will help you or not.
You need to create your own annotation eg. MyAnnot.
And place that annotation on the class which you want to get.
And then using following code you might get the instantiated bean.
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new AnnotationTypeFilter(MyAnnot.class));
for (BeanDefinition beanDefinition : scanner.findCandidateComponents("com.xxx.yyy")){
System.out.println(beanDefinition.getBeanClassName());
}
This way you can get all the beans having your custom annotation.
applicationContext.getBeanDefinitionNames() does not show the beans which are registered without BeanDefinition instance.
package io.velu.core;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#Configuration
#ComponentScan
public class Core {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Core.class);
String[] singletonNames = context.getDefaultListableBeanFactory().getSingletonNames();
for (String singleton : singletonNames) {
System.out.println(singleton);
}
}
}
Console Output
environment
systemProperties
systemEnvironment
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.ConfigurationClassPostProcessor.importRegistry
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
messageSource
applicationEventMulticaster
lifecycleProcessor
As you can see in the output, environment, systemProperties, systemEnvironment beans will not be shown using context.getBeanDefinitionNames() method.
Spring Boot
For spring boot web applications, all the beans can be listed using the below endpoint.
#RestController
#RequestMapping("/list")
class ExportController {
#Autowired
private ApplicationContext applicationContext;
#GetMapping("/beans")
#ResponseStatus(value = HttpStatus.OK)
String[] registeredBeans() {
return printBeans();
}
private String[] printBeans() {
AutowireCapableBeanFactory autowireCapableBeanFactory = applicationContext.getAutowireCapableBeanFactory();
if (autowireCapableBeanFactory instanceof SingletonBeanRegistry) {
String[] singletonNames = ((SingletonBeanRegistry) autowireCapableBeanFactory).getSingletonNames();
for (String singleton : singletonNames) {
System.out.println(singleton);
}
return singletonNames;
}
return null;
}
}
[
"autoConfigurationReport",
"springApplicationArguments",
"springBootBanner",
"springBootLoggingSystem",
"environment",
"systemProperties",
"systemEnvironment",
"org.springframework.context.annotation.internalConfigurationAnnotationProcessor",
"org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory",
"org.springframework.boot.autoconfigure.condition.BeanTypeRegistry",
"org.springframework.context.annotation.ConfigurationClassPostProcessor.importRegistry",
"propertySourcesPlaceholderConfigurer",
"org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.store",
"preserveErrorControllerTargetClassPostProcessor",
"org.springframework.context.annotation.internalAutowiredAnnotationProcessor",
"org.springframework.context.annotation.internalRequiredAnnotationProcessor",
"org.springframework.context.annotation.internalCommonAnnotationProcessor",
"org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor",
"org.springframework.scheduling.annotation.ProxyAsyncConfiguration",
"org.springframework.context.annotation.internalAsyncAnnotationProcessor",
"methodValidationPostProcessor",
"embeddedServletContainerCustomizerBeanPostProcessor",
"errorPageRegistrarBeanPostProcessor",
"messageSource",
"applicationEventMulticaster",
"org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration$EmbeddedTomcat",
"tomcatEmbeddedServletContainerFactory",
"org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration$TomcatWebSocketConfiguration",
"websocketContainerCustomizer",
"spring.http.encoding-org.springframework.boot.autoconfigure.web.HttpEncodingProperties",
"org.springframework.boot.autoconfigure.web.HttpEncodingAutoConfiguration",
"localeCharsetMappingsCustomizer",
"org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration",
"serverProperties",
"duplicateServerPropertiesDetector",
"spring.resources-org.springframework.boot.autoconfigure.web.ResourceProperties",
"org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$DefaultErrorViewResolverConfiguration",
"conventionErrorViewResolver",
"org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration",
"errorPageCustomizer",
"servletContext",
"contextParameters",
"contextAttributes",
"spring.mvc-org.springframework.boot.autoconfigure.web.WebMvcProperties",
"spring.http.multipart-org.springframework.boot.autoconfigure.web.MultipartProperties",
"org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration",
"multipartConfigElement",
"org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration$DispatcherServletRegistrationConfiguration",
"org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration$DispatcherServletConfiguration",
"dispatcherServlet",
"dispatcherServletRegistration",
"requestContextFilter",
"org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration",
"hiddenHttpMethodFilter",
"httpPutFormContentFilter",
"characterEncodingFilter",
"org.springframework.context.event.internalEventListenerProcessor",
"org.springframework.context.event.internalEventListenerFactory",
"reportGeneratorApplication",
"exportController",
"exportService",
"org.springframework.boot.autoconfigure.AutoConfigurationPackages",
"org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration",
"org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$Jackson2ObjectMapperBuilderCustomizerConfiguration",
"spring.jackson-org.springframework.boot.autoconfigure.jackson.JacksonProperties",
"standardJacksonObjectMapperBuilderCustomizer",
"org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperBuilderConfiguration",
"org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration",
"jsonComponentModule",
"jacksonObjectMapperBuilder",
"org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperConfiguration",
"jacksonObjectMapper",
"org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration",
"org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration",
"org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration",
"org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration",
"defaultValidator",
"org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration",
"error",
"beanNameViewResolver",
"errorAttributes",
"basicErrorController",
"org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$EnableWebMvcConfiguration",
"org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter",
"mvcContentNegotiationManager",
"org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration$StringHttpMessageConverterConfiguration",
"stringHttpMessageConverter",
"org.springframework.boot.autoconfigure.web.JacksonHttpMessageConvertersConfiguration$MappingJackson2HttpMessageConverterConfiguration",
"mappingJackson2HttpMessageConverter",
"org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration",
"messageConverters",
"mvcConversionService",
"mvcValidator",
"requestMappingHandlerAdapter",
"mvcResourceUrlProvider",
"requestMappingHandlerMapping",
"mvcPathMatcher",
"mvcUrlPathHelper",
"viewControllerHandlerMapping",
"beanNameHandlerMapping",
"resourceHandlerMapping",
"defaultServletHandlerMapping",
"mvcUriComponentsContributor",
"httpRequestHandlerAdapter",
"simpleControllerHandlerAdapter",
"handlerExceptionResolver",
"mvcViewResolver",
"org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter$FaviconConfiguration",
"faviconRequestHandler",
"faviconHandlerMapping",
"defaultViewResolver",
"viewResolver",
"welcomePageHandlerMapping",
"org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration",
"objectNamingStrategy",
"mbeanServer",
"mbeanExporter",
"org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration",
"springApplicationAdminRegistrar",
"org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration",
"org.springframework.boot.autoconfigure.web.JacksonHttpMessageConvertersConfiguration",
"spring.info-org.springframework.boot.autoconfigure.info.ProjectInfoProperties",
"org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration",
"multipartResolver",
"org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration$RestTemplateConfiguration",
"restTemplateBuilder",
"org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration",
"spring.devtools-org.springframework.boot.devtools.autoconfigure.DevToolsProperties",
"org.springframework.boot.devtools.autoconfigure.LocalDevToolsAutoConfiguration$RestartConfiguration",
"fileSystemWatcherFactory",
"classPathRestartStrategy",
"classPathFileSystemWatcher",
"hateoasObjenesisCacheDisabler",
"org.springframework.boot.devtools.autoconfigure.LocalDevToolsAutoConfiguration$LiveReloadConfiguration$LiveReloadServerConfiguration",
"org.springframework.boot.devtools.autoconfigure.LocalDevToolsAutoConfiguration$LiveReloadConfiguration",
"optionalLiveReloadServer",
"org.springframework.boot.devtools.autoconfigure.LocalDevToolsAutoConfiguration",
"lifecycleProcessor"
]
I've created a gist ApplicationContextAwareTestBase.
This helper class does two things:
It sets all internal fields to null. This allows Java to free memory that isn't used anymore. It's less useful with Spring (the Spring context still keeps references to all the beans), though.
It tries to find all methods annotated with #After in all beans in the context and invokes them after the test.
That way, you can easily reset state of your singletons / mocks without having to destroy / refresh the context.
Example: You have a mock DAO:
public void MockDao implements IDao {
private Map<Long, Foo> database = Maps.newHashMap();
#Override
public Foo byId( Long id ) { return database.get( id ) );
#Override
public void save( Foo foo ) { database.put( foo.getId(), foo ); }
#After
public void reset() { database.clear(); }
}
The annotation will make sure reset() will be called after each unit test to clean up the internal state.
Using the previous answers, I've updated this to use Java 8 Streams API:
#Inject
private ApplicationContext applicationContext;
#Before
public void resetMocks() {
ConfigurableListableBeanFactory beanFactory = ((AbstractApplicationContext) applicationContext).getBeanFactory();
Stream.of(applicationContext.getBeanDefinitionNames())
.map(n -> beanFactory.getSingleton(n))
// My ConfigurableListableBeanFactory isn't compiled for 1.8 so can't use method reference. If yours is, you can say
// .map(ConfigurableListableBeanFactory::getSingleton)
.filter(b -> Mockito.mockingDetails(b).isMock())
.forEach(Mockito::reset);
}

Why Guice prevents from binding to Provider?

Recently, when I played around with Google Guice I was trying to do something like this:
#Override
protected void configure() {
...
bind(Provider.class).to(ViewFactory.class);
...
}
Where ViewFactory was:
public class ViewFactory implements Provider<SomeType> {...}
Of course, Guice didn't let me do that returing error:
1) Binding to Provider is not allowed.
{stacktrace}
What is the reason why it is not possible to bind to provider?
I guess it is because Provider interface is very special to Guice. In fact, all its internal machinery is implemented in term of providers.
Moreover, this could create ambiguities. If bindings to providers were possible:
bind(SomeClass.class).to(SomeClassImpl1.class);
bind(new TypeLiteral<Provider<SomeClass>>() {}).to(() -> new SomeClassImpl2());
then what should Guice inject here?
#Inject
OtherClass(Provider<SomeClass> someClassProvider) { ... }
Should it be a provider which returns SomeClassImpl1 (because of the first binding; remember, direct injections and provider injections are interchangeable in Guice) or should it be a provider which returns SomeClassImpl2 (because of the second binding)?
It really is redundant. Because you can inject SomeClass or Provider<SomeClass> regardless of the actual binding, you can bind the class itself to its provider:
bind(SomeClass.class).toProvider(() -> new SomeClassImpl());
// Either of the following will work
#Inject
OtherClass1(Provider<SomeClass> someClassProvider) { ... }
#Inject
OtherClass2(SomeClass someClass) { ... }
Provider is a special case. Guice does a lot of things behind the scenes with Provider, so they just ban binding to the Provider class entirely. One example is with scoping: your custom Provider might call new every single time, but if you create the provider in the Singleton scope, that should not happen. So Guice doesn't actually inject your provider, it injects a wrapped version. Things like that is why they ban binding to Provider.class directly. Here's a code example:
import com.google.inject.*;
import com.google.inject.name.*;
public class ProviderBindExample {
public static class ProvModule extends AbstractModule {
#Override
protected void configure() {
bind(Foo.class).toProvider(FooProvider.class);
bind(Foo.class).annotatedWith(Names.named("singleton"))
.toProvider(FooProvider.class)
.in(Singleton.class);
}
}
public static interface Foo { }
public static class FooProvider implements Provider<Foo> {
#Override
public Foo get() {
return new Foo() {};
}
}
public static class SomeClass {
#Inject public Provider<Foo> provider;
#Inject #Named("singleton") public Provider<Foo> singletonProvider;
}
public static void main(String... args) {
Injector inj = Guice.createInjector(new ProvModule());
SomeClass s = inj.getInstance(SomeClass.class);
System.out.println("Provider class = " + s.provider.getClass());
System.out.println("Singleton provider class = " + s.singletonProvider.getClass());
Foo first = s.provider.get();
Foo second = s.provider.get();
System.out.printf("regular scope: objects are %s%n", first == second ? "the same" : "different");
first = s.singletonProvider.get();
second = s.singletonProvider.get();
System.out.printf("singleton scope: objects are %s%n", first == second ? "the same" : "different");
}
}
Output:
Provider class = class com.google.inject.internal.InjectorImpl$4
Singleton provider class = class com.google.inject.internal.InjectorImpl$4
regular scope: objects are different
singleton scope: objects are the same

EJB Factory Class

I'm trying to create an EJB factory class, which works like this: You have a method which takes as argument a class of an EJB, then it checks whether the EJB has a remote interface (if not throw an exception) and if it does, it returns the concerning EJB.
The code below does exactly this. However the object it returns is of the type of the remote interface of the concerning bean and not of the bean itself. How can I change this? Is there a way to tell Java that the generic type T is of the same type as the class passed to the methods.
import java.util.Properties;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.naming.*;
public class EJBFactory
{
private InitialContext ctx;
public EJBFactory() throws NamingException {
ctx = new InitialContext();
}
public EJBFactory(String host, String port) throws NamingException {
Properties props = new Properties();
props.setProperty("org.omg.CORBA.ORBInitialHost", host);
props.setProperty("org.omg.CORBA.ORBInitialPort", port);
ctx = new InitialContext(props);
}
.
// To improve: The object returned should be of the type ejbClass
// instead of the remote interface, which it implements
public <T> T createEJB(Class ejbClass) throws NamingException
{
Class remoteInterface = null;
for(Class interface_: ejbClass.getInterfaces()) {
if(interface_.isAnnotationPresent(Remote.class))
remoteInterface = interface_;
}
if(remoteInterface == null)
throw new IllegalArgumentException(
"EJB Requires a remote interface");
// Get the stateless annotation, then get the jndiName
Stateless stateless =
(Stateless)ejbClass.getAnnotation(Stateless.class);
String jndiName = stateless.mappedName();
T ejbObj = (T) ctx.lookup(jndiName);
return ejbObj;
}
}
Example of a unit test which uses the Factory.
import junit.framework.TestCase;
public class SimpleEJBTest extends TestCase
{
TestRemote testBean;
#Override
protected void setUp() throws Exception {
super.setUp();
EJBFactory ejbFactory = new EJBFactory();
testBean = ejbFactory.createEJB(TestBean.class);
}
public void testSayHello() {
assertEquals("Hello", testBean.sayHello());
}
}
Note: The example works with Glassfish, I didn't test it with any other app server.
Clients of EJBs interact with them through the local/ remote interface that the EJBs implement. Client applications never have direct access to an actual session bean class instance. This is done to make instance pooling possible, where the container can reuse EJB instances to service different requests.
I'm not sure why you need to access the actual bean's object (since obviously I dont know your requirement). But if you still need to create an instance of that you can do it as follows using reflection Class.forName(className).newInstance(); Again the instance that you create like this is not an EJB. It is just a POJO thats all.
EDIT - after your comment regarding junit testing: When you access business methods from a JavaSE as follows, you are actually calling the methods in the EJB - just that you interact thru the interface. So if you want to test any business methods you can still do it from an object got thru a JNDI lookup in a Junit test.
//MyGreatBean implements MyGreat. MyGreat has #Remote, MyGreatBean has #Stateless
ref = jndiContext.lookup("MyGreatBean/remote");
MyGreat bean = (MyGreat) ref;
String retValue = bean.businessMethod();
assertEquals("Success", retValue);
From an earlier comment, I get a feeling you want to check what kind of annotations have been added to the actual EJB class - if you want to do that kind of checking without actually running the business methods, you can create an instance using Class.forName... like I mentioned above. When you create an instance like this you can only call methods that don't do any "Java EE" stuff. For example you can call a method in the EJB class that is as follows
public String someMethod(){
return "I am a POJO but I look like an EJB";
}
I do not think that you can get the EJB object. You can only get the interface. The createEJB should be called with the interface and it returns the interface.
try replacing
public <T> T createEJB(Class ejbClass) throws NamingException
with
public <T> T createEJB(Class<T> ejbClass) throws NamingException
Can you try this?
Create a interface. Make it have #Remote. Your ejb that is annotated with #Stateless should implement the above created interface. Now try to do the same thing that you are doing I think it should give you the desired result. Typing it down here without copying from an ide so excuse any errors. But you should get the drift I guess.
#Remote
public interface Example{
String some();
}
#stateless
public class ExampleBean implements Example{
}

Categories