Calling Spring controller method without going to internet - java

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;
}

Related

Sending Spring domain events using a custom ApplicationEventPublisher

I'm trying to implement an application using Spring Boot/Spring Data, following DDD architecture guidelines. I have an Aggregate Root which publish domain events using the method AbstractAggregateRoot::registerEvent() . Furthermore, I need to intercept those events for Logging/Tracing purposes so I decided to make an experiment:
First, implement a custom ApplicationEvent Publisher
public class CustomEventPublisher implements ApplicationEventPublisher {
private final ApplicationEventPublisher publisher;
private final Logger logger = getLogger(CustomEventPublisher.class);
public CustomEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
#Override
public void publishEvent(ApplicationEvent event) {
logger.info("sending an event...");
publisher.publishEvent(event);
}
//.....
}
And then registering as bean
#Configuration
public class CustomEventPublisherConfig {
#Bean
#Primary
public ApplicationEventPublisher getCustomEventPublisher(ApplicationEventPublisher publisher , RabbitTemplate rabbitTemplate) {
return new CustomEventPublisher(publisher, rabbitTemplate);
}
}
this works fine once I explicitly publish events from some sample object with an injected ApplicationEventPublisher
public void pub() {
publisher.publishEvent(new Event(this , 1));
}
#EventListener
public void sub(Event e) {
this.value = e.getValue();
}
and I got the "sending an event..." log entry
then I've tried to define the aggregate root
#Entity
public class AggregateRoot extends AbstractAggregateRoot {
#Id
#GeneratedValue
private Long id;
private int value = 0;
public AggregateRoot setValue(int value) {
registerEvent(new Event(this , value));
return this;
}
}
and
public void pub() {
repository.save(new AggregateRoot().setValue(1));
}
Test pass again but I can clearly see that Spring Data is not using the CustomEventPublisher. I've tried to understand if there is some way to intercept repository.save() call and override the default behaviour, this approach could work even if needs to reinvent the wheel (I don't think that the domain event publishing code is so complicated though) but the only thing I've found is about Spring Data REST that is out of my scope
Any suggestion to overcome this problem?
Thanks in advance
As far as I know, Spring does not provide a way to replace the publisher used by EventPublishingRepositoryProxyPostProcessor. And it seems to me that you have chosen not quite the right path to get what you want to achieve, so I am not answering your direct question, but your requirements described in the beginning.
I would advise you to register a listener using #EventListener and handle your event there:
#EventListener
public void handleEvent(Event event) {
System.out.println(event);
}
Or you can use #TransactionalEventListener to bind the listener to a transaction phase:
#TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
public void handleEvent(Event event) {
System.out.println(event);
}

How to create multiple instances of osgi service without using DS annotations

I have created an Osgi service. I want to create a new instance of my service each time the service request comes.
Code look likes this -
#Component(immediate=true)
#Service(serviceFactory = true)
#Property(name = EventConstants.EVENT_TOPIC, value = {DEPLOY, UNDEPLOY })
public class XyzHandler implements EventHandler {
private Consumer consumer;
public static setConsumer(Consumer consumer) {
this.consumer = consumer;
}
#Override
public void handleEvent(final Event event) {
consumer.notify();
}
}
public class Consumer {
private DataSourceCache cache;
public void notify() {
updateCache(cache);
System.out.println("cache updated");
}
public void updateCache(DataSourceCache cache) {
cache = null;
}
}
In my Consumer class, I want to access the service instance of XyzHandler & set the attribute consumer. Also I would like to have a new service instance of XyzHandler created every time for each request.
I found few articles where it is mentioned that using osgi declarative service annotations this can be achieved.
OSGi how to run mutliple instances of one service
But I want to achieve this without using DS 1.3.
How can I do this without using annotations or how can it be done using DS 1.2?
To me this looks like a case of having asked a question based on what you think the answer is rather than describing what you're trying to achieve. If we take a few steps back then a more elegant solution exists.
In general injecting objects into stateful services is a bad pattern in OSGi. It forces you to be really careful about the lifecycle, and risks memory leaks. From the example code it appears as though what you really want is for your Consumer to get notified when an event occurs on an Event Admin topic. The easiest way to do this would be to remove the XyzHandler from the equation and make the Consumer an Event Handler like this:
#Component(property= { EventConstants.EVENT_TOPIC + "=" + DEPLOY,
EventConstants.EVENT_TOPIC + "=" + UNDEPLOY})
public class Consumer implements EventHandler {
private DataSourceCache cache;
#Override
public void handleEvent(final Event event) {
notify();
}
public void notify() {
updateCache(cache);
System.out.println("cache updated");
}
public void updateCache(DataSourceCache cache) {
cache = null;
}
}
If you really don't want to make your Consumer an EventHandler then it would still be easier to register the Consumer as a service and use the whiteboard pattern to get it picked up by a single XyzHandler:
#Component(service=Consumer.class)
public class Consumer {
private DataSourceCache cache;
public void notify() {
updateCache(cache);
System.out.println("cache updated");
}
public void updateCache(DataSourceCache cache) {
cache = null;
}
}
#Component(property= { EventConstants.EVENT_TOPIC + "=" + DEPLOY,
EventConstants.EVENT_TOPIC + "=" + UNDEPLOY})
public class XyzHandler implements EventHandler {
// Use a thread safe list for dynamic references!
private List<Consumer> consumers = new CopyOnWriteArrayList<>();
#Reference(cardinality=MULTIPLE, policy=DYNAMIC)
void addConsumer(Consumer consumer) {
consumers.add(consumer);
}
void removeConsumer(Consumer consumer) {
consumers.remove(consumer);
}
#Override
public void handleEvent(final Event event) {
consumers.forEach(this::notify);
}
private void notify(Consumer consumer) {
try {
consumer.notify();
} catch (Exception e) {
// TODO log this?
}
}
}
Using the whiteboard pattern in this way avoids you needing to track which XyzHandler needs to be created/destroyed when a bundle is started or stopped, and will keep your code much cleaner.
It sounds like your service needs to be a prototype scope service. This was introduced in Core R6. DS 1.3, from Compendium R6, includes support for components to be prototype scope services.
But DS 1.2 predates Core R6 and thus has no knowledge or support for prototype scope services.

Best practice to 'rollback' REST method calls inside method

The title might be incorrect, but I will try to explain my issue. My project is a Spring Boot project. I have services which do calls to external REST endpoints.
I have a service method which contains several method calls to other services I have. Every individual method call can be successful or not. Every method call is done to a REST endpoint and there can be issues that for example the webservice is not available or that it throws an unknown exception in rare cases. What ever happens, I need to be able to track which method calls were successful and if any one of them fails, I want to rollback to the original state as if nothing happened, see it a bit as #Transactional annotation. All REST calls are different endpoints and need to be called separately and are from an external party which I don't have influence on. Example:
public MyServiceImpl implements MyService {
#Autowired
private Process1Service;
#Autowired
private Process2Service;
#Autowired
private Process3Service;
#Autowired
private Process4Service;
public void bundledProcess() {
process1Service.createFileRESTcall();
process2Service.addFilePermissionsRESTcall();
process3Service.addFileMetadataRESTcall(); <-- might fail for example
process4Service.addFileTimestampRESTcall();
}
}
If for example process3Service.addFileMetadataRESTcall fails I want to do something like undo (in reverse order) for every step before process3:
process2Service.removeFilePermissionsRESTcall();
process1Service.deleteFileRESTcall();
I read about the Command pattern, but that seems to be used for Undo actions inside an application as a sort of history of actions performed, not inside a Spring web application. Is this correct for my use case too or should I track per method/webservice call if it was successful? Is there a best practice for doing this?
I guess however I track it, I need to know which method call failed and from there on perform my 'undo' method REST calls. Although in theory even these calls might also fail of course.
My main goal is to not have files being created (in my example) which any further processes have not been performed on. It should either be all successful or nothing. A sort of transactional.
Update1: improved pseudo implementation based on comments:
public Process1ServiceImpl implements Process1Service {
public void createFileRESTcall() throws MyException {
// Call an external REST api, pseudo code:
if (REST-call fails) {
throw new MyException("External REST api failed");
}
}
}
public class BundledProcessEvent {
private boolean createFileSuccess;
private boolean addFilePermissionsSuccess;
private boolean addFileMetadataSuccess;
private boolean addFileTimestampSuccess;
// Getters and setters
}
public MyServiceImpl implements MyService {
#Autowired
private Process1Service;
#Autowired
private Process2Service;
#Autowired
private Process3Service;
#Autowired
private Process4Service;
#Autowired
private ApplicationEventPublisher applicationEventPublisher;
#Transactional(rollbackOn = MyException.class)
public void bundledProcess() {
BundleProcessEvent bundleProcessEvent = new BundleProcessEvent();
this.applicationEventPublisher.publishEvent(bundleProcessEvent);
bundleProcessEvent.setCreateFileSuccess = bundprocess1Service.createFileRESTcall();
bundleProcessEvent.setAddFilePermissionsSuccess = process2Service.addFilePermissionsRESTcall();
bundleProcessEvent.setAddFileMetadataSuccess = process3Service.addFileMetadataRESTcall();
bundleProcessEvent.setAddFileTimestampSuccess = process4Service.addFileTimestampRESTcall();
}
#TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
public void rollback(BundleProcessEvent bundleProcessEvent) {
// If the last process event is successful, we should not
// be in this rollback method even
//if (bundleProcessEvent.isAddFileTimestampSuccess()) {
// remove timestamp
//}
if (bundleProcessEvent.isAddFileMetadataSuccess()) {
// remove metadata
}
if (bundleProcessEvent.isAddFilePermissionsSuccess()) {
// remove file permissions
}
if (bundleProcessEvent.isCreateFileSuccess()) {
// remove file
}
}
Your operation looks like a transaction, so you can use #Transactional annotation. From your code I can't really tell how you are managing HTTP response calls for each of those operations, but you should consider having your service methods to return them, and then do a rollback depending on response calls. You can create an array of methods like so, but how exactly you want your logic to be is up to you.
private Process[] restCalls = new Process[] {
new Process() { public void call() { process1Service.createFileRESTcall(); } },
new Process() { public void call() { process2Service.addFilePermissionsRESTcall(); } },
new Process() { public void call() { process3Service.addFileMetadataRESTcall(); } },
new Process() { public void call() { process4Service.addFileTimestampRESTcall(); } },
};
interface Process {
void call();
}
#Transactional(rollbackOn = Exception.class)
public void bundledProcess() {
restCalls[0].call();
... // say, see which process returned wrong response code
}
#TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
public void rollback() {
// handle rollback according to failed method index
}
Check this article. Might come in handy.
The answer to this question is quite broad. There are various ways to do distributed transactions to go through them all here. However, since you are using Java and Spring, your best bet is to use something like JTA (Java Transaction API), which enables a distributed transactions across multiple services/instances/etc.. Fortunately, Spring Boot supports JTA using either Atomikos or Bitronix. You can read the doc here.
One approach to enable distributed transactions is through a message broker such as JMS, RabbitMQ, Kafka, ActiveMQ, etc. and use a protocol like XA transactions (two-phase commit). In the case of external services that do not support distributed, one approach is to write a wrapper service that understands XA transactions to that external service.

How can I get annotations on rpc method being called in grpc-java

I need to validate request before different rpc methods being called with different validators.
So I implemented validators like
class BarRequestValidator {
public FooServiceError validate(BarRequest request) {
if (request.bar.length > 12) {
return FooServiceError.BAR_TOO_LONG;
} else {
return null;
}
}
}
and add a custom annotation before my rpc method
class FooService extends FooServiceGrpc.FooServiceImplBase {
#Validated(validator = BarRequestValidator.class)
public void bar(BarRequest request, StreamObserver<BarResponse> responseObserver) {
// Validator should be executed before this line, and returns error once validation fails.
assert(request.bar <= 12);
}
}
But I found that I can't find a way to get annotation information in gRPC ServerInterceptor. Is there any way to implement grpc request validation like this?
You can accomplish this without having the annotation at all, and just using a plain ServerInterceptor:
Server s = ServerBuilder.forPort(...)
.addService(ServerInterceptors.intercept(myService, myValidator))
...
private final class MyValidator implements ServerInterceptor {
ServerCall.Listener interceptCall(call, headers, next) {
ServerCall.Listener listener = next.startCall(call, headers);
if (call.getMethodDescriptor().getFullMethodName().equals("service/method")) {
listener = new SimpleForwardingServerCallListener(listener) {
#Override
void onMessage(request) {
validate(request);
}
}
}
return listener;
}
}
Note that I'm skipping most of the boilerplate here. When a request comes in, the interceptor gets it first and checks to see if its for the method it was expecting. If so, it does extra validation. In the generated code you can reference the existing MethodDescriptors rather than copying the name out like above.

Play! - how to reuse common logic for path fragment

I am currently using Play 2.3 and I have to deal with similar URL mappings:
GET /api/companyId/employeeId/tasks
GET /api/companyId/employeeId/timesheets
etc.
In every GET I need to perform similar logic:
public Promise<Result> getEmployeeTimesheets(Long companyId, Long employeeId) {
return promise(() -> {
if (!companyRepository.one(companyId).isPresent()) {
return notFound("Company doesn't exist");
}
if (!employeeRepository.one(employeeId).isPresent()) {
return notFound("Employee doesn't exist");
}
if (!employeeRepository.employeeWorksForCompany(companyId, employeeId)) {
return forbidden("Employee doesn't work for this company");
}
// some actual logic here
});
}
This code repeats over and over again. So far I used plain old inheritance and moved that repeating code into the parent controller class. It gets the job done, but it certainly isn't perfect solution (because I have to invoke parent method and inspect results manually in every controller action).
Is there some more declarative approach in Play that would automatically handle fragment of URL (/api/companyId/employeeId in our case) and either delegate the execution to an appropriate controller, or return an error response (for example 404 - Not Found).
You said you are calling the method again and again in each controller function instead you can use #With annotation.For ex
create a class CheckUrl.java
public class CheckUrl extends play.mvc.Action.Simple {
public F.Promise<SimpleResult> call(Http.Context ctx) throws Throwable {
String host = request().uri();
if (condition one satisfied) {
return F.Promise.pure(redirect("/someurl"));
}else if (condition two satisfied){
return F.Promise.pure(redirect(controllers.routes.SomeController.index()));
}
}
Place #With(CheckUrl.class) in class to apply to all its function.
#With(CheckUrl.class)
public class MyController extends Controller {
}
and for a particular function
public class MyController extends Controller {
#With(CheckUrl.class)
public static Result index() {
}
}
In the above cases CheckUrl.java is invoked before function in a controller

Categories

Resources