How should I do the ValueFactoryProvider binding in order to have two custom injection annotations coexist in Jersey 2? Below I have included an example of my current approach and as you can see the Hello annotation injection "hides" the SmallTalk annotation injection.
Hello annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.PARAMETER})
public #interface Hello {
}
SmallTalk annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.PARAMETER})
public #interface SmallTalk {
}
Hello annotation resolver:
#Singleton
public class HelloResolver {
public static class HelloInjectionResolver extends ParamInjectionResolver<Hello> {
public HelloInjectionResolver() {
super(HelloValueFactoryProvider.class);
}
}
#Singleton
public static class HelloValueFactoryProvider extends AbstractValueFactoryProvider {
#Inject
public HelloValueFactoryProvider(final MultivaluedParameterExtractorProvider extractorProvider,
final ServiceLocator injector) {
super(extractorProvider, injector, UNKNOWN);
}
#Override
protected Factory<?> createValueFactory(final Parameter parameter) {
final Class<?> classType = parameter.getRawType();
if (classType == null || (!classType.equals(String.class))) return null;
return new AbstractContainerRequestValueFactory<String>() {
#Override
public String provide() {
return "Hello!";
}
};
}
}
public static class Binder extends AbstractBinder {
#Override
protected void configure() {
bind(HelloValueFactoryProvider.class).to(ValueFactoryProvider.class).in(Singleton.class);
bind(HelloInjectionResolver.class).to(
new TypeLiteral<InjectionResolver<Hello>>() {
}
).in(Singleton.class);
}
}
}
SmallTalk annotation resolver:
#Singleton
public class SmallTalkResolver {
public static class SmallTalkInjectionResolver extends ParamInjectionResolver<SmallTalk> {
public SmallTalkInjectionResolver() {
super(SmallTalkValueFactoryProvider.class);
}
}
#Singleton
public static class SmallTalkValueFactoryProvider extends AbstractValueFactoryProvider {
#Inject
public SmallTalkValueFactoryProvider(final MultivaluedParameterExtractorProvider extractorProvider,
final ServiceLocator injector) {
super(extractorProvider, injector, UNKNOWN);
}
#Override
protected Factory<?> createValueFactory(final Parameter parameter) {
final Class<?> classType = parameter.getRawType();
if (classType == null || (!classType.equals(String.class))) return null;
return new AbstractContainerRequestValueFactory<String>() {
#Override
public String provide() {
return "Nice weather.";
}
};
}
}
public static class Binder extends AbstractBinder {
#Override
protected void configure() {
bind(SmallTalkValueFactoryProvider.class).to(ValueFactoryProvider.class).in(Singleton.class);
bind(SmallTalkInjectionResolver.class).to(
new TypeLiteral<InjectionResolver<SmallTalk>>() {
}
).in(Singleton.class);
}
}
}
Resource configuration:
public class MyApplication extends ResourceConfig {
public MyApplication() {
register(new HelloResolver.Binder());
register(new SmallTalkResolver.Binder());
registerClasses(HelloResource.class);
}
}
Resource using both injection annotations:
#Path("/")
public class HelloResource {
#GET
#Path("hello")
#Produces("application/json")
public String hello(#Hello final String hello, #SmallTalk final String smallTalk) {
return hello + " " + smallTalk;
}
}
Result when requesting the resource - should have been "Hello! Nice weather.":
Found a solution! I added
if (parameter.getAnnotation(Hello.class) == null) return null;
and
if (parameter.getAnnotation(SmallTalk.class) == null) return null;
to the createValueFactory method of the two value factory providers.
Related
when I use a aspect on an annotation, I cannot use AnnotationUtils.getAnnotation,
//here, I cannot not find PulsarListener
PulsarListener annotation = AnnotationUtils.getAnnotation(method, PulsarListener.class);
and when i remove #Aspect ,then it's ok.
#Target({ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface PulsarListener {
String[] topics() default {};
}
#Aspect
#Slf4j
#Component
#Order(0)
public class MDCAspect {
#Around("#annotation(PulsarListener)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
try {
String requestUUID = MDC.get("requestUUID");
if (StringUtils.isEmpty(requestUUID)) {
String uid = ObjectId.get().toHexString();
MDC.put("requestUUID", uid);
}
return joinPoint.proceed();
} finally {
MDC.clear();
}
}
}
#Component
#Slf4j
public class PulsarConsumer {
#PulsarListener(topics = "${topics}")
public void listen(Message<byte[]> receive) {
//doSomething
}
}
public class PulsarPostProcessor implements BeanPostProcessor {
#Value("${pulsar.service.url}")
private String pulsar_service_url;
#Autowired
private ApplicationContext applicationContext;
#Override
public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
Method[] methods = bean.getClass().getDeclaredMethods();
for (Method method : methods) {
//here , i canot not found PulsarListener
//here is the problem
PulsarListener annotation = AnnotationUtils.getAnnotation(method, PulsarListener.class);
if (annotation != null) {
if (log.isDebugEnabled()) {
log.debug("bean :{},method:{}", beanName, method.getName());
}
}
}
}
Hi i am trying to study named injection in Dagger2
Here are my Java classes but none of them seems to be working.
What i want is that based on #Named annotation i wish to get different objects.
public interface Server {
public void start();
public void stop();
public String request(String request);
}
public abstract class AbstractServer implements Server {
private boolean started;
#Override
public void start() {
started = true;
}
#Override
public void stop() {
if (!started) {
throw new IllegalStateException("Server was not started");
}
}
}
public class AudioServer extends AbstractServer{
#Override
public String request(String request) {
return "Response from Audio server: " + request;
}
}
public class VideoServer extends AbstractServer {
#Override
public String request(String request) {
return "Response from Video server: " + request;
}
}
#Module
public class ServerModule {
public ServerModule() {
}
#Provides
#Named("audio")
#Singleton
AudioServer provideAudioServer() {
return new AudioServer();
}
#Provides
#Named("video")
#Singleton
VideoServer provideVideoServer() {
return new VideoServer();
}
}
Please not ServerComponent.java is not compiling
#Singleton
#Component(modules = {ServerModule.class})
public interface ServerComponent {
AudioServer provideAudioServer();
VideoServer provideVideoServer();
void inject(TestInject inject);
}
public class TestInject {
private static final Logger logger = Logger.getLogger(TestInject.class.getSimpleName());
#Inject
#Named("audio")
Server audioServer;
public TestInject() {
// ServerComponent component = DaggerServerComponent.builder()
// .build();
// component.inject(this);
}
public void test() {
String serverResponse = null;
if (audioServer != null) {
serverResponse = audioServer.request("game.mp3");
logger.warning(serverResponse);
} else {
serverResponse = "Failure";
logger.info(serverResponse);
}
}
public static void main(String[] args) {
TestInject inject = new TestInject();
inject.test();
}
}
EDITED Please see answer in TestInject.java and ServerComponent.java
public interface Server {
public void start();
public void stop();
public String request(String request);
}
public abstract class AbstractServer implements Server {
private boolean started;
#Override
public void start() {
started = true;
}
#Override
public void stop() {
if (!started) {
throw new IllegalStateException("Server was not started");
}
}
}
public class AudioServer extends AbstractServer{
#Override
public String request(String request) {
return "Response from Audio server: " + request;
}
}
public class VideoServer extends AbstractServer {
#Override
public String request(String request) {
return "Response from Video server: " + request;
}
}
#Module
public class ServerModule {
public ServerModule() {
}
#Provides
#Named("audio")
#Singleton
AudioServer provideAudioServer() {
return new AudioServer();
}
#Provides
#Named("video")
#Singleton
VideoServer provideVideoServer() {
return new VideoServer();
}
}
Please not ServerComponent.java is not compiling
#Singleton
#Component(modules = {ServerModule.class})
public interface ServerComponent {
#Named("audio")
Server provideAudioServer();
#Named("video")
Server provideVideoServer();
void inject(TestInject inject);
}
public class TestInject {
private static final Logger logger = Logger.getLogger(TestInject.class.getSimpleName());
#Inject
#Named("audio")
Server audioServer;
#Inject
#Named("video")
Server videoServer;
public TestInject() {
ServerComponent component = DaggerServerComponent.builder()
.build();
component.inject(this);
}
public void testAudioServer() {
String serverResponse = null;
if (audioServer != null) {
serverResponse = audioServer.request("game.mp3");
logger.warning(serverResponse);
} else {
serverResponse = "audio server Failure";
logger.info(serverResponse);
}
}
public void testVideoServer() {
String serverResponse = null;
if (videoServer != null) {
serverResponse = videoServer.request("movie.mp4");
logger.warning(serverResponse);
} else {
serverResponse = "Video server Failure";
logger.info(serverResponse);
}
}
public static void main(String[] args) {
TestInject inject = new TestInject();
inject.testAudioServer();
inject.testVideoServer();
}
}
Your main problem seems to be related to the fact that you expect in the class TestInject a Server named audio while your provider returns AudioServer so dagger cannot satisfy your dependency.
Indeed don't forget that the annotation #Named is used to distinguish 2 objects of the same type, in other words you can annotate with #Named("audio") different providers as long as they don't return the same type. The object produced will then be identified by its type and its name.
So for example here is one way to fix you problem:
The class TestInject:
public class TestInject {
...
public TestInject() {
// Needed to inject your dependencies
ServerComponent component = DaggerServerComponent.builder()
.build();
component.inject(this);
}
...
}
The class ServerComponent
#Singleton
#Component(modules = ServerModule.class)
public interface ServerComponent {
void inject(TestInject inject);
}
The class ServerModule
#Module
public class ServerModule {
#Provides
#Named("audio")
#Singleton
public Server provideAudioServer() {
return new AudioServer();
}
#Provides
#Named("video")
#Singleton
public Server provideVideoServer() {
return new VideoServer();
}
}
Even with your question update, your module should be what I propose abose otherwise it won't compile for the same reason described previously.
I use GWTP and restyGWT. I would like to use placeManager in restyGWT DispatcherCallback, when my rest server will answer with 401 unauthorised I would like to redirect application to login page, that User could apply credentials and retried his request.
To do this I have to somehow get instance of PlaceManager (from gwtp framework). I cannot use #Inject annotation, cause I have manuall call to constructor as follow:
public class ForbiddenDispatcherFilter implements DispatcherFilter {
#Override
public boolean filter(Method method, RequestBuilder builder) {
builder.setCallback(new ForbiddenDispatcherCallback(method));
return true;
}
}
public class ForbiddenDispatcherCallback implements RequestCallback {
protected RequestCallback requestCallback;
public ForbiddenDispatcherCallback(Method method) {
this.requestCallback = method.builder.getCallback();
}
#Override
public void onResponseReceived(Request request, Response response) {
if (response.getStatusCode() == Response.SC_FORBIDDEN || response.getStatusCode() == Response.SC_UNAUTHORIZED) {
// make a hard redirect to login page
// TODO change redirect to GWTP native
Window.Location.assign("#login");
// PlaceRequest placeRequest = new
// PlaceRequest.Builder(placeManager.getCurrentPlaceRequest()).nameToken(Routing.Url.login).build();
// placeManager.revealPlace(placeRequest);
} else {
requestCallback.onResponseReceived(request, response);
}
}
public class RestyDispatcher extends DefaultFilterawareDispatcher {
public RestyDispatcher() {
addFilter(new ForbiddenDispatcherFilter());
addFilter(new BasicAuthHeaderDispatcherFilter());
}
#Override
public Request send(Method method, RequestBuilder builder) throws RequestException {
return super.send(method, builder);
}
}
Please help.
Edit
public class ClientModule extends AbstractPresenterModule {
#Override
protected void configure() {
bind(RestyGwtConfig.class).asEagerSingleton();
install(new DefaultModule.Builder()//
.defaultPlace(Routing.HOME.url)//
.errorPlace(Routing.ERROR.url)//
.unauthorizedPlace(Routing.LOGIN.url)//
.tokenFormatter(RouteTokenFormatter.class).build());
install(new AppModule());
// install(new
// GinFactoryModuleBuilder().build(AssistedInjectionFactory.class));
bind(CurrentUser.class).in(Singleton.class);
bind(IsAdminGatekeeper.class).in(Singleton.class);
bind(UserLoginGatekeeper.class).in(Singleton.class);
// Google Analytics
// bindConstant().annotatedWith(GaAccount.class).to("UA-8319339-6");
// Load and inject CSS resources
bind(ResourceLoader.class).asEagerSingleton();
}
}
and:
public class RestyGwtConfig {
static {
// GWT.log("--> RestyGwtConfig -> setDispatcher");
Defaults.setDispatcher(new RestyDispatcher());
// GWT.log("--> RestyGwtConfig -> setServiceRoot");
Defaults.setServiceRoot(new Resource(GWT.getModuleBaseURL()).resolve(ServiceRouting.SERVICE_ROOT).getUri());
UserCredentials.INSTANCE.setUserName("ronan");
UserCredentials.INSTANCE.setPassword("password");
}
}
How and where do you create your ForbiddenDispatcherFilter ?
You could use guice's AssistedInjection to inject the PlaceManager into your ForbiddenDispatcherCallback.
public class ForbiddenDispatcherCallback implements RequestCallback {
protected RequestCallback requestCallback;
protected PlaceManager placeManager;
#Inject
public ForbiddenDispatcherCallback(PlaceManager placeManager, #Assisted Method method) {
this.placeManager = placeManager;
this.requestCallback = method.builder.getCallback();
}
}
You need to define an factory interface:
public interface AssistedInjectionFactory {
ForbiddenDispatcherCallback createForbiddenCallback(Method method);
}
In the configure method of your ClientModule you need to call:
install(new GinFactoryModuleBuilder().build(AssistedInjectionFactory.class));
Then you can instantiate your class this way:
public class ForbiddenDispatcherFilter implements DispatcherFilter {
AssistedInjectionFactory factory;
#Inject
public ForbiddenDispatcherFilter(AssistedInjectionFactory factory)
{
this.factory = factory;
}
#Override
public boolean filter(Method method, RequestBuilder builder) {
builder.setCallback(factory.AssistedInjectionFactory(method))
return true;
}
}
Of course this requires that you also inject the ForbiddenDispatcherFilter.
Edit:
You could try to pass the RestyDispatcher to the constructor of your RestyGWTConfig:
public class RestyGwtConfig {
#Inject
public RestyGwtConfig(RestyDispatcher dispatcher) {
Defaults.setDispatcher(dispatcher);
}
static {
// GWT.log("--> RestyGwtConfig -> setServiceRoot");
Defaults.setServiceRoot(new Resource(GWT.getModuleBaseURL()).resolve(ServiceRouting.SERVICE_ROOT).getUri());
UserCredentials.INSTANCE.setUserName("ronan");
UserCredentials.INSTANCE.setPassword("password");
}
}
The RestyDispatcher looks like this:
public class RestyDispatcher extends DefaultFilterawareDispatcher {
#Inject
public RestyDispatcher(ForbiddenDispatcherFilter filter) {
addFilter(filter);
addFilter(new BasicAuthHeaderDispatcherFilter());
}
#Override
public Request send(Method method, RequestBuilder builder) throws RequestException {
return super.send(method, builder);
}
}
The example I tried to follow:
#PrepareForTest(X.class)
public class XTest extends PowerMockTestCase {
#Test
public void test() {
whenNew(MyClass.class).withNoArguments().thenThrow(new IOException("error message"));
X x = new X();
x.y(); // y is the method doing "new MyClass()"
..
}
}
The factory class I am trying to unit test:
public final class LoadableBeanFactory implements ILoadableBeanFactory {
private static final class Loader {
private static final LoadableBeanFactory INSTANCE = new LoadableBeanFactory();
}
private LoadableBeanFactory() { }
public static #Nonnull LoadableBeanFactory getInstance() {
return Loader.INSTANCE;
}
public final #Nonnull <BeanT extends ILoadableBean> BeanT create(final Class<BeanT> beanClass) {
final BeanT optionBean;
try {
final Constructor<BeanT> ctor = beanClass.getConstructor();
optionBean = ctor.newInstance();
return beanClass.cast(optionBean);
} catch(Exception e) {
throw new IllegalArgumentException("Could not instantiate an instance of " + beanClass);
}
}
}
My test is below. The factory does not return the mock. I am thinking that this is because the factory is a singleton that is instantiated and loaded with a private static loader class. So, is there a way to mock this object creation scenario or should I just give up on making this into a true unit test?
#PrepareForTest(LoadableBeanFactory.class)
#Test(groups = {"FactoryTestGroup", "LoadableBeanFactoryTestGroup"})
public class LoadableBeanFactoryTest extends PowerMockTestCase {
#Mock LoadableBean mockBean;
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void shouldCreateBean() {
try {
PowerMockito.whenNew(LoadableBean.class).withNoArguments().thenReturn(mockBean);
LoadableBeanFactory.getInstance().create(LoadableBean.class);
assertEquals(LoadableBeanFactory.getInstance().create(LoadableBean.class), mockBean,
"LoadableBeanFactory should have return mocked bean, but did not: " + mockBean);
} catch(Exception e) {
fail("Failed to mock bean creation");
}
}
}
Why would you even want to do that?
If you wrap the factory in an abstraction (a separate class) then you can inject it via constructor and mock its create method.
public class BeanFactory {
public <BeanT extends ILoadableBean> BeanT create(final Class<BeanT> beanClass) {
return LoadableBeanFactory.getInstance().create(beanClass);
}
}
and now your class that you want to work with
public class SomeClass {
private final BeanFactory beanFactory;
public SomeClass(BeanFactory beanFactory) {
this.beanFactory= beanFactory;
}
public void doSth() {
beanFactory.create(...);
}
}
And then you don't need to have PowerMock at all and your design is really nice.
I have managed to successfully implement a custom injection annotation with target PARAMETER. I do not understand how I make my the annotation support target METHOD as well though?
Here is my sample code:
Hello annotation:
#Retention(RUNTIME)
#Target({METHOD, PARAMETER})
public #interface Hello {
}
Hello annotation resolver:
#Singleton
public class HelloResolver {
public static class HelloInjectionResolver extends ParamInjectionResolver<Hello> {
public HelloInjectionResolver() {
super(HelloValueFactoryProvider.class);
}
}
#Singleton
public static class HelloValueFactoryProvider extends AbstractValueFactoryProvider {
#Inject
public HelloValueFactoryProvider(final MultivaluedParameterExtractorProvider extractorProvider,
final ServiceLocator injector) {
super(extractorProvider, injector, UNKNOWN);
}
#Override
protected Factory<?> createValueFactory(final Parameter parameter) {
if (!String.class.equals(parameter.getRawType())) return null;
if (parameter.getAnnotation(Hello.class) == null) return null;
return new AbstractContainerRequestValueFactory<String>() {
#Override
public String provide() {
final DateTime now = DateTime.now();
if (22 < now.getHourOfDay() || now.getHourOfDay() < 6) {
throw new WebApplicationException(FORBIDDEN);
} else {
return format("Hello, it is %s o'clock so I am awake! :)", forPattern("HH:mm").print(now));
}
}
};
}
}
public static class Binder extends AbstractBinder {
#Override
protected void configure() {
bind(HelloValueFactoryProvider.class).to(ValueFactoryProvider.class).in(Singleton.class);
bind(HelloInjectionResolver.class).to(
new TypeLiteral<InjectionResolver<Hello>>() {
}
).in(Singleton.class);
}
}
}
Hello resources:
#Path("hello")
public class HelloResource {
#GET
#Path("method")
#Produces(APPLICATION_JSON)
#Hello
public String method() {
return "Hello!";
}
#GET
#Path("param")
#Produces(APPLICATION_JSON)
public String param(#Hello final String hello) {
return hello;
}
}
When I hit
http://localhost:8080/hello/method
I get a Hello! back no matter if the hour is within the forbidden interval.
I am not sure this will work, but you could try this:
public static class HelloInjectionResolver extends ParamInjectionResolver<Hello> {
public HelloInjectionResolver() {
super(HelloValueFactoryProvider.class);
}
public boolean isMethodParameterIndicator() {
return true;
}
}
Warning: I have not tried this myself but in theory that should allow your resolver to work as a parameter in the method.