Play! - how to reuse common logic for path fragment - java

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

Related

Create bean instance at runtime for interface

i am kind of stuck on a problem with creating beans, or probably i got the wrong intention.. Maybe you can help me solve it:
I got a application which takes in requests for batch processing. For every batch i need to create an own context depending on the parameters issued by the request.
I will try to simplyfy it with the following example:
I receive a request to process in a batch FunctionA which is a implementation for my Function_I interface and has sub-implementation FunctionA_DE and FunctionA_AT
Something like this:
public interface Function_I {
String doFunctionStuff()
}
public abstract class FunctionA implements Function_I {
FunctionConfig funcConfig;
public FunctionA(FunctionConfig funcConfig) {
this.funcConfig = funcConfig;
}
public String doFunctionStuff() {
// some code
String result = callSpecificFunctionStuff();
// more code
return result;
}
protected abstract String callSpecificFunctionStuff();
}
public class FunctionA_DE extends FunctionA {
public FunctionA_DE(FunctionConfig funcConf) {
super(funcConf)
}
protected String callSpecifiFunctionStuff() {
//do some specificStuff
return result;
}
}
public class FunctionA_AT extends FunctionA {
public FunctionA_AT(FunctionConfig funcConf) {
super(funcConf)
}
protected String callSpecifiFunctionStuff() {
//do some specificStuff
return result;
}
}
what would be the Spring-Boot-Way of creating a instance for FunctionA_DE to get it as Function_I for the calling part of the application, and what should it look like when i add FunctionB with FunctionB_DE / FunctionB_AT to my classes..
I thought it could be something like:
PSEUDO CODE
#Configuration
public class FunctionFactory {
#Bean(SCOPE=SCOPE_PROTOTYPE) // i need a new instance everytime i call it
public Function_I createFunctionA(FunctionConfiguration funcConfig) {
// create Function depending on the funcConfig so either FunctionA_DE or FunctionA_AT
}
}
and i would call it by Autowiring the FunctionFactory into my calling class and use it with
someSpringFactory.createFunction(functionConfiguration);
but i cant figure it out to create a Prototype-Bean for the function with passing a parameter.. And i cant really find a solution to my question by browsing through SO, but maybe i just got the wrong search terms.. Or my approach to solve this issue i totally wrong (maybe stupid), nobody would solve it the spring-boot-way but stick to Factories.
Appreciate your help!
You could use Springs's application context. Create a bean for each of the interfaces but annotate it with a specific profile e.g. "Function-A-AT". Now when you have to invoke it, you can simply set the application context of spring accordingly and the right bean should be used by Spring.
Hello everyone and thanks for reading my question.
after a discussion with a friend who is well versed in the spring framework i came to the conclusion that my approach or my favoured solution was not what i was searching for and is not how spring should be used. Because the Function_I-Instance depends on the for the specific batch loaded configuration it is not recommended to manage all these instances as #Beans.
In the end i decided to not manage the instances for my Function_I with spring. but instead i build a Controller / Factory which is a #Controller-Class and let this class build the instance i need with the passed parameters for decision making on runtime.
This is how it looks (Pseudo-Code)
#Controller
public class FunctionController {
SomeSpringManagedClass ssmc;
public FunctionController(#Autowired SomeSpringManagedClass ssmc) {
this.ssmc = ssmc;
}
public Function_I createFunction(FunctionConfiguration funcConf) {
boolean funcA, cntryDE;
// code to decide the function
if(funcA && cntryDE) {
return new FunctionA_DE(funcConf);
} else if(funB && cntryDE) {
return new FunctionB_DE(funcConf);
} // maybe more else if...
}
}

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

How to return a result from an Action in Java Play Framework?

All my controllers use this CheckDowntimeAction which I created.
#Singleton
#With(CheckDowntimeAction.class)
public class MyController extends Controller {
}
As expected, on every request, the CheckDowntimeAction prints "I'm here!". but how do I abort inside the CheckDowntimeAction so that if the site is down, I return my own result which I create in the CheckDowntimeAction?
public class CheckDowntimeAction extends play.mvc.Action.Simple {
#Override
public CompletionStage<Result> call(Http.Request req) {
logger.info("I'm here!");
// just move along, nothing to see here
return delegate.call(req);
}
}
This works, but it prints the downtime result AFTER the MyController is already done running. I want it to run BEFORE the controller completes.
public class CheckDowntimeAction extends play.mvc.Action.Simple {
#Override
public CompletionStage<Result> call(Http.Request req) {
if (downtime) {
Result r = badRequest("Site is down");
// before Play 2.7, this would have been:
// return F.Promise.pure(badRequest(r));
return delegate.call(req).thenApply(result -> r);
}
// just move along, nothing to see here
return delegate.call(req);
}
}
Note this is using Java Play 2.7 which uses Requests instead of Contexts, and F.Promise is no longer available. See https://www.playframework.com/documentation/2.7.x/JavaHttpContextMigration27
According to documentation in https://www.playframework.com/documentation/2.5.1/api/java/play/libs/F.Promise.html F.Promise is deprecated, it says to use CompletableFuture.completedFuture instead. What changed in Play 2.7 is that F.Promise was finally removed entirely.
I believe the correct Play 2.7 way is thus: return CompletableFuture.completedFuture(r);

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.

Implementing dynamic menu for Spring MVC/AOP application

I wish to implement dynamically changeable menu (updating whenever annotated method or controller added) for my Spring MVC application.
What i want is to introduce new annotation (#RequestMenuMapping) which will go to #Controller beans and their methods (just like #RequestMapping works).
Heres is what i want, User class, producing menu like
Users
Index | List | Signup | Login
with following code:
#Controller
#RequestMapping("user")
#RequestMenuMapping("Users")
public class User {
#RequestMapping("")
#RequestMenuMapping("Index")
public String index(/* no model here - just show almost static page (yet with JSP checks for authority)*/) {
return "user/index.tile";
}
#RequestMapping("list")
#RequestMenuMapping("List")
public String list(Model model) {
model.addAttribute("userList",/* get userlist from DAO/Service */);
return "user/list.tile";
}
#RequestMapping("signup")
#RequestMenuMapping("Signup")
public String signup(Model model) {
model.addAttribute("user",/* create new UserModel instance to be populated by user via html form */);
return "user/signup.tile";
}
#RequestMapping("login")
#RequestMenuMapping("Login")
public String login(Model model) {
model.addAttribute("userCreds",/* create new UserCreds instance to be populated via html form with login and pssword*/);
return "user/login.tile";
}
}
I think that Spring AOP may help me to pointcut methods with #RequestMenuMapping annotation and via #AfterReturning add something representing web-site menu to model.
But this raises two questions:
How do i get Model instance in #AfterReturning advice method in case it is missing in adviced method (as in .index())?
How do i get all methods (as in java reflection Method) and classes (as in java reflection Class) annotated with #RequestMenuMapping in order to build complete menu index?
I think a better soultion would be a bean post processor to scan all controller classes for the #RequestMenuMapping and a HandlerInterceptor to add the menu items to every model map.
InterceptorDemo:
#Aspect
#Component
public class InterceptorDemo {
#Pointcut("#annotation(org.springframework.web.bind.annotation.RequestMapping)")
public void requestMapping() {
}
#Pointcut("#annotation(you.package.RequestMenuMapping)")
public void requestMenuMapping() {
}
#AfterReturning("requestMapping() && equestMenuMapping()")
public void checkServer(JoinPoint joinPoint,Object returnObj) throws Throwable {
Object[] args = joinPoint.getArgs();
Model m = (Model)args[0];
// use joinPoint get class or methd...
}
}
If you want to intercept Contoller with you own, you can wirte another pointcut and ProceedingJoinPoint object can get what you want.
Q1:
ModelAndView object create at org.springframework.web.servlet.DispatcherServlet.doDispatch()
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// Do we need view name translation?
if (mv != null && !mv.hasView()) {
mv.setViewName(getDefaultViewName(request));
}
So, you can intercept handle method after returing or override the method.
Q2:As far as i know, there are two ways getting annotation methods.
1.Use AOP:
You can declare a pointcut like this:
#Pointcut("#annotation(you.package.RequestMenuMapping)")
public void requestMenuMappingPountcut() {
}
2.Use reflection.
Class clazz = Class.forName(classStr);
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(RequestMapping.class)
&& method.isAnnotationPresent(RequestMenuMapping.class)) {
// do something
}
}

Categories

Resources