KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > bsf > remoting > http > HttpSessionServer


1 /**
2  * Copyright (c) 2002 Bright Side Factory. All rights
3  * reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in
14  * the documentation and/or other materials provided with the
15  * distribution.
16  *
17  * 3. The end-user documentation included with the redistribution,
18  * if any, must include the following acknowledgment:
19  * "This product includes software developed by the
20  * Bright Side Factory (http://www.bs-factory.org/)."
21  * Alternately, this acknowledgment may appear in the software itself,
22  * if and wherever such third-party acknowledgments normally appear.
23  *
24  * 4. The names "Bright Side", "BS Factory" and "Bright Side Factory" must
25  * not be used to endorse or promote products derived from this
26  * software without prior written permission. For written
27  * permission, please contact info@bs-factory.org.
28  *
29  * 5. Products derived from this software may not be called "Bright Side",
30  * nor may "Bright Side" appear in their name, without prior written
31  * permission of the Apache Software Foundation.
32  *
33  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
34  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
35  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
36  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
37  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
38  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
39  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
40  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
41  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
42  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
43  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44  * SUCH DAMAGE.
45  * ====================================================================
46  *
47  * This software consists of voluntary contributions made by many
48  * individuals on behalf of the Bright Side Factory. For more
49  * information on the Bright Side Factory, please see
50  * <http://www.bs-factory.org/>.
51  *
52  */

53 package org.bsf.remoting.http;
54
55 import org.bsf.remoting.util.naming.PropertiesICFactory;
56 import org.apache.commons.logging.*;
57 import org.bsf.remoting.EJBDefinition;
58 import org.bsf.remoting.http.HttpServiceKey;
59 import org.bsf.remoting.http.HttpServiceRequest;
60 import org.bsf.remoting.http.HttpServiceResponse;
61
62 import javax.ejb.EJBHome JavaDoc;
63 import javax.ejb.EJBObject JavaDoc;
64 import javax.ejb.Handle JavaDoc;
65 import javax.ejb.EJBException JavaDoc;
66 import javax.naming.Context JavaDoc;
67 import javax.rmi.PortableRemoteObject JavaDoc;
68 import javax.servlet.ServletException JavaDoc;
69 import javax.servlet.http.HttpServlet JavaDoc;
70 import javax.servlet.http.HttpServletRequest JavaDoc;
71 import javax.servlet.http.HttpServletResponse JavaDoc;
72 import javax.servlet.http.HttpSession JavaDoc;
73 import java.io.IOException JavaDoc;
74 import java.io.ObjectInputStream JavaDoc;
75 import java.io.ObjectOutputStream JavaDoc;
76 import java.io.OutputStream JavaDoc;
77 import java.lang.reflect.InvocationTargetException JavaDoc;
78 import java.lang.reflect.Method JavaDoc;
79 import java.rmi.NoSuchObjectException JavaDoc;
80 import java.rmi.Remote JavaDoc;
81 import java.rmi.RemoteException JavaDoc;
82 import java.util.Hashtable JavaDoc;
83 import java.util.Map JavaDoc;
84 import java.security.Principal JavaDoc;
85
86 /**
87  * This is the servlet that intercept all the client calls and transmits these to the EJBs.
88  * All the client calls are Http POST calls that use url rewriting to keep the session id.
89  * <p>
90  * If the client uses authentication, it should call an authenticate(String login, String password)
91  * on a session.
92  * <p>
93  * The lookup on EJB are made through the _ejbContext InitialContext. This context
94  * is instantiated using the properties
95  *
96  */

97 public class HttpSessionServer extends HttpServlet JavaDoc {
98
99     Log log = LogFactory.getLog( HttpSessionServer.class );
100
101     //The cache of remote stateless services (statefull are saved in the user session)
102
protected static Hashtable JavaDoc serviceCache = new Hashtable JavaDoc();
103
104     //The index used as key for the statefull services
105
private int _maxServiceIndex = 0;
106
107     //The Initial context for EJB lookup
108
private static Context JavaDoc _ejbContext;
109     private static String JavaDoc _ejbContextProperties = null;
110
111
112     private static final String JavaDoc STATEFULL_CACHE = "stafullCache";
113
114
115     /**
116      * Uses the properties defined in the servlet environment to instantiate the
117      * principalManager and the initial context properties.
118      */

119     public void init() throws ServletException JavaDoc {
120         super.init();
121
122
123         if ( _ejbContextProperties == null ) {
124             _ejbContextProperties = getServletContext()
125                     .getInitParameter( "ejbContextProperties" );
126         }
127         initEjbContext( _ejbContextProperties );
128
129     }
130
131     /**
132      * The only http call used by the client side is POST in order to deal with unlimited (??)
133      * size of stream.
134      */

135     protected void doPost( HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response )
136             throws ServletException JavaDoc, IOException JavaDoc {
137
138         //Extract the serviceRequest from the stream
139
HttpServiceRequest httpServiceRequest = getHttpServiceRequest( request );
140         HttpServiceResponse httpServiceResponse;
141         EJBObject JavaDoc remoteRef;
142
143
144         //Verifiying the session existence
145
HttpSession JavaDoc session = request.getSession( false );
146
147         if ( session == null ) {
148             //this is the first call, we create the session .
149
session = request.getSession( true );
150
151             Principal JavaDoc principal = request.getUserPrincipal();
152             String JavaDoc username = null;
153             if (principal != null){
154                 username = principal.getName();
155             }
156             if (username == null){
157                 username ="";
158             }
159             log.info("Creation of a new session for : " + request.getContextPath()
160                     + " " + username);
161         }
162
163         try {
164
165
166             //retrieve the service reference
167
remoteRef = getEjbReference( httpServiceRequest, request );
168
169
170             //We can make the remote call
171
httpServiceResponse = processRemoteCall( remoteRef, httpServiceRequest, request );
172
173         } catch( NoSuchObjectException JavaDoc e ) {
174
175             //The server may have restart => we clear the cache and the IC
176
//and retry
177
initEjbContext( _ejbContextProperties );
178             serviceCache = new Hashtable JavaDoc();
179             remoteRef = getEjbReference( httpServiceRequest, request );
180             try {
181                 httpServiceResponse = processRemoteCall(
182                         remoteRef, httpServiceRequest, request );
183             } catch( NoSuchObjectException JavaDoc ex ) {
184                 //There is definitely a problem
185
httpServiceResponse = new HttpServiceResponse(
186                         new RemoteException JavaDoc( "Impossible to make the remote call."
187                                              + "Restart your application." ) );
188             }
189         }
190
191         //Write the result in the http stream
192
response.addHeader( "jsessionid", session.getId() );
193         writeHttpServiceResponse( response, httpServiceResponse );
194     }
195
196     /**
197      * Retrieves the reference to the EJBObject by the definition or by the cache depending
198      * on the request.
199      */

200     private EJBObject JavaDoc getEjbReference( HttpServiceRequest httpServiceRequest, HttpServletRequest JavaDoc request ) {
201         EJBObject JavaDoc remoteRef;
202         if ( httpServiceRequest.isStateless() )
203             remoteRef = getRemote( httpServiceRequest.getRemoteService() );
204         else
205             remoteRef = getFromCache( httpServiceRequest.getKeyToStatefullService(),
206                                       request );
207         return remoteRef;
208     }
209
210
211     /**
212      */

213     private HttpServiceResponse processRemoteCall( EJBObject JavaDoc remoteService,
214                                                    HttpServiceRequest httpServiceRequest, HttpServletRequest JavaDoc request )
215             throws NoSuchObjectException JavaDoc {
216
217         HttpServiceResponse httpServiceResponse;
218         try {
219             String JavaDoc p_methodName = httpServiceRequest.getMethodName();
220             Class JavaDoc[] paramTypes = httpServiceRequest.getParamTypes();
221             Object JavaDoc[] p_args = httpServiceRequest.getArgs();
222
223             HttpServiceKey newServiceKey = null;
224             Object JavaDoc remoteResult = null;
225
226             if ( remoteService == null )
227                 throw new IllegalArgumentException JavaDoc
228                         ( "UserSessionBean : The remote must be not null" );
229
230             if ( p_methodName == null )
231                 throw new IllegalArgumentException JavaDoc
232                         ( "UserSessionBean : The invoked method must be not null" );
233
234
235             Method JavaDoc invokedMethod = remoteService.getClass().getMethod( p_methodName,
236                                                                        paramTypes );
237
238             log.debug( "Invoking : " + p_methodName );
239             remoteResult = invokedMethod.invoke( remoteService, narrowArgs( p_args ) );
240
241             if ( remoteResult instanceof EJBObject JavaDoc ) {
242                 //The result of the invoked method is an EJB
243
//we create a new kew to store it
244
newServiceKey = new HttpServiceKey( _maxServiceIndex++ );
245
246                 // put the statefull ref in the session
247
putInCache( newServiceKey, (EJBObject JavaDoc) remoteResult, request );
248
249                 // and send the key as the result
250
remoteResult = newServiceKey;
251             }
252             httpServiceResponse = new HttpServiceResponse( remoteResult );
253         } catch( InvocationTargetException JavaDoc e ) {
254             if ( e.getTargetException() instanceof NoSuchObjectException JavaDoc ) {
255                 NoSuchObjectException JavaDoc noSuchObjectException = (NoSuchObjectException JavaDoc) e.getTargetException();
256                 throw noSuchObjectException;
257             }
258
259             //The target exception is the only result sent back on the client
260
httpServiceResponse = new HttpServiceResponse( e.getTargetException() );
261         } catch( NoSuchMethodException JavaDoc ex ) {
262             throw new RuntimeException JavaDoc( ex.getLocalizedMessage() );
263         } catch( IllegalAccessException JavaDoc ex ) {
264             throw new RuntimeException JavaDoc( ex.getLocalizedMessage() );
265         }
266         return httpServiceResponse;
267     }
268
269     /**
270      * writes the response in the http stream
271      */

272     private void writeHttpServiceResponse( HttpServletResponse JavaDoc response, HttpServiceResponse httpServiceResponse ) throws IOException JavaDoc {
273         OutputStream JavaDoc outputStream = response.getOutputStream();
274         ObjectOutputStream JavaDoc oos = new ObjectOutputStream JavaDoc( outputStream );
275         oos.writeObject( httpServiceResponse );
276         oos.close();
277     }
278
279     /**
280      * gets the request from the http stream
281      */

282     private HttpServiceRequest getHttpServiceRequest( HttpServletRequest JavaDoc request )
283             throws IOException JavaDoc {
284         //On recupere la requete dans le flux d'entree
285
ObjectInputStream JavaDoc ois = new ObjectInputStream JavaDoc(
286                 request.getInputStream() );
287         HttpServiceRequest httpServiceRequest = null;
288         try {
289             httpServiceRequest = (HttpServiceRequest)
290                     ois.readObject();
291         } catch( ClassNotFoundException JavaDoc e ) {
292             throw new RuntimeException JavaDoc(e.getLocalizedMessage());
293         }
294         ois.close();
295         return httpServiceRequest;
296     }
297
298
299     /**
300      * As we invoke the method on the remote service using the reflect package,
301      * we must know the classes of the arguments
302      */

303     private Class JavaDoc[] getParamTypes( Object JavaDoc[] p_args ) {
304         int argsLength = 0;
305         if ( p_args != null ) {
306             argsLength = p_args.length;
307         }
308         if ( argsLength == 0 )
309             return new Class JavaDoc[ 0 ];
310         Class JavaDoc[] types = new Class JavaDoc[ argsLength ];
311         for ( int i = 0 ; i < argsLength ; i++ ) {
312             types[ i ] = p_args[ i ].getClass();
313         }
314         return types;
315     }
316
317
318     /**
319      * Used for a call on a stateless service. The first time this method is called
320      * on a service the remote service is created by the invocation of the create method
321      * on the EJBHome.
322      */

323     private EJBObject JavaDoc getRemote( EJBDefinition p_service ) {
324
325         EJBObject JavaDoc result = null;
326
327         Object JavaDoc item = serviceCache.get( p_service.getJndiName() );
328         if (item != null)
329             result = (EJBObject JavaDoc) PortableRemoteObject.narrow( item, p_service.getRemoteClass() );
330         else{
331             try {
332                 EJBHome JavaDoc home = (EJBHome JavaDoc) PortableRemoteObject.narrow(
333                         _ejbContext.lookup( p_service.getJndiName() ), p_service.getHomeClass() );
334                 Method JavaDoc createMethod = home.getClass().getMethod( "create", null );
335                 result = (EJBObject JavaDoc) createMethod.invoke( home, null );
336                 serviceCache.put( p_service.getJndiName(), result );
337             } catch( Exception JavaDoc ex ) {
338                 log.fatal( "Error while getting the Home : " + p_service.getHomeClass() );
339                 log.fatal( "with the lookup on : " + p_service.getJndiName(), ex );
340             }
341         }
342         return result;
343     }
344
345     /**
346      * We put in cache the handles to prepare the passivation
347      */

348     private void putInCache( HttpServiceKey p_serviceKey, EJBObject JavaDoc p_stub,
349                              HttpServletRequest JavaDoc request ) {
350         try {
351             getStatefulCache(request).put( p_serviceKey, p_stub.getHandle() );
352         } catch( RemoteException JavaDoc ex ) {
353             log.fatal("Error during the retrieving of the stateful EJB handle." +
354                     "\nThe next calls on the EJB will fail!!!", ex);
355         }
356     }
357
358
359     /**
360      * Retrieve the statefull service from cache
361      */

362     private EJBObject JavaDoc getFromCache( HttpServiceKey serviceKey, HttpServletRequest JavaDoc request ) {
363         EJBObject JavaDoc result = null;
364         Class JavaDoc remoteClass = null;
365         Handle JavaDoc handle = null;
366         try {
367             handle = (Handle JavaDoc) getStatefulCache(request).get( serviceKey );
368             result = handle.getEJBObject();
369             //we don't know the remote class of the EJBObject => we find it with the EJBMetadata
370
remoteClass = result.getEJBHome().getEJBMetaData().getRemoteInterfaceClass();
371             result = (EJBObject JavaDoc) PortableRemoteObject.narrow( result, remoteClass );
372         } catch( Exception JavaDoc ex ) {
373             log.fatal(ex.getLocalizedMessage(), ex);
374             throw new RuntimeException JavaDoc(ex.getLocalizedMessage());
375         }//should never happen
376
return result;
377     }
378
379     /**
380      * Retrieve the stateful cache from the session. If the stateful cache hasn't been
381      * created yet, instantiate it.
382      * @param request
383      * @return
384      */

385     private Map JavaDoc getStatefulCache( HttpServletRequest JavaDoc request){
386         HttpSession JavaDoc session = request.getSession( false );
387         if ( session == null ) {
388             log.error( "There is no session, it is not possible"
389                        + " to call a statefull service" );
390             return null;
391         }
392         Map JavaDoc statefulCache = (Map JavaDoc) session.getAttribute( STATEFULL_CACHE );
393         if (statefulCache == null){
394             statefulCache = new Hashtable JavaDoc();
395             session.setAttribute( STATEFULL_CACHE, statefulCache );
396         }
397         return statefulCache;
398     }
399
400     /**
401      * Allows the session to have remote EJBObject as arguments
402      */

403     private static Object JavaDoc[] narrowArgs( Object JavaDoc[] p_args ) {
404         if ( p_args == null ) return null;
405         int length = p_args.length;
406         Object JavaDoc[] result = new Object JavaDoc[ length ];
407         for ( int i = 0 ; i < length ; i++ ) {
408             if ( p_args[ i ] instanceof Remote JavaDoc )
409                 result[ i ] = PortableRemoteObject.narrow( p_args[ i ], EJBObject JavaDoc.class );
410             else
411                 result[ i ] = p_args[ i ];
412         }
413         return result;
414     }
415
416
417     private static synchronized void initEjbContext( String JavaDoc jndiProperties ) {
418
419         if ( _ejbContext != null ) {
420             _ejbContext = null;
421
422             //Running the GC we realease the weak references used to cache
423
// the server references.
424
System.gc();
425         }
426         _ejbContext = PropertiesICFactory.createInitialContext( jndiProperties );
427     }
428
429 }
Popular Tags