I need to add certain functionality to an existing ejb projects.
Specifically - the client info, such as IP addres, login credentials (who is connected) and client application name
My bean is a stateless, so I worry there is an issue with such an approach..
My client code currently has the following:
private static MySession getmySession() throws RemoteException {
if(mySession != null) return mySession; //mySession is a private variable
try {
Properties h = new Properties();
h.put(Context.INITIAL_CONTEXT_FACTORY, contextFactory);
h.put(Context.PROVIDER_URL, serverUrl ); //t3://localhost
InitialContext ctx = new InitialContext(h);
mySessionHome home = (mySessionHome) ctx.lookup( "mySessionEJB" );
mySession = home.create();
return mySession;
} catch(NamingException ne) {
throw new RemoteException(ne.getMessage());
} catch(CreateException ce) {
throw new RemoteException(ce.getMessage());
}
}
Ideally, I would like my 'mySession' know about the client at the point it is returned.
If that may not be possible,
I would like to send a client info at the time a particular method of MySession is called.
Somewhere in this code
public static List getAllMembers() throws RemoteException, CatalogException
{
getMySession();
List list = mySession.getAllMembers() );
return list;
}
There are quite many such methods, so this is less desirable. but I will take it if it solves the task.
At the end of the day, when "getAllMembers()" executes on the server, I want to know particular info of which client has called it. (there can be many different, including webservices)
Thanks
First thing - what are you doing with the client information? If you're planning to use it for auditing, this sounds like a perfect use for Interceptors!
The EJB way to access user information is via the user's Principal, and there's no problem using this in a stateless bean. You may find that this doesn't get all the information you would like - this answer suggests getting the user IP isn't entirely supported.
Related
I need to change the thread pool of the underlying Grizzly transport layer.
According to the docs of GrizzlyHttpServerFactory:
Should you need to fine tune the underlying Grizzly transport layer, you can obtain direct access to the corresponding Grizzly structures with server.getListener("grizzly").getTransport().
and
To make certain options take effect, you need to work with an inactive HttpServer instance (that is the one that has not been started yet). To obtain such an instance, use one of the below factory methods with start parameter set to false
Since I like to put my self in the worse situations :-) the method I need shuld be:
HttpServer server= GrizzlyHttpServerFactory
.createHttpServer(getURI(), this.config, serviceLocator, false);
but the only method available (nearest to my case) is:
public static HttpServer createHttpServer(final URI uri,
final GrizzlyHttpContainer handler, final boolean secure,
final SSLEngineConfigurator sslEngineConfigurator, final boolean start) {
//....
}
If I understand the GrizzlyHttpContainer is private so I should use:
GrizzlyHttpContainer httpContainer =
new GrizzlyHttpContainerProvider().createContainer(GrizzlyHttpContainer.class, config);
But, since I'm sharing a ServiceLocator between resources and internal classes (a couple of ActiveMQ subscribers). I wonder if it were possible to achieve something like this:
GrizzlyHttpContainer httpContainer =
new GrizzlyHttpContainerProvider()
.createContainer(GrizzlyHttpContainer.class, configuration, serviceLocator);
Ideally what i need is a method like this:
public class GrizzlyHttpContainerProvider implements ContainerProvider {
#Override
public <T> T createContainer(Class<T> type, Application application, Object parentContext) throws ProcessingException {
if (HttpHandler.class == type || GrizzlyHttpContainer.class == type) {
return type.cast(new GrizzlyHttpContainer(application, parentContext));
}
return null;
}
}
Any suggestion about how to achieve this?
I'd would prefer a cleaner solution then creating the server with one of the provided methods that (for my case) auto start the server. Then stop it (waiting for termination somehow) and then finally:
this.server.getListener("grizzly").getTransport().setWorkerThreadPool(....);
and restarting it.
Best Regards,
Luca
Edit
This is cheating :-) ... this is the "dark way" (don't do it at home):
private GrizzlyHttpContainer getGrizzlyHttpContainer(final Application application,
final Object context) {
try {
Class<?> cls = Class.forName(
"org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpContainer");
Constructor<?> cons = cls.getDeclaredConstructor(Application.class, Object.class);
//System.out.println("Constructor Name--->>>"+cons.getName());
cons.setAccessible(true);
return (GrizzlyHttpContainer)cons.newInstance(application, context);
} catch (Exception err) {
return null;
}
}
I am using Guice's RequestScoped and Provider in order to get instances of some classes during a user request. This works fine currently. Now I want to do some job in a background thread, using the same instances created during request.
However, when I call Provider.get(), guice returns an error:
Error in custom provider, com.google.inject.OutOfScopeException: Cannot
access scoped object. Either we are not currently inside an HTTP Servlet
request, or you may have forgotten to apply
com.google.inject.servlet.GuiceFilter as a servlet
filter for this request.
afaik, this is due to the fact that Guice uses thread local variables in order to keep track of the current request instances, so it is not possible to call Provider.get() from a thread different from the thread that is handling the request.
How can I get the same instances inside new threads using Provider? It is possible to achieve this writing a custom scope?
I recently solved this exact problem. There are a few things you can do. First, read up on ServletScopes.continueRequest(), which wraps a callable so it will execute as if it is within the current request. However, that's not a complete solution because it won't forward #RequestScoped objects, only basic things like the HttpServletResponse. That's because #RequestScoped objects are not expected to be thread safe. You have some options:
If your entire #RequestScoped hierarchy is computable from just the HTTP response, you're done! You will get new instances of these objects in the other thread though.
You can use the code snippet below to explicitly forward all RequestScoped objects, with the caveat that they will all be eagerly instantiated.
Some of my #RequestScoped objects couldn't handle being eagerly instantiated because they only work for certain requests. I extended the below solution with my own scope, #ThreadSafeRequestScoped, and only forwarded those ones.
Code sample:
public class RequestScopePropagator {
private final Map<Key<?>, Provider<?>> requestScopedValues = new HashMap<>();
#Inject
RequestScopePropagator(Injector injector) {
for (Map.Entry<Key<?>, Binding<?>> entry : injector.getAllBindings().entrySet()) {
Key<?> key = entry.getKey();
Binding<?> binding = entry.getValue();
// This is like Scopes.isSingleton() but we don't have to follow linked bindings
if (binding.acceptScopingVisitor(IS_REQUEST_SCOPED)) {
requestScopedValues.put(key, binding.getProvider());
}
}
}
private final BindingScopingVisitor<Boolean> IS_REQUEST_SCOPED = new BindingScopingVisitor<Boolean>() {
#Override
public Boolean visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) {
return scopeAnnotation == RequestScoped.class;
}
#Override
public Boolean visitScope(Scope scope) {
return scope == ServletScopes.REQUEST;
}
#Override
public Boolean visitNoScoping() {
return false;
}
#Override
public Boolean visitEagerSingleton() {
return false;
}
};
public <T> Callable<T> continueRequest(Callable<T> callable) {
Map<Key<?>, Object> seedMap = new HashMap<>();
for (Map.Entry<Key<?>, Provider<?>> entry : requestScopedValues.entrySet()) {
// This instantiates objects eagerly
seedMap.put(entry.getKey(), entry.getValue().get());
}
return ServletScopes.continueRequest(callable, seedMap);
}
}
I have faced the exact same problem but solved it in a different way. I use jOOQ in my projects and I have implemented transactions using a request scope object and an HTTP filter.
But then I created a background task which is spawned by the server in the middle of the night. And the injection is not working because there is no request scope.
Well. The solutions is simple: create a request scope manually. Of course there is no HTTP request going on but that's not the point (mostly). It is the concept of the request scope. So I just need a request scope that exists alongside my background task.
Guice has an easy way to create a request scope: ServletScope.scopeRequest.
public class MyBackgroundTask extends Thread {
#Override
public void run() {
RequestScoper scope = ServletScopes.scopeRequest(Collections.emptyMap());
try ( RequestScoper.CloseableScope ignored = scope.open() ) {
doTask();
}
}
private void doTask() {
}
}
Oh, and you probably will need some injections. Be sure to use providers there, you want to delay it's creation until inside the created scope.
Better use ServletScopes.transferRequest(Callable) in Guice 4
Guice users! I have a situation here and I could find a workaround, but I'm not satisfied with my solution. It's very similar to Using the provider from two different scopes, but the answer there doesn't fit my situation.
I have a class like this, which I inject in a lot of places:
MyBusinessClass {
#Inject
MyBusinessClass(#AuthenticatedUser User user) {};
}
Up to some moment in the past, I just got the #AuthenticatedUser User from the web session, so I had:
bind(User.class).annotatedWith(AuthenticatedUser.class).toProvider(new AuthenticatedUserProvider());
...
public static class AuthenticatedUserProvider implements Provider<User> {
#Inject
Provider<Session> session;
public User get() {
return SessionUtil.getUserFromSession(session.get());
}
}
The problem:
That worked great till I needed to use the same MyBusinessClass inside a different Guice scope (and also outside the request scope). I created a JobScope, very similiar to the scope example in Guice docs, created a kind of JobSession, binded it to the JobScope, and put the #AuthenticatedUser User instance I want injected when MyBusinessClass is used inside the JobSession.
That's where I'm not proud of what I did.. I "improved" my provider to try to provide the #AuthenticatedUser User for all scopes, and I ended up with this ugly provider:
public static class AuthenticatedUserProvider implements Provider<User> {
#com.google.inject.Inject(optional=true)
Provider<Session> session;
#com.google.inject.Inject(optional=true)
Provider<JobSession> jobSession;
#Override
public User get() {
try {
return SessionUtil.getUserFromSession(session.get());
} catch (Exception e) {
try {
return SessionUtil.getUserFromJobSession(jobSession.get());
} catch (Exception ee) {
throw new IllegalStateException("Current scope doesn't have a auth user!");
}
}
}
}
The provider does a try-an-error approach to find which session (web session or job session) is available and return the user for the first one it is able to get. It works because of the #com.google.inject.Inject(optional=true) and also because the scopes are mutually exclusive.
Is there a better way to achieve this? I just want to have MyBusinessClass injected with #AuthenticatedUser User for any scope it is used transparently, and let Guice modules/providers find the right place to get the satisfying instance.
This code has been taken from org.glassfish.jersey.grizzly2 project, like the method name indicate, createHttpServer should be responsible "only" for creating and returning an instance of the HttpServer class, I just wonder why should the HttpServer.start call be encapsulated in such way ?
public static HttpServer createHttpServer(final URI uri,
final GrizzlyHttpContainer handler,
final boolean secure,
final SSLEngineConfigurator sslEngineConfigurator
final boolean start) {
final String host = (uri.getHost() == null) ? NetworkListener.DEFAULT_NETWORK_HOST : uri.getHost();
final int port = (uri.getPort() == -1) ? DEFAULT_HTTP_PORT : uri.getPort();
final NetworkListener listener = new NetworkListener("grizzly", host, port);
listener.setSecure(secure);
if (sslEngineConfigurator != null) {
listener.setSSLEngineConfig(sslEngineConfigurator);
}
final HttpServer server = new HttpServer();
server.addListener(listener);
// Map the path to the processor.
final ServerConfiguration config = server.getServerConfiguration();
if (handler != null) {
config.addHttpHandler(handler, uri.getPath());
}
config.setPassTraceRequest(true);
if (start) {
try {
// Start the server.
server.start();
} catch (IOException ex) {
throw new ProcessingException(LocalizationMessages.FAILED_TO_START_SERVER(ex.getMessage()), ex);
}
}
return server;
}
This is a public API method, not a class.
Single responsibility principle in wiki says
Every class should have a single responsibility, and that responsibility should be entirely encapsulated by the class.
SRP is intended for loose coupling and robustness. It definitely helps developers in maintaining the same while keeping it well functioning.
So had it been some internal method or class, I would have agreed.
The design goals of public API are completely different.
First thing you have to make sure about is ease of use.
Your software should hide internal idiosyncrasies of the implementation and design as well.
If a user is calling this method, and is unaware of requirement to call other method for starting, he/she'd be confused. We can not force users to know entire workflow of the software i.e. calling each small step manually.
Hope this helps.
The only advantage I see is that the user has to write less code. I totally disagree with this practice. If it says "create", then it should only create. Anyway, as far as its clearly specified in the documntation, it shoul be "ok" to do that... It's not the worst violation of the SRP I have seen...
For some background, I'm using JBoss AS 7 with EJB. I'm sending a message to my server from the client using errai message bus when it initially connects to retrieve its session ID so that I can make requests from it later on and have the server respond to the specific client.
How do I go about doing this? Can I inject a HttpSession object server side somehow? I'm very new to this so please bear with me. If I'm too vague let me know and I'll try to elaborate more.
If you are sending a message to an ErraiBus service method, you will have the Message object available. You can retrieve the session from it and get that session's ID like this:
#Service
public class ClientHelloService implements MessageCallback {
#Override
public void callback(final Message message) {
HttpSession session = message.getResource(
HttpServletRequest.class, HttpServletRequest.class.getName()).getSession();
System.out.println("Client said hello. Session ID: " + session.getId());
}
}
If you are instead sending the message to an Errai RPC endpoint, you will not have such easy access to the message. In this case, you will have to use the RpcContext.getSession() method:
#Service
public class ClientHelloRpcServiceImpl implements ClientHelloRpcService {
#Override
public void hello() {
HttpSession session = RpcContext.getHttpSession();
System.out.println("Client said hello. Session ID: " + session.getId());
}
}
The way this works is simple but ugly: RpcContext class stores the Message object that contained the RPC request in a ThreadLocal, and it just retrieves the HttpSession from that.
// the following variable is in the Http servlet service() method arguments
// only shown here this way to demonstrate the process
javax.servlet.http.HttpServletRequest serviceRequest;
javax.servlet.http.HttpServletResponse serviceResp; // addCookie()
javax.servlet.http.HttpSession cecil;
javax.servlet.http.Cookie[] reqCk;
// "(boolean overload) "true" creates the session" or call the other overload version method with no argument
// to retrieve the session getSession() "the server container stores and creates sessions"
// false in that version is to avoid bothering for a session to cut down uneeded processing
cecil = serviceRequest.getSession();//creates a session if it does not have one
String httpSession_ID = cecil.getID();
if((reqCk = serviceRequest.getCookies()) == null){
// perhaps create a cookie here using "new class "
// cookiePiece = new javax.servlet.http.Cookie("COOKIENAME",....); ....YOU MUST LEARN THE COOKIE PARTS WRITING RULES FOR BROWSER COOKIES !!! ; ; ;
serviceResp.addCookie(cookiePiece); // now is on the servers array "reqCk"
}else{
// process the cookie here using javax.servlet.http.Cookie methods
}
Other ways of storing and retrieving data are session scoped JSP or JSF beans.