GWT RequestFactory - adding custom methods to proxy classes? - java

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.

Related

Class instance based on input parameter using Guice binding

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.

Calling Spring controller method without going to internet

tldr: Is there a way to make an internal request (using the method's path) without going to the internet?
--
Why do I need it? I have a project which receives many events. The decision of who will handle each event is made by a Controller. So I have something similar to this:
#RestController
#RequestMapping("/events")
public class EventHandlerAPI {
#Autowired
private EventAHandler eventAhandler;
#Autowired
private EventBHandler eventBhandler;
#PostMapping("/a")
public void handleEventA(#RequestBody EventA event) {
eventAhandler.handle(id, event);
}
#PostMapping("/b")
public void handleEventB(#RequestBody EventB event) {
eventBhandler.handle(id, event);
}
}
We recently added support to receive events through a Queue service. It sends to us the payload and the event class. Our decision is to let both interfaces working (rest and queue). The solution to avoid code duplication was to keep the Controller choosing which handler will take care of the event. The code nowadays is similar to this:
#Configuration
public class EventHandlerQueueConsumer {
#Autowired
private EventHandlerAPI eventHandlerAPI;
private Map<Class, EventHandler> eventHandlers;
#PostConstruct
public void init() {
/* start listen queue */
declareEventHandlers();
}
private void declareEventHandlers() {
eventHandlers = new HashMap<>();
eventHandlers.put(EventAHandler.class, (EventHandler<EventAHandler>) eventHandlerAPI::handleEventA);
eventHandlers.put(EventBHandler.class, (EventHandler<EventBHandler>) eventHandlerAPI::handleEventB);
}
private void onEventReceived(AbstractEvent event) {
EventHandler eventHandler = eventHandlers.get(event.getClass());
eventHandler.handle(event);
}
private interface EventHandler<T extends AbstractEvent> {
void handle(T event);
}
}
This code works, but it doesn't let the controller choose who will handle the event (our intention). The decision is actually being made by the map.
What I would like to do was to invoke the controller method through it's request mapping without going to the internet. Something like this:
#Configuration
public class EventHandlerQueueConsumer {
// MADE UP CLASS TO SHOW WHAT I WANT
#Autowired
private ControllerInkover controllerInvoker;
#PostConstruct
public void init() { /* start listen queue */ }
private void onEventReceived(AbstractEvent event) {
controllerInvoker.post(event.getPath(), new Object[] { event });
}
}
This way is much cleaner and let all the decisions be made by the controller.
I've researched a lot and didn't found a way to implement it. Debugging spring, I found how he routes the request after the DispatcherServlet, but all the spring internals uses HttpServletRequest and HttpServletResponse :(
Is there a way to make an internal request (using the method's path) without going to the internet?
They are classes of the same application
Then it should easy enough.
1) You can call your own API on http(s)://localhost:{port}/api/{path} using RestTemplate utility class. This is preferred way, since you'll follow standard MVC pattern. Something like:
restTemplate.exchange(uri, HttpMethod.POST, httpEntity, ResponseClass.class);
2) If you don't want to invoke network connection at all, then you can either use Spring's internal to find the mapping/method map or use some reflection to build custom
map upon controller's startup. Then you can pass your event/object to the method from the map in a way shown in your mock-up class. Something like:
#RequestMapping("foo")
public void fooMethod() {
System.out.println("mapping = " + getMapping("fooMethod")); // you can get all methods/mapping in #PostContruct initialization phase
}
private String getMapping(String methodName) {
Method methods[] = this.getClass().getMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName() == methodName) {
String mapping[] = methods[i].getAnnotation(RequestMapping.class).value();
if (mapping.length > 0) {
return mapping[mapping.length - 1];
}
}
}
return null;
}

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

DropWizard/Jersey API Clients

DropWizard uses Jersey under the hood for REST. I am trying to figure out how to write a client for the RESTful endpoints my DropWizard app will expose.
For the sake of this example, let's say my DropWizard app has a CarResource, which exposes a few simple RESTful endpoints for CRUDding cars:
#Path("/cars")
public class CarResource extends Resource {
// CRUDs car instances to some database (DAO).
public CardDao carDao = new CarDao();
#POST
public Car createCar(String make, String model, String rgbColor) {
Car car = new Car(make, model, rgbColor);
carDao.saveCar(car);
return car;
}
#GET
#Path("/make/{make}")
public List<Car> getCarsByMake(String make) {
List<Car> cars = carDao.getCarsByMake(make);
return cars;
}
}
So I would imagine that a structured API client would be something like a CarServiceClient:
// Packaged up in a JAR library. Can be used by any Java executable to hit the Car Service
// endpoints.
public class CarServiceClient {
public HttpClient httpClient;
public Car createCar(String make, String model, String rgbColor) {
// Use 'httpClient' to make an HTTP POST to the /cars endpoint.
// Needs to deserialize JSON returned from server into a `Car` instance.
// But also needs to handle if the server threw a `WebApplicationException` or
// returned a NULL.
}
public List<Car> getCarsByMake(String make) {
// Use 'httpClient' to make an HTTP GET to the /cars/make/{make} endpoint.
// Needs to deserialize JSON returned from server into a list of `Car` instances.
// But also needs to handle if the server threw a `WebApplicationException` or
// returned a NULL.
}
}
But the only two official references to Drop Wizard clients I can find are totally contradictory to one another:
DropWizard recommended project structure - which claims I should put my client code in a car-client project under car.service.client package; but then...
DropWizard Client manual - which makes it seem like a "DropWizard Client" is meant for integrating my DropWizard app with other RESTful web services (thus acting as a middleman).
So I ask, what is the standard way of writing Java API clients for your DropWizard web services? Does DropWizard have a client-library I can utilize for this type of use case? Am I supposed to be implementing the client via some Jersey client API? Can someone add pseudo-code to my CarServiceClient so I can understand how this would work?
Here is a pattern you can use using the JAX-RS client.
To get the client:
javax.ws.rs.client.Client init(JerseyClientConfiguration config, Environment environment) {
return new JerseyClientBuilder(environment).using(config).build("my-client");
}
You can then make calls the following way:
javax.ws.rs.core.Response post = client
.target("http://...")
.request(MediaType.APPLICATION_JSON)
.header("key", value)
.accept(MediaType.APPLICATION_JSON)
.post(Entity.json(myObj));
Yes, what dropwizard-client provides is only to be used by the service itself, most likely to communicate other services. It doesn't provide anything for client applications directly.
It doesn't do much magic with HttpClients anyway. It simply configures the client according to the yml file, assigns the existing Jackson object mapper and validator to Jersey client, and I think reuses the thread pool of the application. You can check all that on https://github.com/dropwizard/dropwizard/blob/master/dropwizard-client/src/main/java/io/dropwizard/client/JerseyClientBuilder.java
I think I'd go about and structure my classes as you did using Jersey Client. Following is an abstract class I've been using for client services:
public abstract class HttpRemoteService {
private static final String AUTHORIZATION_HEADER = "Authorization";
private static final String TOKEN_PREFIX = "Bearer ";
private Client client;
protected HttpRemoteService(Client client) {
this.client = client;
}
protected abstract String getServiceUrl();
protected WebResource.Builder getSynchronousResource(String resourceUri) {
return client.resource(getServiceUrl() + resourceUri).type(MediaType.APPLICATION_JSON_TYPE);
}
protected WebResource.Builder getSynchronousResource(String resourceUri, String authToken) {
return getSynchronousResource(resourceUri).header(AUTHORIZATION_HEADER, TOKEN_PREFIX + authToken);
}
protected AsyncWebResource.Builder getAsynchronousResource(String resourceUri) {
return client.asyncResource(getServiceUrl() + resourceUri).type(MediaType.APPLICATION_JSON_TYPE);
}
protected AsyncWebResource.Builder getAsynchronousResource(String resourceUri, String authToken) {
return getAsynchronousResource(resourceUri).header(AUTHORIZATION_HEADER, TOKEN_PREFIX + authToken);
}
protected void isAlive() {
client.resource(getServiceUrl()).get(ClientResponse.class);
}
}
and here is how I make it concrete:
private class TestRemoteService extends HttpRemoteService {
protected TestRemoteService(Client client) {
super(client);
}
#Override
protected String getServiceUrl() {
return "http://localhost:8080";
}
public Future<TestDTO> get() {
return getAsynchronousResource("/get").get(TestDTO.class);
}
public void post(Object object) {
getSynchronousResource("/post").post(object);
}
public void unavailable() {
getSynchronousResource("/unavailable").get(Object.class);
}
public void authorize() {
getSynchronousResource("/authorize", "ma token").put();
}
}
if anyone is trying to use DW 0.8.2 when building a client, and you're getting the following error:
cannot access org.apache.http.config.Registry
class file for org.apache.http.config.Registry not found
at org.apache.maven.plugin.compiler.AbstractCompilerMojo.execute(AbstractCompilerMojo.java:858)
at org.apache.maven.plugin.compiler.CompilerMojo.execute(CompilerMojo.java:129)
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:132)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:208)
... 19 more
update your dropwizard-client in your pom.xml from 0.8.2 to 0.8.4 and you should be good. I believe a jetty sub-dependency was updated which fixed it.
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-client</artifactId>
<version>0.8.4</version>
<scope>compile</scope>
</dependency>
You can integrated with Spring Framework to implement

RPC call - static methods is not working

I am trying to define a static method in the service interface to make an rpc call. But it doesn't allow me to do so. here I am pasting my code
Client class
public void sendDomesticData(String product,String dma,String yrmnths,String dist,String metrics) {
String url = GWT.getModuleBaseURL() + "domesticservice";
domesticServiceAsync = (DomesticServiceAsync) GWT.create(DomesticService.class);
ServiceDefTarget endpoint = (ServiceDefTarget) domesticServiceAsync;
endpoint.setServiceEntryPoint(url);
domesticServiceAsync.sendDomesticData(product,dma,yrmnths,dist,metrics,new Domestichandler<Void>() );
}
public class Domestichandler<Void> implements AsyncCallback<Void> {
#Override
public void onFailure(Throwable caught) {
String error = caught.getMessage();
System.out.println(error);
}
public void onSuccess(Void result) {
System.out.println("perfect");
}
}
Service
public interface DomesticService extends RemoteService {
public void sendDomesticData(String product,String dma,String yrmnths,String dist,String metrics);
}
public interface DomesticServiceAsync {
void sendDomesticData(String product,String dma,String yrmnths,String dist,String metrics,AsyncCallback<Void> callback);
}
Server side -
public void sendDomesticData(String product, String dma, String yrmnths, String dist, String metrics) {
System.out.println(product);
}
Basically I am trying to send the values from the front interface to the server side and I don't want any return value. But the values passed to the server side should be stored globally in the server class so i can access those values in different method. I tried changing all the senddomestic values to static but it won't allow me to do so? why?
Because RemoteServiceServlet needs to invoke your service methods somehow and the implementation expects instance methods. But this shouldn't prevent you from assigning the method data to static fields. Just be aware of multi threading.
GWT always uses instance methods for RPC calls, static methods are not possible in this case.
What is important to understand about GWT is that any RemoteServiceServlet instances are created and maintained by the servlet container (e.g. Tomcat). The servlet container might create a number of servlet instances on startup (Tomcat creates 6 RemoteServiceServlet instances by default) and then uses load balancing to determine which servlet handles an RPC request at a particular point in time. Depending on settings of course, you have little control over which RemoteServiceServlet instance exactly will handle a specific RPC request.
Therefore, if you want to store information on the server side globally using RPC calls, the idea proposed by YuPPie to use static fields of your RemoteServiceServlet implementation is a BAD idea. You will have no idea which of the RemoteServiceServlet instances maintained by the server contains your static data, and any subsequent calls to retrieve the data will give erratic results.
You have a few options, though. Storing the information in a database (or something similar) is the most straightforward option, but from your post I'm guessing you want something simpler. A singleton class which holds your data is probably the way to go. A thread-safe example:
public class DataContainer
{
private static DataContainer _singleton;
private String _dataField1;
public static synchronized DataContainer getInstance()
{
if (_singleton == null)
_singleton = new DataContainer();
return _singleton;
}
public synchronized String getDataField1()
{
return _dataField1;
}
public synchronized void setDataField1(String dataField1)
{
_dataField1 = dataField1;
}
}
Then in the server side implementation of your RPC call you could do something like:
public void sendDomesticData(String product, String dma, String yrmnths, String dist, String metrics)
{
DataContainer.getInstance().setDataField1(product);
}
This way, if there are multiple servlet instances they will all share the singleton instance of DataContainer, thus giving you a place to store your data globally. I hope this will help you.

Categories

Resources