Every request that my Java application receives, passes through 4 layers:
Handler --> Validator --> Processor --> DAO
Handler is the API Resource. (Handler.java)
Validator validates the input. (Validator.java)
Processor performs some business logic. (Processor.java)
DAO is the DB communication layer. (DAO.java)
The input request has a field called the request_type. Based on this request_type, I want to create different objects for all the layer classes, i.e:
request_type_A should pass through Handler1, Validator1, Processor1, DAO1 (instances)
request_type_B should pass through Handler2, Validator2, Processor2, DAO2 (instances)
request_type_C should pass through Handler3, Validator3, Processor3, DAO3 (instances).. and so on
To clarify, the requirement is to create different chain of objects for a given request type, so that two request having different request_type have entirely different object chain instances. Basically i want to shard my application's object based on a given request_type.
I am using Spring Boot. Is there a way that spring's ApplicationContext can provide different object chains for different object types. Or should I manage these instances by my own?
Is there a way I can create a library which would give me a new object instance for every layer, based on the request_type using Spring's ApplicationContext?
Or should i create multiple ApplicationContext?
Based on comments & question, I understand that you would be receiving 2 or 3 request_type.
So main idea which I have used here is to use constructor injection of chained objects with different configuration beans which will be used based on your request type.
Feel free to check-out this simple demonstration based code from github where I have proposed my idea : https://github.com/patilashish312/SpringObjectChaining
So based on this code, I can confirm that
This application is not creating chain of object per request but will re-use if same type of requests received by application
Objects assigned to one request type is not being used by other request type.
Below console output is proof :
displaying request MyRequest(id=1, name=Ashish, requestType=requestTypeA)
Printing handler bean com.spr.boot3.ConditionalVerification.Handler.MyHandler#31182e0a
Printing validator bean com.spr.boot3.ConditionalVerification.Validator.MyValidator#484e3fe7
Printing processor bean com.spr.boot3.ConditionalVerification.Processor.MyProcessor#70f9b9c7
Printing dao bean com.spr.boot3.ConditionalVerification.Dao.MyDao#2a8175d9
inside dao, doing DAO processing
displaying request MyRequest(id=1, name=Ashish, requestType=requestTypeA)
Printing handler bean com.spr.boot3.ConditionalVerification.Handler.MyHandler#31182e0a
Printing validator bean com.spr.boot3.ConditionalVerification.Validator.MyValidator#484e3fe7
Printing processor bean com.spr.boot3.ConditionalVerification.Processor.MyProcessor#70f9b9c7
Printing dao bean com.spr.boot3.ConditionalVerification.Dao.MyDao#2a8175d9
inside dao, doing DAO processing
displaying request MyRequest(id=1, name=Ashish, requestType=requestTypeB)
Printing handler bean com.spr.boot3.ConditionalVerification.Handler.MyHandler#55ea9008
Printing validator bean com.spr.boot3.ConditionalVerification.Validator.MyValidator#5b2d74c5
Printing processor bean com.spr.boot3.ConditionalVerification.Processor.MyProcessor#5f12fb78
Printing dao bean com.spr.boot3.ConditionalVerification.Dao.MyDao#1a107efe
inside dao, doing DAO processing
displaying request MyRequest(id=1, name=Ashish, requestType=requestTypeB)
Printing handler bean com.spr.boot3.ConditionalVerification.Handler.MyHandler#55ea9008
Printing validator bean com.spr.boot3.ConditionalVerification.Validator.MyValidator#5b2d74c5
Printing processor bean com.spr.boot3.ConditionalVerification.Processor.MyProcessor#5f12fb78
Printing dao bean com.spr.boot3.ConditionalVerification.Dao.MyDao#1a107efe
inside dao, doing DAO processing
I had a similar requirement in my solution. What I built was a general-purpose command handler, and used a decorator pattern of annotations on each command to provide the specification for which handlers, validators, processors, and dao.
In my implementation, I have API handlers which convert requests to specific commands. Command class was an subclass of an abstract command class with a generic type param.
API -> all API variables are copied into a wrapper data model. (This could encapsulate the entrypoint of your handler concept or request_type concept)
Command extends AbstractCommand where T is the wrapper data model.
Then I would have an annotation for each of your concepts: Handler, Validator, Processor, Dao.
The general purpose command handler would have a method that "process"es commands by reading their annotations and then lining up the annotation helper that corresponds to that annotation. This could use the application context to load the bean of the class referenced in the annotation value. By providing a sequencing property for each of the annotation helpers you could loop over the sorted helpers to perform actions in the right order.
In my implementation this was further augmented by whether or not the command included asynchronous behavior, so that all the synchronous behavior would occur first, and the asychronous behavior would be wrapped in a background thread.
The beans that are injected in the rest controller don't vary with the http request content. What you can do is factor your request_type as a path variable and create the desired chains in separate http mappings like so:
#PostMapping(value = "/request_type_A")
public Object handle1(args...){
// Validator1 --> Processor1 --> DAO1
}
#PostMapping(value = "/request_type_B")
public Object handle2(args...){
// Validator2 --> Processor2 --> DAO2
}
If this is not practical for whatever reason and you must specify the type dynamically in the #RequestBody, #PathVariable or #RequestParam, then I would suggest implementing a resolver bean similar to this:
#Component
public class Resolver {
private final RequestTypeAValidator requestTypeAValidator;
private final RequestTypeBValidator requestTypeBValidator;
...
public IValidator getValidator(String requestType) {
switch (requestType) {
case "request_type_A":
return requestTypeAValidator;
case "request_type_B":
return requestTypeBValidator;
default:
throw new IllegalArgumentException("cannot find validator");
}
}
}
The drawback of this approach is that it does not comply with the "Open-Closed" principle in the sense that for any new request type, you will need to edit the resolvers. That can be fixed by using a HashMap in the resolver and letting the beans register themselves to that map on #PostConstruct:
#Component
public class Resolver {
private final Map<String, IValidator> validators = new HashMap<>();
public IValidator getValidator(String requestType) {
IValidator result = validators.get(requestType);
if (Objects.isNull(result)) {
throw new IllegalArgumentException("cannot find validator");
}
return result;
}
public void register(String type, IValidator validator) {
validators.put(type, validator)
}
}
#Component
public class ValidatorA implements IValidator {
private final Resolver resolver;
#PostConstruct
private void register() {
resolver.register("request_type_A", this);
}
...
}
However, in this approach there is a direct dependency from all implementations back to the Resolver.
Lastly, you could inject dynamically like so:
#Component
public class Resolver {
private final ApplicationContext applicationContext;
...
public IValidator getValidator(String requestType) {
switch (requestType) {
case "request_type_A":
try {
return applicationContext.getBean(ValidatorA.class);
} catch (NoSuchBeanDefinitionException e) {
// handle exception
}
case "request_type_B":
try {
return applicationContext.getBean(ValidatorB.class);
} catch (NoSuchBeanDefinitionException e) {
// handle exception
}
default:
throw new IllegalArgumentException("cannot find validator");
}
}
}
Note: Avoid taking the client specified string as the class name or type directly in the applicationContext.getBean() call. That is not safe and may present a great security vulnerability, use a switch or dictionary to resolve the correct bean name or bean type.
If you want to inject multiple instances of the same classes, create a configuration class and declare the beans like this:
#Configuration
public class BeanConfiguration {
#Bean
public IValidator aValidator(){
return new ValidatorImpl(...);
}
#Bean
public IValidator bValidator(){
return new ValidatorImpl(...);
}
}
And then to inject it, you can either use the dynamic resolution by name as above, or use the #Qualifier annotation:
#Service
public class MyService {
private final ApplicationContext applicationContext;
private final IValidator bValidator;
public MyService(ApplicationContext applicationContext, #Qualifier("bValidator") IValidator bValidator) {
this.applicationContext = applicationContext;
this.bValidator = bValidator;
}
public void getDynamically(){
IValidator aValidator = (IValidator)applicationContext.getBean("aValidator");
}
}
I have created a custom channel in Spring cloud stream with custom inputs and outputs. Let's suppose this is the created channel:
public interface Channel {
String FOO = "foo-request";
String BAR = "bar-response";
#Input(FOO)
SubscribableChannel fooRequest();
#Output(BAR)
MessageChannel barResponse();
}
Something.java:
public class Something{
#Autowired
private Channel channel;
public void doSomething(..){
// Do some steps
channel.barRequest().send(MessageBuilder.withPayload(outputMessage).build())
}
}
As it can be seen I am injecting the custom channel in the Something class to send the message at the end of a method.
When I would like to test this method, I am having some issues with the injection done in the Something class. I cannot inject the Something class because it's not a component. But this class injects a Channel object as it can be seen. So here is what I have done to pass the limitation of injecting an internal property for this class:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {MyChannel.class})
public class SomethingTest{
#Autowired
private Channel myChannel;
#Test
public void TestDoSomething(){
// cannot inject it as it does not have any qualified bean
Something something = new Something();
ReflectionTestUtils.setField(something, "channel", channel);
}
#EnableBinding(Channel.class)
public static class MyChannel {
}
}
Without the ReflectionTestUtls line, I am getting a NullPointerException on channel.barRequest().send() in the doSomething method. With having this line to pass the injected object, I am getting the following error:
org.springframework.messaging.MessageDeliveryException: Dispatcher has no subscribers for channel 'application.bar-response'.; nested exception is org.springframework.integration.MessageDispatchingException: Dispatcher has no subscribers
First of all, I am not sure if what I am doing is the best way of dealing with my custom channel and testing the corresponding method, so please let me know if there is a better way. Second, why am I getting this exception and how I can address it?
P.S: I have already set the required configurations in my application.yml file for the test related to the binders and channels in a similar way that I have been doing with running the application in a normal way. This approach has been working so far with other properties.
the first thing noticing is you try to send to your Input Channel which typically recevies the messages.
Try changing it to channel.barResponse().send(...)
Other then that i am facing the same issue, but with a more complicated use-case. We have already definied our rabbit queues.
I am trying to configure Camel using Spring using only annotations and straight up Java. Here is a route I have created:
#Produce(uri = "activemq:my.route")
ProducerTemplate producer;
#RequestMapping(value = "/test", method = RequestMethod.POST)
public void testCamel()
{
producer.sendBody("TEST");
}
A camel context is needed, which I am trying to define in my AppConfig class:
My current version:
#Autowired
private ApplicationContext applicationContext;
#Bean
public CamelContext camelContext() throws Exception {
SpringCamelContext springCamelContext = new SpringCamelContext(applicationContext);
springCamelContext.addRoutes(new com.bigideas.routing.Routes());
return springCamelContext;
}
The Routes class is just an empty class that extends RouteBuilder.
The problem is that when I actually call the testCamel method, my producer is null. I know I can do the context in an xml configuration file, I was just wondering what I am doing wrong when not using xml.
I believe you should follow instructions here: http://camel.apache.org/spring-java-config.html , so one #Configuration class extending CamelConfiguration with #ComponentScan.
Moreover your com.bigideas.routing.Routes should be w Spring Bean too (#Bean or #Component)
Use camel 2.18 and spring-boot, camel context build-up and unit-testing has been made very easy.
take a look at this example
https://github.com/RakeshBhat/rb-camelservlet218-xamples
The class that extends routebuilder should be annotated with #Component. Also in the Spring Controller class, you need to auto-wire Camel Context and then use create producer/consumer templates. make sure you call start/stop methods of template, its so resource are used efficiently.
I want to have a service in my Spring application that watches a directory for changes using the Java 7 WatchService. The idea is that when a file in the directory is changed, clients connected via WebSockets are notified.
How do I get a bean to run in its own thread as a service?
What you are looking for is Asynchronous execution. With a correctly configured context (see the link), you declare a class like so
#Component
public class AsyncWatchServiceExecutor {
#Autowired
private WatchService watchService; // or create a new one here instead of injecting one
#Async
public void someAsyncMethod() {
for (;;) {
// use WatchService
}
}
}
Everything you do in someAsyncMethod() will happen in a separate thread. All you have to do is call it once.
ApplicationContext context = ...; // get ApplicationContext
context.getBean(AsyncWatchServiceExecutor.class).someAsyncMethod();
Use the WatchService as described in the Oracle documentation.
If you don't have direct access to your ApplicationContext, you can inject the bean in some other bean and call it in a #PostConstruct method.
#Component
public class AsyncInitializer {
#Autowired
private AsyncWatchServiceExecutor exec;
#PostConstruct
public void init() {
exec.someAsyncMethod();
}
}
Careful which proxying strategy (JDK or CGLIB) you use.
I'm a Tapestry5 user and wondering how I would #Inject a service class with a few arguments. From my understanding, using #Inject to inject a service class is very similar to instantiating a class with new MyClass();. The problem I seem to be having is I'm not sure how to pass the arguments into the service.
Example
Using Tapestry Servce
public class Main {
#Inject
private MyService myService;
public Main() {
//Where would I pass in my arguements?
this.myService();
//I can't seem to do this by passing the arg's in through
//this.myService(arg1, arg2) unless I may be missing something.
}
}
Traditional Usage
public class Main {
public Main() {
//In this example I can pass my arg's into the constructor.
MyService myService = new MyService(arg1, arg2);
}
}
You are not quite right in assuming that #Inject is similar to instantiation. You might somewhat argue this when your service is annotated with #Scope(ScopeConstants.PERTHREAD) but even then, tapestries IoC will instantiate the service for you. I find that most of my services are instantiated only once by tapestry and #Inject'ing them gives me a reference to this service. If you want to #Inject a service you will first need to define it with your AppModule. The simplest way to make your service available though the IoC is to bind it like so in your AppModule:
public static void bind(ServiceBinder binder) {
binder.bind(ServiceInterface.class, ServiceImplementation.class)
}
Then in your pages/components you can #Inject the interface like:
#Inject
private ServiceInterface service;
If your service then needs constructor arguments, you can create a constructor in your ServiceImplementation.class taking your required arguments. If those arguments are in themselves bound services, tapestry will figure this out and you're done. If these arguments are not services known to Tapetsry and you can't bind them for whatever reason, you can create a build method in your AppModule:
/**
* These methods may in them selves take bound services as arguments helping you build your new service
*/
public ServiceInterface buildServiceInterface(AnotherBoundService service2) {
...
return new ServiceImplementation(service2, someMoreArgsIfRequired)
}
Might you not want to use the IoC, you can always just instantiate the service in your page/component because they are just simple pojo's. Do have a look at the IoC documentation. It nicely outlines all powerful features at your disposal.