Class instance based on input parameter using Guice binding - java

I have Java interface classes.
public interface ModelClient {
}
public interface DownstreamService1Client extends ModelClient {
public ContentData getContentData();
}
public interface DownstreamService2Client extends ModelClient {
public ContentData getContentData();
}
public interface DownstreamService3Client extends ModelClient {
public ContentData getContentData();
}
I have another spec builder method:
ModelClientSpec<DownstreamService1Client> spec = ModelClientSpec.builder(DownstreamService1Client.class);
Above spec can be used to create a client:
DownstreamService1Client client = context.getResourceClient(spec);
which can be used to call downstream client to get data:
ContentData data = client.getContentData(); // get the data from downstream service.
I have created following client spec static map:
"contentType" -> DownstreamClientSpec
"music" -> DownstreamClient1Spec
"books" -> DownstreamClient2Spec
...
Now I have a handler method:
public ContentData handle(String contentType) {
// need to get a client based on contentType
client = ???
return client.getContentData()
}
How do I get the client based on the contentType other than having the switch statement for contentType and specific client creation logic? Is it a clean way to dynamically bind the specific client using Guice?
Thanks!

I guess, Multibindings, and specifically MapBinder can be used to achieve what you are looking for. Bind your client to a map, inject it and get specific implementation from that map by key.

Related

Adding functionality to implementation classes without changing the implemented Interface

I have got an interface that defines some service methods for data retrieval:
public interface DataReceiver {
public Data getData();
}
Then i have a class that implements this interface and loads the data through a connection. I supply this connection using constructor injection:
public class ConnectionDataReceiver implements DataReceiver {
private Connection connection;
public ConnectionDataReceiver(Connection connection) {
this.connection = connection;
}
public Data getData() {
return connection.query("blabla");
}
}
This works pretty nicely. I can instantiate my ConnectionDataReceiver objects using the constructor, or i could add a factory method/class that extends the usability by providing an option to select a config file for connection setup. I then use my new class through my interface, so i can easily swap out the implementation (like loading the data from a file instead of a connection).
But what if i want to change my connection during runtime, without instantiating a new ConnectionDataReceiver? I would have to add getters and setters for my class. But since they are not part of my public service definition, i can't put them in my interface. I could use the implementation object in my code to set a new connection, but it feels pretty awkward hanging onto a reference to the original object only for maybe changing the connection object:
ConnectionDataReceiver conDataRec = new ConnectionDataReceiver(myConnection);
DataReceiver dataRec = conDataRec;
// use dataRec
conDataRec.setConnection(myNewConnection);
// use dataRec again
In this example it would be the easiest way to just instantiate a new ConnectionDataReceiver and just reassign dataRec, but what if the instantiation of my object is really expensive? How do i give my implementation classes additional functionality while still being able to use my old service interface? Or is it generally frowned upon changing data at runtime, when the interface doesn't define that functionality?
What you can do is that adding following two simple methods in your interface:
public void setProperty(String name, Object value);
public Object getProperty(String name);
Now with the help of these two simple methods, you may configure as many additional functionalities as you want in your implementation classes without adding a new method for a new feature (of your implementation class) in your super type.
This pattern is used in following interface:
com.ibm.msg.client.jms.JmsQueueConnectionFactory
The interface has setCharProperty, setDoubleProperty, setFloatProperty etc so that when they release a new implementation they do not have to modify the interface.
My version:
Interface
public interface DataReceiver
{
public Data getData();
}
Implementation
public class ConnectionDataReceiver implements DataReceiver
{
private Connection connection;
public ConnectionDataReceiver(Connection connection)
{
this.connection = connection;
}
public Data getData()
{
return connection.query("blabla");
}
}
Interface using in business layer, here method setReceiver will assign new implementation of interface in run-time.
public class SomeBusinessLogic
{
private DataReceiver receiver;
public SomeBusinessLogic(DataReceiver receiver)
{
this.receiver = receiver;
}
public void setReceiver(DataReceiver receiver)
{
this.receiver = receiver;
}
}
With this approach you can change implementation of DataReceiver in run-time

You must use at least one, but no more than one http method annotation on for reaseasy proxy client

I am trying to implement a simple client in rest easy, but I am getting an error saying "You must use at least one, but no more than one http method annotation". In my server implementation, I have added a http annotation on my method.
#Path("/")
public class TestResource
{
#GET
#Path("/domain/{value}")
public String get(#PathParam("value") final String value) {
return "Hello" + value;
}
}
I debugged it through, the first time it is not hitting the runtime exception, However, it is making a second call to it and failing, not sure why and how.
My client as junit test:
#Test
public void testPerformRestEasy() {
ResteasyClient client = new ResteasyClientBuilder().build();
ResteasyWebTarget target = client.target("http://localhost:8080/");
TestResource proxy = target.proxy(TestResource.class);
String response = proxy.get("user");
Assert.assertEquals("Hellouser", response);
}
The code where it is failing
private static <T> ClientInvoker createClientInvoker(Class<T> clazz, Method method, ResteasyWebTarget base, ProxyConfig config)
{
Set<String> httpMethods = IsHttpMethod.getHttpMethods(method);
if (httpMethods == null || httpMethods.size() != 1)
{
throw new RuntimeException("You must use at least one, but no more than one http method annotation on: " + method.toString());
}
ClientInvoker invoker = new ClientInvoker(base, clazz, method, config);
invoker.setHttpMethod(httpMethods.iterator().next());
return invoker;
}
Error:
java.lang.RuntimeException: You must use at least one, but no more than one http method annotation on: public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
at org.jboss.resteasy.client.jaxrs.ProxyBuilder.createClientInvoker(ProxyBuilder.java:76)
at org.jboss.resteasy.client.jaxrs.ProxyBuilder.proxy(ProxyBuilder.java:52)
at org.jboss.resteasy.client.jaxrs.ProxyBuilder.build(ProxyBuilder.java:120)
at org.jboss.resteasy.client.jaxrs.internal.ClientWebTarget.proxy(ClientWebTarget.java:72)
Does anyone know what the issue is here?
The Resteasy JAXRS 2 client does not seem to accept implementation classes directly. To make it work, you have to create a properly annotated interface. It is used by Resteasy to generate a client proxy and your server must implement exactly the same interface.
So in your case, you have to split your code into an interface and a separate implementation class:
#Path("/")
public interface TestResource {
#GET
#Path("/domain/{value}")
String get(#PathParam("value") final String value);
}
public class TestResourceImpl implements TestResource {
#Override String get(final String value) {
return "Hello" + value;
}
}
I'm not sure if this is Resteasy-specific or required by the specification, but solved the same issue for me. You can find the section that gave me the hint here in the documentation.
You have to define the MIME media type resource representation of resource(#Produces/#Consumes) from client. Like -
#Path("/")
public class TestResource
{
#GET
#Produces("text/plain")
#Path("/domain/{value}")
public String get(#PathParam("value") final String value) {
return "Hello" + value;
}
}
The Jboss Client framework Doc will help you more.
In my case the developer of the Rest Client Interface had wrongly extended RestEasyClientProxy. It wasn't the methods in the Rest Interface that were missing the http annotations, but the inherited methods.
Removing extends RestEasyClientProxy from the Rest Client Interface code fixed the issue.

Headerenricher Spring Integration and java dsl

I'me using Spring Integration and java dsl specifications to implement my IntegrationFlow.
I want to use an custom header enricher to add some file names to the header, it will be something like :
public class FileHeaderNamingEnricher {
public Message<File> enrichHeader(Message<File> fileMessage) {
// getting some details fom the database ...
return messageBuilder
.setHeader("filename", "somestuff")
.build();
}
}
And my Integration flow will look like :
public IntegrationFlow myflow() {
return IntegrationFlows.from("input")
.enrich // here I want to enrich the header using my class
}
Can any one help me with this please ?
You can have your FileHeaderNamingEnricher extend AbstractReplyProducingMesageHandler (put your code in handleRequestMessage()).
Or, implement GenericHandler<T> (its handle method gets the payload and headers as parameters and can return a message).
Then use the .handle method...
...
.handle(myEnricher())
...
#Bean
public void MessageHandler myEnricher() {
return new FileHeaderNamingEnricher();
}

Restlet: How can I retrieve DTO with setting custom MediaType?

How can I send GET request for entity with custom MediaType?
For example I want to retrieve MyUserDTO and set MediaType to application/user+yml.
For now I have two separated actions. I can retrieve entity:
resource.get(MyUserDTO.class);
and can retrieve string:
resource.get(new MediaType("application", "user+yml");
But how to combine them? Or maybe there is some trick to configure Restlet to teach him how to work with custom MediaTypes.
In fact, you have the right approach but you don't use the right constructor of the class MediaType (new MediaType(name, description)).
To make your code work, you need to change it to this:
resource.get(new MediaType("application/user+yml"));
On the server side, you will get this:
#Get
public Representation getSomething() {
System.out.println(">> media types = " +
getRequest().getClientInfo().getAcceptedMediaTypes());
// Display this: media types = [application/user+yml:1.0]
(...)
}
You can leverage the extension support of Restlet by adding a value within the annotation Get. In your case, you need to add a custom extension as described below:
public class MyApplication extends Application {
public MyApplication() {
getMetadataService().addExtension(
"myextension", new MediaType("application/user+yml"));
(...)
}
#Override
public Restlet createInboundRoot() {
(...)
}
}
Now you can use the extension within your server resource:
#Get("myextension")
public Representation getSomething() {
(...)
}
This method will be used with the expected media type is application/user+yml.
Hope it helps you,
Thierry

GWT RequestFactory - adding custom methods to proxy classes?

Is is possible to add a method to GWT RequestFactory's proxy class? Let's say I have this:
#ProxyFor(value = MyEntity.class)
interface MyEntityProxy extends EntityProxy {
String getData(); // got it on server side
}
GetData() is backed at server side, that's fine. What if I'd like to add a method like this:
#ProxyFor(value = MyEntity.class)
interface MyEntityProxy extends EntityProxy {
String getData(); // got it on server side
String getDataAndAppendQwerty(); // want this one on client side
}
I want to manually implement getDataAndAppendQwerty(). It's 100% client-side code and the question is just where should I put the implementation.
The answer would be AutoBean categories, but they're not (yet) surfaced in RequestFactory.
I don't know of an easy way. You could use a wrapper and delegate
public class MyEntityProxyExt implements MyEntityProxy {
private final MyEntityProxy proxy;
public MyEntityProxyExt(MyEntityProxy proxy) {
this.proxy = proxy;
}
#Override
public String getData() {
return proxy.getData();
}
public Object getDataAndAppendQwerty() {
return proxy.getData() + "qwerty";
}
}
but you'd have to manually wrap all your proxy objects on the client when you get them back from the server.

Categories

Resources