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.
Related
I'm trying to print a message after the application startup with #PostConstruct, but nothing is printed.
package dev.renansouza.server;
import javax.annotation.PostConstruct;
import javax.inject.Singleton;
#Singleton
public class ServerService {
#PostConstruct
public void print() {
System.out.println("Hello!");
}
}
I have read that #PostConstruct is Lazy. Does this mean that I need to do
something else for this to work?
You can also use #EventListener annotation to acheive what you what, if using #PostConstruct is not that important to you.
For example in your case, you can add following code in any class to listen for application startup event.
#EventListener
void onStartup(ServerStartupEvent event) {
println("Hey, I work from anywhere in project..")
}
Code shared above is in Groovy
Keep in mind, the event listener added in main application class is usually called first from what I have observed.
The problem (aka feature) is, as you already mentioned, the lazy loading.
I see two solutions:
You have to do something to cause that bean to be initialized.
Change the scope of the bean from #Singleton to #Context
Micronaut has a few built-in scopes (see https://docs.micronaut.io/latest/guide/index.html#scopes) and the documentation of #Context states (see https://docs.micronaut.io/latest/api/io/micronaut/context/annotation/Context.html)
Context scope indicates that the classes life cycle is bound to that of the BeanContext and it should be initialized and shutdown during startup and shutdown of the underlying BeanContext.
Micronaut by default treats all Singleton bean definitions as lazy and will only load them on demand. By annotating a bean with #Context you can ensure that the bean is loaded at the same time as the context.
package dev.renansouza.server;
import javax.annotation.PostConstruct;
import io.micronaut.context.annotation.Context;
#Context
public class ServerService {
#PostConstruct
public void print() {
System.out.println("Hello!");
}
}
See the project at https://github.com/jeffbrown/renansouzapostconstruct.
https://github.com/jeffbrown/renansouzapostconstruct/blob/master/src/main/java/renansouzapostconstruct/ServerService.java
package renansouzapostconstruct;
import javax.annotation.PostConstruct;
import javax.inject.Singleton;
#Singleton
public class ServerService {
#PostConstruct
public void print() {
System.out.println("Hello!");
}
}
https://github.com/jeffbrown/renansouzapostconstruct/blob/master/src/main/java/renansouzapostconstruct/DemoController.java
package renansouzapostconstruct;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.HttpStatus;
#Controller("/demo")
public class DemoController {
private ServerService serverService;
public DemoController(ServerService serverService) {
this.serverService = serverService;
}
#Get("/")
public HttpStatus index() {
return HttpStatus.OK;
}
}
When you start the app you won't see the message printed to standard out because the service bean won't have been initialized. Send a request to http://localhost:8080/demo/ and then you will see the message printed to stdout.
I hope that helps.
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
}
}
I have a complicated scenario, for which i have no idea how to go about:
I have my ejbs running in a remote server.
And my web application running in a different server.
I have an ApplicationContext, that will differ based on domain, language, country etc.
I would like this application context to be passed to the remote EJB anonymously, in such a way that developers dont have to invoke all of their backend requests with the ApplicationContext as a parameter.
This is the scenarion, lets says i have a remote Stateless EJB:
#Stateless
public class MyStateless implements MyStatelessRemote{
//The application context that needs to be supplied form the front-end
#Inject //probably define a producer method to always supply a new one.
private ApplicationContext applicationContext;
public void doCheckSomething(final MySomethingData data){}
}
And on the frontend:
#SessionScoped
#Named
public class MyController implements Serializable{
#EJB
private MyStatelessRemote statelessRemote
//The current application/session context to be passed to the Stateless ejb on every invocation.
#Inject
private ApplicationContext executionContext;
public void doSomeOrderOrSomethingSimilar(){
//At this point, the current application context needs to be supplied to the remote EJB
//Which it may use to check on order validity based on configurations such as country
//language etc.
statelessRemote.doCheckSomething(mySomething);
}
}
With more than 20 EJBS and each having an average of 8 to 10 methods, and considering the likelihood that almost every ejb may need to know the executioncontext of the caller,
is it possible to parse the current execution context, through configuration or otherwise to the ejb during invocation of any method?
I am using Wildfly8 with remote ejb3.1, CDI1.1, JSF2.2
The application context may change when, for example the user changes his/her language
EDIT:
I am looking for something similar to Web Service inbound and outbound interceptors.
What you're describing is not possible using CDI/EJB, without passing in parameters to your remote EJB.
After few months working on jboss/wildfly server, i finally found a away to achieve this functionality:
Client-Side Code: (Based on jboss ejbclient)
package com.mycompany.view.service.wildfly.invocationcontext;
import com.mycompany.ejb.internal.MyCompanyAccount;
import com.mycompany.view.service.account.LoggedInAccount;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;
import org.jboss.ejb.client.AttachmentKey;
import org.jboss.ejb.client.EJBClientContext;
import org.jboss.ejb.client.EJBClientInterceptor;
import org.jboss.ejb.client.EJBClientInvocationContext;
import static com.mycompany.management.wildfly.invocationcontext.MyCompanyInvocationContextKey.MYCOMPANY_ACCOUNT_NUMBER;
/**
* Registers itself as a jboss client interceptor.
* #author marembo
*/
#Singleton
#Startup
public class MyCompanyInvocationContextInterceptor implements EJBClientInterceptor {
private static final Logger LOG = Logger.getLogger(MyCompanyInvocationContextInterceptor.class.getName());
private static final AttachmentKey<Long> MYCOMPANY_ACCOUNT_NUMBER_KEY = new AttachmentKey<>();
#Inject
#LoggedInAccount
private Instance<MyCompanyAccount> loggedInAccount;
#PostConstruct
void registerSelf() {
EJBClientContext.requireCurrent().registerInterceptor(0, this);
}
#Override
public void handleInvocation(final EJBClientInvocationContext ejbcic) throws Exception {
LOG.log(Level.INFO, "Intercepting invocation on: {0}", ejbcic.getInvokedMethod());
final EJBClientContext clientContext = ejbcic.getClientContext();
if (!loggedInAccount.isUnsatisfied()) {
final MyCompanyAccount mycompanyAccount = loggedInAccount.get();
if (mycompanyAccount != null) {
final Long accountNumber = mycompanyAccount.getAccountNumber();
clientContext.putAttachment(MYCOMPANY_ACCOUNT_NUMBER_KEY, accountNumber);
}
}
ejbcic.getContextData().put(MYCOMPANY_ACCOUNT_NUMBER, "348347878483");
ejbcic.sendRequest();
}
#Override
public Object handleInvocationResult(final EJBClientInvocationContext ejbcic) throws Exception {
return ejbcic.getResult();
}
}
On the server side, i register a global interceptor:
package com.mycompany.management.wildfly.extension;
import com.mycompany.management.facade.account.MyCompanyAccountFacade;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Resource;
import javax.ejb.EJB;
import javax.ejb.EJBContext;
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;
/**
* Default interceptor does not require #Interceptor
* #author marembo
*/
public class MyCompanyInvocationContextReceiver {
private static final Logger LOG = Logger.getLogger(MyCompanyInvocationContextReceiver.class.getName());
#Resource
private EJBContext ejbContext;
#EJB
private MyCompanyInvocationContext mycompanyInvocationContext;
#EJB
private MyCompanyAccountFacade mycompanyAccountFacade;
#AroundInvoke
public Object setMyCompanyAccount(final InvocationContext invocationContext) throws Exception {
final Map<String, Object> contextData = ejbContext.getContextData();
LOG.log(Level.INFO, "EJBContext data: {0}", contextData);
LOG.log(Level.INFO, "InvocationContext data: {0}", invocationContext.getContextData());
return invocationContext.proceed();
}
}
and the ejb-jar.xml:
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar xmlns = "http://java.sun.com/xml/ns/javaee"
version = "3.1"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd">
<interceptors>
<interceptor>
<interceptor-class>com.mycompany.management.wildfly.extension.MyCompanyInvocationContextReceiver</interceptor-class>
</interceptor>
</interceptors>
<assembly-descriptor>
<interceptor-binding>
<ejb-name>*</ejb-name>
<interceptor-class>com.mycompany.management.wildfly.extension.MyCompanyInvocationContextReceiver</interceptor-class>
</interceptor-binding>
</assembly-descriptor>
</ejb-jar>
As indicated on the server side interceptor, you can get the client-sent-data either from the InvocationContext or from the EJBContext.
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.
I've got a session scoped CDI bean, and I need to somehow access the HttpServletRequest object in this bean's #PostConstruct method. Is it possible? I've tried to Inject such an object, but it results in:
WELD-001408 Unsatisfied dependencies for type [HttpServletRequest] with qualifiers [#Default] at injection point [[field] #Inject ...]
As I understood while googling, the Seam framework has such a functionality, but I have a standard Java EE application on a GlassFish server.
Is it even possible to somehow pass the request to a CDI bean's #PostConstruct method?
As per your comment, you want access to the user principal. You can just inject it like this: #Inject Principal principal; or #Resource Principal principal;, see Java EE 6 Tutorial.
Update
I'll answer your direct question. In Java EE 7 (CDI 1.1) injection of HttpServletRequest is supported out of the box. In Java EE 6 (CDI 1.0) however, this is not supported out of the box. To get it working, include the class below into your web-app:
import javax.enterprise.inject.Produces;
import javax.servlet.ServletRequest;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;
#WebListener
public class CDIServletRequestProducingListener implements ServletRequestListener {
private static ThreadLocal<ServletRequest> SERVLET_REQUESTS = new ThreadLocal<>();
#Override
public void requestInitialized(ServletRequestEvent sre) {
SERVLET_REQUESTS.set(sre.getServletRequest());
}
#Override
public void requestDestroyed(ServletRequestEvent sre) {
SERVLET_REQUESTS.remove();
}
#Produces
private ServletRequest obtain() {
return SERVLET_REQUESTS.get();
}
}
Note: Tested only on GlassFish 3.1.2.2
When using the code from rdcrng be aware of the following:
* The producer-method obtain is dependent-scoped, thus is only called once for application scoped beans (and will resolve to problems for every other request except the first)
* You can solve this with #RequestScoped
* When RequestScoped annotated, you will only get a proxy, and thus you cannot cas it to HttpServletRequest. So you maybe want a producer for HttpServletRequest.
Also note: As per CDI specification link passage 3.6, java ee beans are NOT consideres managed beans. Thus you will end up with two instances of CDIServletRequestProducingListener - one managed by the Java EE container, one managed by the CDI-container. It only works because SERVLET_REQUESTS is static.
Following the modified code for your convenience.
import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Produces;
import javax.servlet.ServletRequest;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpServletRequest;
#WebListener
public class CDIServletRequestProducingListener implements ServletRequestListener {
private static ThreadLocal<ServletRequest> SERVLET_REQUESTS = new ThreadLocal<ServletRequest>();
#Override
public void requestInitialized(ServletRequestEvent sre) {
SERVLET_REQUESTS.set(sre.getServletRequest());
}
#Override
public void requestDestroyed(ServletRequestEvent sre) {
SERVLET_REQUESTS.remove();
}
#RequestScoped
#Produces
private HttpServletRequest obtainHttp() {
ServletRequest servletRequest = SERVLET_REQUESTS.get();
if (servletRequest instanceof HttpServletRequest) {
return (HttpServletRequest) servletRequest;
} else {
throw new RuntimeException("There is no HttpServletRequest avaible for injection");
}
}
}