Adobe AEM, how to get SlingHttpServletRequest object from Apache felix OSGI service - java

I am working on Adobe AEM 6.0 and still new to Apache Felix and Sling and I would like to know how to get instance of SlingHttpServletRequest from an OSGI service annotated with #Service.
Is it possible to get the request from ResourceResolverFactory or SlingRepository?
#Service
#Component(metatype = false)
public class TestServiceImpl implements TestService{
#Reference
private ResourceResolverFactory resourceResolverFactory;
#Reference
private SlingRepository repository;
}
I am aware that SlingHttpServletRequest is readily available for classes extending SlingAllMethodsServlet however as for my requirement I need to write a service rather than a servlet.
The rationale behind why I need SlingHttpServletRequest is because I need to get the client's IP address for audit logging purposes.
Is there any better way to do this? Or at least someone can help point me to correct direction how I can achieve such requirement.

I think the Filter is what you need. Create a service that implements Filter. The doFilter method is to be called on every Sling request (if sling.filter.scope=REQUEST of course).
See also Sling Filter
package com.examples.test.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.SlingHttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#Component(
metatype = true,
label = "Test Filter",
immediate = true,
enabled = true
)
#Service
#Properties({
#Property(name = "sling.filter.scope", value = "REQUEST", propertyPrivate = true),
#Property(name = "service.ranking", intValue = 100, propertyPrivate = true)
})
public class TestFilter implements Filter {
private final Logger log = LoggerFactory.getLogger(getClass());
#Override
public void destroy() {
// TODO Auto-generated method stub
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
//If you'll need some SlingHttpServletRequest functionality
//SlingHttpServletRequest httpRequest = (SlingHttpServletRequest) request;
log.info(request.getRemoteAddr());
chain.doFilter(request, response);
}
#Override
public void init(FilterConfig config) throws ServletException {
// TODO Auto-generated method stub
}
}

Related

Spring Around Aspect does not get called in controller

The problem is that when I want to print some logs from some controller methods the around annotated method with LogInit annotation does not get called, but when I am annotating some other service method with the same annotation the around method does get called. I am stuck for some time in this problem and I need to make it work.
I have the following aspect:
package com.db.mybank.backend.aop;
import com.db.mybank.backend.model.presentation.CustomerDataVO;
//import org.apache.log4j.LogManager;
import org.apache.log4j.MDC;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class LogInitAspect {
private static final Logger LOGGER = LoggerFactory.getLogger("SPLUNK");
private static final String METRIC_SPLITTER = ";";
/**
* Creates AOP pointcut and advice
* #param joinPoint (the executing method which has been annotated with #LogInit )
* #return
* #throws Throwable
*/
#Around("#annotation(com.db.mybank.backend.aop.LogInit)")
public void logInitSplunk(ProceedingJoinPoint joinPoint) {
initMDC();
CustomerDataVO proceed = null;
try {
proceed = (CustomerDataVO) joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
LOGGER.info(getFormattedLogLine());
return;
}
private String getFormattedLogLine() {
return MDC.get(MDCEnum.TIMESTAMP.getValue())+METRIC_SPLITTER+MDC.get(MDCEnum.CCCARD.getValue())+METRIC_SPLITTER
+MDC.get(MDCEnum.SESSIONID.getValue())+METRIC_SPLITTER+MDC.get(MDCEnum.TIMESTAMP_REQUEST.getValue())+METRIC_SPLITTER
+MDC.get(MDCEnum.TIMESTAMP_RESPONSE.getValue())+METRIC_SPLITTER+MDC.get(MDCEnum.SERVICE_ID.getValue())+METRIC_SPLITTER
+MDC.get(MDCEnum.SERVICE_URI.getValue())+METRIC_SPLITTER+MDC.get(MDCEnum.RESULT.getValue())+METRIC_SPLITTER
+MDC.get(MDCEnum.DEVICE_INFO.getValue())+METRIC_SPLITTER+MDC.get(MDCEnum.LOGIN_TIMESTAMP.getValue())+METRIC_SPLITTER
+MDC.get(MDCEnum.RESULT_LOGIN.getValue())+METRIC_SPLITTER+MDC.get(MDCEnum.LOGOUT_TIMESTAMP.getValue())+METRIC_SPLITTER
+MDC.get(MDCEnum.IP_ADDRESS_CLIENT.getValue())+METRIC_SPLITTER+MDC.get(MDCEnum.USER_AGENT_CLIENT.getValue())+METRIC_SPLITTER
+MDC.get(MDCEnum.TIMESTAMP_AUTHORIZATION.getValue())+METRIC_SPLITTER+MDC.get(MDCEnum.TRANSACTION_SERVICE_NAME.getValue())+METRIC_SPLITTER
+MDC.get(MDCEnum.VALUE_AND_CURRENCY.getValue())+METRIC_SPLITTER+MDC.get(MDCEnum.TRANSACTION_ID.getValue())+METRIC_SPLITTER
+MDC.get(MDCEnum.TIMESTAMP_TRANSACTION_SUBMIT_TO_BACKEND.getValue())+METRIC_SPLITTER+MDC.get(MDCEnum.NDG.getValue())+METRIC_SPLITTER;
}
private void initMDC(){
MDC.put(MDCEnum.TIMESTAMP.getValue(),"N/A");
MDC.put(MDCEnum.CCCARD.getValue(),"N/A");
MDC.put(MDCEnum.SESSIONID.getValue(),"N/A");
MDC.put(MDCEnum.TIMESTAMP_REQUEST.getValue(),"N/A");
MDC.put(MDCEnum.TIMESTAMP_RESPONSE.getValue(),"N/A");
MDC.put(MDCEnum.SERVICE_ID.getValue(),"N/A");
MDC.put(MDCEnum.SERVICE_URI.getValue(),"N/A");
MDC.put(MDCEnum.RESULT.getValue(),"N/A");
MDC.put(MDCEnum.DEVICE_INFO.getValue(),"N/A");
MDC.put(MDCEnum.LOGIN_TIMESTAMP.getValue(),"N/A");
MDC.put(MDCEnum.RESULT_LOGIN.getValue(),"N/A");
MDC.put(MDCEnum.LOGOUT_TIMESTAMP.getValue(),"N/A");
MDC.put(MDCEnum.IP_ADDRESS_CLIENT.getValue(),"N/A");
MDC.put(MDCEnum.USER_AGENT_CLIENT.getValue(),"N/A");
MDC.put(MDCEnum.TIMESTAMP_AUTHORIZATION.getValue(),"N/A");
MDC.put(MDCEnum.TRANSACTION_SERVICE_NAME.getValue(),"N/A");
MDC.put(MDCEnum.VALUE_AND_CURRENCY.getValue(),"N/A");
MDC.put(MDCEnum.TRANSACTION_ID.getValue(),"N/A");
MDC.put(MDCEnum.TIMESTAMP_TRANSACTION_SUBMIT_TO_BACKEND.getValue(),"N/A");
MDC.put(MDCEnum.NDG.getValue(),"N/A");
}
}
And I have the following controller method which is not working.
#RequestMapping(value = "/prelogin", method = RequestMethod.POST)
#ResponseBody
#LogInit
public AuthDataVO preLogin(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {

Spring boot localise every response parameter from RestController

I am trying to localize almost every parameter in the response of each API in my project.
I have figured out that we can do something like this in spring boot:
MessageSourceAccessor accessor = new MessageSourceAccessor(messageSource, locale);
return accessor.getMessage(code);
and keep the code versus localized message mapping in messages_en.properties, messages_fr.properties etc.
But for my application I specifically have two requirements:
I want to separate this logic from my business logic i.e., I don't want to write localization logic in each and every controller.
I want to try it at each and every response parameter for all the response through the server, maybe while Jackson is converting objects to string or after conversion to JSON.
Is there a way in spring boot to achieve this or are there any libraries available for this?
I have found a solution for this. Instead of using String for fields, I am using a custom class like LocalizedText:
import lombok.AllArgsConstructor;
import lombok.Data;
#Data
#AllArgsConstructor
public class LocalizedText {
private String text;
}
For serialization, I have created a Deserializer LocalizedTextSerailizer, something like this:
import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
#Component
public class LocalizedTextSerializer extends StdSerializer<LocalizedText> {
private static final long serialVersionUID = 619043384446863988L;
#Autowired
I18nUtil messages;
public LocalizedTextSerializer() {
super(LocalizedText.class);
}
public LocalizedTextSerializer(Class<LocalizedText> t) {
super(t);
}
#Override
public void serialize(LocalizedText value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeString(messages.get(value.getText()));
}
}
I18nUtil:
import java.util.Locale;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.NoSuchMessageException;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
#Component
#Slf4j
public class I18nUtil {
#Autowired
private MessageSource messageSource;
public String get(String code) {
try {
MessageSourceAccessor accessor = new MessageSourceAccessor(messageSource, Locale.getDefault());
return accessor.getMessage(code);
} catch (NoSuchMessageException nsme) {
log.info("Message not found in localization: " + code);
return code;
}
}
}
This pretty much serves the purpose, I don't have to mess up with the business logic and I can localize any parameter for any response in the application.
Note:
Here I18nUtil, returns the same code if it couldn't find any message in the message.properties.
Default locale is used in I18nUtil, for demonstration.

Using #CrossOrigin annotation with JAX-RS (Jersey)

Is it possible to annotate #CrossOrigin(Spring-MVC) with JAX-RS(Jersey) bassed annotations?
You can create something like that by implementing ContainerRequestFilter and ContainerResponseFilter (see: Filters) with Annotation driven Name binding or Dynamic binding.
Here an Annotation you could use for Name or Dynamic binding:
import javax.ws.rs.NameBinding;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#NameBinding
#Retention(RetentionPolicy.RUNTIME)
public #interface CrossOrigin {
String origins();
}
Here is an incomplete example of a DynamicFeature implementation containing and registering the filter classes in dynamic manner:
import org.glassfish.jersey.server.model.AnnotatedMethod;
import javax.annotation.Priority;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Priorities;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.container.*;
import javax.ws.rs.core.*;
import javax.ws.rs.ext.Provider;
import java.io.IOException;
#Provider
public class CrossOriginDynamicFeature implements DynamicFeature {
//check annotation, register filters if CrossOrigin is present (DynamicBinding)
#Override
public void configure(final ResourceInfo resourceInfo, final FeatureContext configuration) {
final AnnotatedMethod am = new AnnotatedMethod(resourceInfo.getResourceMethod());
if (resourceInfo.getResourceClass().getAnnotation(CrossOrigin.class) != null) {
configuration.register(CrossOriginFilter.class);
configuration.register(ResponseCorsFilter.class);
}
}
//this filter handles the request and checks the origin given
#Priority(Priorities.AUTHENTICATION)
private static class CrossOriginFilter implements ContainerRequestFilter {
#Context
private HttpServletRequest httpRequest;
#Context
private ResourceInfo resourceInfo;
#Override
public void filter(ContainerRequestContext requestContext)
throws IOException {
String origins = resourceInfo.getResourceMethod().getDeclaredAnnotation(CrossOrigin.class).origins();
String originHeader = requestContext.getHeaderString("origin");
//Maybe you want a different behaviour here.
//To prevent the execution of the annotated resource method
//if the origin of the request is not in the specified list,
//we break the execution with a 401.
if (Arrays.asList(origins.split(",")).contains(originHeader)) {
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
}
}
}
//if the Request filter allows the access,
//the CORS header are added to the response here.
//There are other stackoverflow questions regarding this theme.
public class ResponseCorsFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
//... add CORS header
}
}
}

Instantiate EJB Via Factory

So I have a service I am hooking by instantiating it through a factory that creates a proxy so it can process some annotations I have on the service. So my question is this...is there a way with JavaEE to have my dependency injection instantiate the instances of said service through the factory instead of however EJB's are normally instantiated by the server.
And otherwise...is there another way I could direct the Servlet or EJB container to process annotations for me? Like a bolt in of sorts that could have the code for handling the reflective analysis of the annotated class/method/fields?
I am sorry if this question is hard to understand, I'm having a hard time figuring out how to ask it. Here is an example of a factory one might use to instantiate a service (through a proxy).
package com.trinary.test.service;
import java.lang.reflect.Proxy;
import com.trinary.security.owasp.proxy.OWASPMethodValidatorProxy;
public class TestServiceFactory {
Class<?>[] interfaces = {TestService.class};
public TestService createESignService() throws IllegalArgumentException, InstantiationException, IllegalAccessException {
return (TestService)Proxy.newProxyInstance(
this.getClass().getClassLoader(),
interfaces,
new OWASPMethodValidatorProxy<TestService>(TestServiceImpl.class));
}
}
I would love it if in a servlet I might do something like this:
package com.trinary.test.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.trinary.test.service.TestService;
public class TestServlet extends HttpServlet {
private static final long serialVersionUID = -1778574173539761350L;
#EJB protected TestService testService;
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("PATH: " + req.getPathInfo());
// ...
resp.setContentType("text/html");
resp.getWriter().append("<html><h1>TESTY TEST!</h1></html>");
}
}
In the above you can see how I might be injecting test service into my servlet. But I would like the EJB container to instantiate new instances of TestService using the factory instead of however the container usually does it. Is there a way to do this?
There's no way to directly intercept #EJB at the injection point, but you could intercept the method call on the actual bean using an EJB interceptor. If you can switch to CDI #Inject in the client, then you could use a CDI interceptor. At that point, you could use a CDI producer method to have more control over the object injected into the servlet.
I just discovered how to solve my issue. First as a commenter pointed out, I went with CDI instead of EJB (I need to understand what the difference between the two is and if most Application servers support it).
Secondly I used the #Produces annotation on the method in my factory like so:
import java.lang.reflect.Proxy;
import javax.ejb.Stateless;
import javax.enterprise.inject.Default;
import javax.enterprise.inject.Produces;
import com.trinary.security.owasp.proxy.OWASPMethodValidatorProxy;
#Local
public class TestServiceFactory {
Class<?>[] interfaces = {TestService.class};
#Produces
#Default
public TestService createESignService() throws IllegalArgumentException, InstantiationException, IllegalAccessException {
return (TestService)Proxy.newProxyInstance(
this.getClass().getClassLoader(),
interfaces,
new OWASPMethodValidatorProxy<TestService>(TestServiceImpl.class));
}
}
And then in my servlet I can now do this:
import java.io.IOException;
import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.trinary.test.service.TestService;
public class TestServlet extends HttpServlet {
private static final long serialVersionUID = -1778574173539761350L;
#Inject TestService testService;
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("PATH: " + req.getPathInfo());
testService.method(...);
// ...
resp.setContentType("text/html");
resp.getWriter().append("<html><h1>TESTY TEST!</h1></html>");
}
}
However if you were to try to use this with the service TestService marked up as a managed bean, you would get an exception because it would be ambiguous which way you want to instantiate it unless you want to use a #Qualifer annotation. I elected not to. I just want everywhere this gets injected to use the factory to instantiate.

Jersey - How to use #Context annotation outside of a servlet?

I am trying to set up a Jersey ClientResponseFilter. It is working fine, but I want to deserialize my request parameters into a String so I can write helpful messages into a log file containing the actual data.
I was thinking about using MessageBodyWorkers for this. As this link below says:
"In case you need to directly work with JAX-RS entity providers, for example to serialize an entity in your resource method, filter or in a composite entity provider, you would need to perform quite a lot of steps."
Source: 7.4. Jersey MessageBodyWorkers API
This is exactly what I want to prevent.
So I was thinking about injecting the messagebodyworkers into my filter like this:
package somepackage.client.response;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientResponseContext;
import javax.ws.rs.client.ClientResponseFilter;
import javax.ws.rs.core.Context;
import javax.ws.rs.ext.Provider;
import org.glassfish.jersey.message.MessageBodyWorkers;
import org.slf4j.Logger;
#Provider
public class ResponseFilter implements ClientResponseFilter {
// TODO: these workers are not injected
#Context
private MessageBodyWorkers workers;
private final Logger logger;
public ResponseFilter(Logger logger) {
this.logger = logger;
}
#Override
public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext)
throws IOException {
if (responseValid(responseContext)) {
return;
}
logger.error("Error", "Some param");
}
private boolean responseValid(ClientResponseContext responseContext) {
if (responseContext.getStatus() == HttpServletResponse.SC_OK) {
return true;
}
return false;
}
}
But the reference is always null and remains null. Note that this filter is running in a standalone application, no servlet container is available.
Why isn't the annotation working in this case? How can I make it work? Or if making this approach to work is impossible, how can I work around this?
Any suggestions?
OK. Here is the workaround solution for the problem above: we should use #Inject and the HK2 Dependency Injection Kernel
HK2 Dependency Injection Kernel Link
First we need to make some changes to the filter:
package somepackage.client.response;
import java.io.IOException;
import javax.inject.Inject;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientResponseContext;
import javax.ws.rs.client.ClientResponseFilter;
import org.glassfish.jersey.message.MessageBodyWorkers;
import org.slf4j.Logger;
public class ResponseFilter implements ClientResponseFilter {
#Inject
private MessageBodyWorkers workers;
private Logger logger;
#Override
public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext)
throws IOException {
if (responseValid(responseContext)) {
return;
}
logger.error("Error", "Some param");
}
private boolean responseValid(ClientResponseContext responseContext) {
if (responseContext.getStatus() == HttpServletResponse.SC_OK) {
return true;
}
return false;
}
public void setLogger(Logger logger) {
this.logger = logger;
}
}
As you can see the constructor changed, the class uses the default constructor, and the annotation changed to #Inject. Be aware that there are two #Inject annotations with the same name. Make sure you use: javax.inject.Inject.
Then we need to implement org.glassfish.hk2.utilities.binding.AbstractBinder:
package somepackage.client;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.message.MessageBodyWorkers;
import org.glassfish.jersey.message.internal.MessageBodyFactory;
public class Binder extends AbstractBinder {
#Override
protected void configure() {
bind(MessageBodyFactory.class).to(MessageBodyWorkers.class);
}
}
And finally we should register the filter and our binder in the client:
...
client.register(ResponseFilter.class);
client.register(new SitemapBinder());
...
Then the worker is going to be injected fine.

Categories

Resources