Visualizzazione post con etichetta access. Mostra tutti i post
Visualizzazione post con etichetta access. Mostra tutti i post

venerdì 21 giugno 2013

Tricky session management

Session management is always a pain in the ass for the programmers.
Is never the case to say that it is easy.

With the introduction of the technology up to here spoken in this blog (EJB), we have a new way to manage user' session.

Let's see how.
This practice will work both in the case you want to develop a web application that uses a EJB as logic tier and in case you have a desktop application that uses the logic tier as business tier.

The trick consist in use a stateful session bean in order to keep active the session.

@Stateful(name="Login")
@Remote(IUser.class)
//@StatefulTimeout(value = 10, unit = TimeUnit.SECONDS)
@StatefulTimeout(value = 2, unit = TimeUnit.HOURS)
public class LoginManager 
implements IUser {
 private static Logger logger = Logger.getLogger(LoginManager.class);
 private Integer IdUtente;
....
....
    @Remove
    public void doLogout() {
     logger.info("distruggo la sessione e rimuovo l'EJB");
     this.IdUtente = null;
     
    }
    @PreDestroy
    private void myPreDestroy(){
     logger.info("PREDESTROY invalido la sessione e distruggo il bean");
     this.IdUtente = null;
     
    }
....
}
The method doLogin(String user, String password) is omitted because irrelevant for this scope.
In order to understand the trick, let's analyze the code from the top.

Stateful session beans, so the EJB container will handle itself the question to manage the user session. 
Remote, needed because we want to expose this methods via the public interface IUser.
Statefultimeout is used to keep active for a certain defined time the EJB. After this time is triggered, the ejb container (jboss/glassfish/whatever) will handle to remove the ejb.
Exaclty: the ejb will be removed, thus the user session will be invalidated.
Let's see the

  •  @remove notation. Indicates that the stateful session bean is to be removed by the container after completion of the method. 
  •  @predestroy notation. Goes hand in hand with remove, when the method marked with remove completes, the EJB container will invoke the method annotated with the @javax.annotation.PreDestroy annotation, if any, and then destroy the stateful session bean.


Remove is used to "remove" the bean after the method doLogout is called. 
Predestroy is used to invalidate the session, when the timer triggers the end of the session. Infact no one will call the doLogout method, but we want to invalidate anyhow the session.

Of couse the invalidation here means just put a variable to null, but you may want to perform more complicated actions. 

On the client side the session is maintained just keeping the pointer to the ejb handler. If the ejb has been removed on server side, using the stored handler will result in an exception. Thus the programmer will understand that the session is no more valid. 


sources:

venerdì 21 dicembre 2012

Accessing remote EJB on JBoss AS 7.1 from web application

Finally, after many and many hours spent on a solution, I'm able to deploy an EJB module and access it from a differente machine! Here are the steps:

Environment: 
1) JBoss AS 7.1 running on a Debian Distribution. To build and deploy the EJB module I used Eclipse (juno version)
2) Apache Tomcat 7.0.32 on a DIFFERENT machine on which I'm going to develop a simple servlet to get the remote ejb deployed on JBoss. I used Netbeans 7.2 here, just because I still prefer Netbeans when developing web applications. 


STEP 1. This is the key point of the entire work. I found hundreds of guides on the web explaining how to invoke remote ejb from a separated client, but noone of these pointed out this important issue. Either you are using Glassfish or JBoss to develop your EJB module you first have to set your server in such a way that is reachable from the outside and not only in localhost. To do this you have to edit the server descriptor, in this case ( JBoss ) standalone.xml. In ${JBOSS_HOME}/standalone/configuration/ there is our file, open it. Quite at the end of the file, we can find the interfaces definitions; ADD A NEW INTERFACE (I called it mine), setting the access of it to any ip. The result should be this:
  
        
            
        
        
            
        
        
            
        
        
            
        

    
Following the interfaces, we can find the socket-binding-group tag, which sets the ports required for the given interfaces. In our case, change the default-interface parameter to match the new interface ( mine again in this case ); the outpout should be:
 
        
        
        
        
        
        
        
        
        
        
        
            
        
    
STEP 2. Now we need to add a new APPLICATION user to the JBoss server. To do that run the add-user script ( ${JBOSS_HOME}/bin/ ) and select the b) option. The user and password will be used by the client to access the EJB module on the server. The a) option instead (managment user) allow you to create a username and password needed to access the administration panel of JBoss.

STEP 3. We are ready to develop an EJBModule on our JBoss server. Just create for example a stateless session bean on eclipse, providing a remote interface:

session bean pattern:

package ejb;

import javax.ejb.Remote;

@Stateless
@Remote(RemoteFirstBean.class)
public class FirstBean implements RemoteFirstBean{
...
//implemtation of interface's methods
}

interface pattern:

package ejb;

public interface RemoteFirstBean{
//declaration of methods
}

Now we can deploy the module on JBoss, no errors should rise up.

STEP 4. On my client pc, I create a java Web application using Tomcat as web server. After that, just create a servlet including in the processRequest method the following code just to test our remote invocation:

final Hashtable jndiProperties = new Hashtable();
jndiProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
try {
            final Context context = new InitialContext(jndiProperties);
            String appName = "";
            String moduleName = "jboss-firstbean";
            String distinctName = "";
            String beanName = "FirstBean";
            String interfaceFullName = "ejb.RemoteFirstBean";
            final String jndi = "ejb:" + appName + "/" + moduleName + "/" + distinctName + "/" + beanName + "!" + viewClassName;
            RemoteFirstBean statelessRemote = (RemoteFirstBean)context.lookup(jndi);

        } catch (NamingException ex) {
            System.out.println("problems");
        }

As we can see, first create an InitialContext putting "org.jboss.ejb.client.naming" as url_pkg_prefixes. This tells our application to lookup in his classpath for the jboss-ejb-client.properties file in which it will find all the information to access our remote ejb. Now we just need to lookup in the context for the bean using the jndi specification used the JBoss (remember to include in the project the same interface as the JBoss one). The jndi specification is the following:

ejb:{app-Name}/{module-Name}/{distinct-Name}/{bean-Name}!{fullPath-remote-Interface}

Appname should be the name of the .ear file we have deployed on JBoss (if deploying an Enterprise Application) . In my case I just deployed a .jar file as simple EJB module so app-name will be empty. Module-name is the name of the EJB module deployed ( the .jar file on JBoss ), in my case is "jboss-firstbean". Distinct name as well is empty. The full path of the remote interface in my case is ejb.RemoteFirstBean. The resulting jndi will be:

ejb:/jboss-firstbean//FirstBean!ejb.RemoteFirstBean

After the lookup, we can try some methods of the bean to verify if everything works.

STEP 5. As I said before, we must now set the properties file. It must be placed in the classpath of the web application. Create a file with the name jboss-ejb-client.properties and insert the following(field,value):

remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false
remote.connections=default
remote.connection.default.host=192.168.1.108
remote.connection.default.port=4447
remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false
remote.connection.default.username=USERNAMEUSEDINSTEP2
remote.connection.default.password=PASSWORDUSEDINSTEP2

4447 is the default port for remoting of JBoss. Just use the Ip of the JBoss server machine.

STEP 6. Last step. In order to use org.jboss.ejb.client.naming on the client application, I needed to include in my project the jboss-client.jar library which can be found in ${JBOSS_HOME}/bin/client/

STEP 7. Run the servlet, and all should works fine :) (EJBModule should be running as well :P )