I have recently started looking into Spring Integration as a result of this question and have a question about object-type based routing.
The application I am maintaining needs to process requests from an incoming ActiveMQ queue (request.queue) and send a response back to the caller on a topic (response.topic). At present the requests are structured as follows:
public abstract class Request {
// base class
}
public abstract class CustomerRequest extends Request {
// base class for customer-specific requests
}
public class FindCustomerByIdRequest extends CustomerRequest {
private int id;
}
public class FindAllCustomersRequest extends CustomerRequest {
private boolean includeArchivedCustomers;
}
public class AddCustomerRequest extends CustomerRequest {
private String name;
private Date signupDate;
private Address address;
}
I have a service for each high level domain object which provides the functionality to service these incoming requests:
#Service
public class CustomerService {
public CustomerResponse findCustomerById(FindCustomerByIdRequest request) {
// code snipped
return customerResponse;
}
public AddCustomerResponse addCustomer(AddCustomerRequest request) {
// code snipped
return addCustomerResponse;
}
}
I need to route each specific request to the approriate method in CustomerService via #ServiceActivator which I understand can be done by creating a separate channel for each request and implementing a PayloadTypeRouter to place requests on the correct channel based on type.
Over time the list of request types is going to grow, and I am questioning whether a one-channel-per-request setup is efficient/practical/scalable. For example, if there are 100 different request types in the future there are going to be 100 different channels.
What would be great is if I could route the high-level requests of superclass CustomerRequest to CustomerService and have Spring work out the approriate method to call via an annotation or some other mechanism. Does anyone know if this is possible, or have any comments regarding the many-channels approach?
If there is no ambiguity, use <service-activator ... reg="fooBean" /> (no method) and the framework will chose the target method based on the payload.
If there is ambiguity (more than one method for the same type), it will fail.
However, a single class with 100+ methods is probably not a good design.
It seems to be that your request types are app feature specific. This suggest that you have one queue for all the possible application features. That is horrible idea. You should have at least separate queue per feature.
I suggest to rethink the design of your app.
Related
I am currently reevaluating a graphql stack at work. We iterate fairly quickly on our internal graphql API and the iterations get more and more painful to manage, especially for the consumers, due to breaking changes in the API. Therefore, I would like to pursue a code-first approach in java. By that I mean I want to share the classes for our domain model between the server side and client side. In addition, I want one or more java interfaces to define a set of public methods which returns objects of our domain model classes. The purpose of the interfaces is to make a contract or public API between the server and client to ensure both sides implements the contract. Our graphql API is the main product but I want to hide the API slightly and rather expose a java client.
Main objective: How to obtain a versioned graphql API hosted by a versioned server and a versioned reference client in java for customers of the graphql API. On new releases of the graphql API I want to be able to release a corresponding client and domain model.
So far i have been playing around with the Quarkus extensions quarkus-smallrye-graphql and quarkus-smallrye-graphql-client.
Interface
#GraphQLClientApi(configKey = "star-wars-typesafe")
public interface StarWarsClientApi {
List<Film> allFilms();
}
Client
#Path("/")
#ApplicationScoped
public class StarWarsResource {
#Inject
StarWarsClientApi client;
#GET
#Path("/typesafe")
#Produces(MediaType.APPLICATION_JSON)
#Blocking
public List<Film> getAllFilmsUsingTypesafeClient() {
return client.allFilms();
}
}
Server
#GraphQLApi
public class FilmResource implements StarWarsClientApi{
#Inject
GalaxyService service;
#Query()
#Description("Get all Films from a galaxy far far away")
public List<Film> allFilms() {
return service.getAllFilms();
}
}
Then I run into a AmbiguousResolutionException. Which I guess makes sense, even for me. Afterwards, I tried to define a Qualifier called #Client. Now I am facing a UnsatisfiedResolutionException. And by now my knowledge and creativity stops in regards to CDI.
Anyone who can pinpoint what I am doing wrong or suggest an alternative approach to achieve the main objective from above?
Interface
#GraphQLClientApi(configKey = "star-wars-typesafe")
#Client
public interface StarWarsClientApi {
List<Film> allFilms();
}
Client
#Path("/")
#ApplicationScoped
public class StarWarsResource {
#Inject #Client
StarWarsClientApi client;
#GET
#Path("/typesafe")
#Produces(MediaType.APPLICATION_JSON)
#Blocking
public List<Film> getAllFilmsUsingTypesafeClient() {
return client.allFilms();
}
}
Consider I have a bunch of data processors extending the same interface :
IProcessor.java
public interface IProcessor() {
void processName(String name);
}
BookProcessor.java
public class BookProcessor implements IProcessor {
#Override
public void processName(String name){
//Process name in book specific logic
}
}
MagazineProcessor.java
public class MagazineProcessor implements IProcessor {
#Override
public void processName(String name){
//Process name in magazine specific logic
}
}
All of these processors reside in a client, say MyClient and this client receives data from a service, say MyService.
I want to implement namespacing so that from this namespacing, MyClient can pick up the correct processor automatically. For this I was thinking MyService itself should send namespaced data (maybe namespaced id). My question how do I implement this namesapcing so that both MyService and MyClient are sharing it even when MyService has no knowledge about the processors that are present in MyClient.
When MyService sends data to MyClient, it has to provide a hint on what type of data it's sending. So that MyClient can pick up the right processor for this type of data.
So the right approach would be:
Make a contract between a client and the server which states what type of data the server can send. And then, on the client side, you should implement a matcher, which matches a data type to its processor.
So that MyService does not know anything about data processors a client can use. It just knows what it's sending.
I am maintaining an existing application which receives requests from ActiveMQ and sends responses back to the sender via an ActiveMQ topic. At present there is a single message consumer class which receives messages via a simple DefaultMessageListenerContainer:
#Component
public class RequestConsumer {
#Autowired
CustomerService customerService;
#Autowired
JmsSenderService jmsSenderService;
public void handleMessage(Message message) {
if (message instanceof CustomerRequest) {
CustomerRequest customerRequest = (CustomerRequest) message;
Customer customer = customerService.getCustomerById(customerRequest.getId());
CustomerResponse customerResponse = new CustomerResponse();
customerResponse.addCustomer(customer);
jmsSenderService.sendCustomerResponse(customerResponse);
}
}
}
I need to extend the application to process a number of different requests (e.g. OrderRequest, InvoiceRequest, InventoryRequest, AddressRequest etc.) and send an appropriate response back to the sender. My first thought was to add the functionality to the existing class like so:
public void handleMessage(Message message) {
if (message instanceof CustomerRequest) {
// deal with CustomerRequest
} else if (message instanceof InvoiceRequest) {
// deal with InvoiceRequest
} else if (message instanceof InventoryRequest) {
// deal with InventoryRequest
}
}
However this will make the class quite large. I also thought about implementing one queue per request type (e.g. customer.request.queue, invoice.request.queue) and implementing multiple DefaultMessageListenerContainer, one per queue, but this doesn't seem like a great idea either because of the multiple boilerplated classes I'd need to create.
I feel like there must be a way to implement some kind of routing based on the type of incoming object and map it to an object-specific implementation to process the request, but I'm not sure whether this is something Spring/JMS already provides.
Has anyone done something like this before, and if so is there a "Springy" way to do it?
If you are willing to invest some time (as this solution comes with an initial time-investment) then there is an absolutely Spring-conform solution:
Spring Integration
This library (a good JMS example here) has prebuilt and well-tested solutions for all common messaging issues you might face, including the problem you just described above (message-type based routing).
Does anybody know how to follow this recommendation at [camel pojo producing][1]:
We recommend Hiding Middleware APIs from your application code so the
next option might be more suitable. You can add the #Produce
annotation to an injection point (a field or property setter) using a
ProducerTemplate or using some interface you use in your business
logic. e.g.
public interface MyListener {
String sayHello(String name);
}
public class MyBean {
#Produce(uri = "activemq:foo")
protected MyListener producer;
public void doSomething() {
// lets send a message
String response = producer.sayHello("James");
}
}
Here Camel will automatically inject a smart client side proxy at the
#Produce annotation - an instance of the MyListener instance. When we
invoke methods on this interface the method call is turned into an
object and using the Camel Spring Remoting mechanism it is sent to the
endpoint - in this case the ActiveMQ endpoint to queue foo; then the
caller blocks for a response.
but with headers, in order to work with this line of DSL:
from("jms:activemq:toBeStored").recipientList(header("stores").tokenize(",")).parallelProcessing().ignoreInvalidEndpoints();
which I have successfully tested with the method of #EndpointInject
public class Foo {
#EndpointInject(uri="activemq:foo.bar")
ProducerTemplate producer;
public void doSomething() {
if (whatever) {
producer.sendBodyAndHeader("<hello>world!</hello>", "stores","store1");
}
}
}
like as described before the recommendation.
In brief: how can I make the injected smart client side proxy send also the headers I want?
thanks
I want to create a base controller class that my other controllers will inherit from. I have a simple public api that takes the authentication token via the query string, so I want to do this:
public class MyBaseController {
private String token = "";
public MyBaseController() {
}
}
And then my real controller would be like:
#Controller
#RequestMapping("/api/users")
public class UserController extends MyBaseControler {
// controller methods here
}
My question is, how can i get access to the HttpServletRequest in my base controller, and get the querystring parameter "?token=abc123" value and set the token var with the value.
Is this thread safe? It is my understanding that there will be a new controller instance per request correct?
Your controllers are better off if they're stateless.
You can inject them with Spring services as needed, but I don't see any reason why they have to hang onto the value of the token as a member variable.
It's far more likely that you'll want to store the token in session scope. I think your idea is wrong-headed.
I'll point out that Spring itself has moved away from inheritance for controllers. They're all annotation-based now, with no common base class or interface. Why do you think devolving back to the design they abandoned is a good thing?
You don't need a base controller, either.