KickJava   Java API By Example, From Geeks To Geeks.

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


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.apache.commons.logging.*;
56 import org.apache.commons.codec.binary.Base64;
57 import org.bsf.remoting.EJBDefinition;
58 import org.bsf.remoting.http.HttpServiceInvocationHandler;
59 import org.bsf.remoting.http.HttpServiceKey;
60 import org.bsf.remoting.http.HttpServiceRequest;
61 import org.bsf.remoting.http.HttpServiceResponse;
62
63 import java.io.ObjectInputStream JavaDoc;
64 import java.io.ObjectOutputStream JavaDoc;
65 import java.lang.reflect.Method JavaDoc;
66 import java.lang.reflect.Proxy JavaDoc;
67 import java.net.HttpURLConnection JavaDoc;
68 import java.net.URL JavaDoc;
69 import java.rmi.RemoteException JavaDoc;
70
71 /**
72  * This class handle the http protocole for the application server communication.
73  * Client classes can make http calls in parallel threads. The number of thread
74  * is limited by the field maxThreadCount.
75  * The parameters of the communication (port, host, ...)can be set by the ressource
76  * BsfModules.xml.
77  */

78 public class HttpSessionClient {
79
80     private static Log log = LogFactory.getLog( HttpSessionClient.class );
81
82     private static int requestNb;
83     private String JavaDoc sessionId;
84
85     private static final String JavaDoc AUTHENTICATED_SERVLET = "/authenticatedHttpSession";
86     private static final String JavaDoc UNAUTHENTICATED_SERVLET = "/httpSession";
87
88     /** Default call method is http*/
89     private static final String JavaDoc DEFAULT_PROTOCOL = "http";
90
91     /** Default Server Host*/
92     private static final String JavaDoc SERVER_HOST = "localHost";
93
94     /** Default Server Port*/
95     private static final int SERVER_PORT = 8080;
96
97     /** Default Server File*/
98     private static final String JavaDoc SERVER_CONTEXT = "remoting";
99
100     /** Default Server File*/
101     private static final int DEFAULT_THREAD_COUNT = 1;
102
103     /** BSF default mime type */
104     private static final String JavaDoc MIME_TYPE = "application/x-bsf";
105
106
107
108     /** The method used to transmit the call, the default is http but it can be https
109      * to secure the communication
110      */

111     private String JavaDoc protocol;
112
113     /** Where is the server */
114     private String JavaDoc host;
115
116     /**
117      * The context used at deployment time.
118      */

119     private String JavaDoc context;
120
121
122     /** Used for authentification */
123     private String JavaDoc login;
124
125     /** password used for basic authentification */
126     private String JavaDoc pass;
127
128     /** Http Port used */
129     private int port = -1;
130
131     /** The number of parallel thread used to perform the http call. */
132     private int maxThreadCount = DEFAULT_THREAD_COUNT;
133
134     /** The number of thread that are currently making a call */
135     private int curUsedThread = 0;
136
137     // Singleton attribute
138
protected static HttpSessionClient _instance = null;
139
140     /**
141      * Default constructor
142      */

143     protected HttpSessionClient() {
144         super();
145         log.debug( "Session client created." );
146
147     }
148
149     /**
150      * Invokes a stateless remote service
151      */

152     public Object JavaDoc invoke( EJBDefinition p_service, Method JavaDoc m, Object JavaDoc[] args )
153             throws Throwable JavaDoc {
154         Object JavaDoc result = null;
155
156         HttpServiceRequest request = new HttpServiceRequest(
157                 p_service, m.getName(), m.getParameterTypes(), args );
158
159         //Making the http call
160
result = invokeHttp( request );
161
162         if ( result instanceof HttpServiceKey ) {
163             //The return type of the method was an ejb, we return to
164
// the client a new dynamic proxy
165
HttpServiceInvocationHandler service = new HttpServiceInvocationHandler( (HttpServiceKey) result );
166             try {
167                 result = Proxy.newProxyInstance( this.getClass().getClassLoader(),
168                                                  new Class JavaDoc[]{m.getReturnType()}, service );
169             } catch( IllegalArgumentException JavaDoc e ) {
170                 log.fatal( e.getLocalizedMessage(), e );
171                 throw new RuntimeException JavaDoc(e.getLocalizedMessage());
172             }
173         }
174
175         return result;
176     }
177
178
179     /**
180      * Invokes a statefull remote service
181      */

182     public Object JavaDoc invoke( HttpServiceKey p_servicekey, Method JavaDoc m, Object JavaDoc[] args )
183             throws Throwable JavaDoc {
184         Object JavaDoc result = null;
185
186         HttpServiceRequest request = new HttpServiceRequest(
187                 p_servicekey, m.getName(), m.getParameterTypes(), args );
188
189         result = invokeHttp( request );
190
191         //If the return type of the method was an ejb,
192
//we return to the client a new dynamic proxy
193
if ( result instanceof HttpServiceKey ) {
194             result = new HttpServiceInvocationHandler( (HttpServiceKey) result );
195         }
196         return result;
197     }
198
199
200     /**
201      * Performs the http call.
202      */

203     private Object JavaDoc invokeHttp( HttpServiceRequest request ) throws Throwable JavaDoc {
204
205         String JavaDoc file = null;
206
207         if (isAuthenticatedCall()){
208             file = getAuthenticatedServerFile();
209         } else {
210             file = getUnauthenticatedServerFile();
211         }
212
213         int port = ( this.port == -1 ) ? SERVER_PORT : this.port;
214         String JavaDoc host = ( this.host == null ) ? SERVER_HOST : this.host;
215         String JavaDoc protocol = (this.protocol == null ) ? DEFAULT_PROTOCOL : this.protocol;
216
217         try {
218             getThreadLock();
219
220             int currentRequestNb = requestNb++;
221             log.debug( "Start remote call " + currentRequestNb + " " + request.getMethodName() );
222
223             HttpServiceResponse httpResponse;
224
225             //A session exists in the server we use URL rewriting to pass the session id
226
if ( sessionId != null ) {
227                 StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
228                 sb.append( file.toString() );
229                 sb.append( ";jsessionid=" );
230                 sb.append( sessionId );
231                 file = sb.toString();
232             }
233
234             URL JavaDoc url = new URL JavaDoc( protocol, host, port, file );
235
236             HttpURLConnection JavaDoc httpURLConnection = (HttpURLConnection JavaDoc) url.openConnection();
237             httpURLConnection.setRequestMethod( "POST" );
238             httpURLConnection.setDoOutput( true );
239             httpURLConnection.setDoInput( true );
240             httpURLConnection.setUseCaches( false );
241             // Set the content type to be application/x-bsf
242
httpURLConnection.setRequestProperty( "Content-Type", MIME_TYPE );
243
244             //If we have a login and a password => we can perform a basic
245
//authentification
246
if (isAuthenticatedCall()){
247
248                 String JavaDoc loginAndPass = login + ':' + pass;
249                 httpURLConnection.setRequestProperty("Authorization",
250                         "Basic "
251                         + new String JavaDoc (Base64.encodeBase64(loginAndPass.getBytes())));
252             }
253
254             ObjectOutputStream JavaDoc oos = new ObjectOutputStream JavaDoc(
255                     httpURLConnection.getOutputStream() );
256             oos.writeObject( request );
257             oos.close();
258
259             ObjectInputStream JavaDoc ois = new ObjectInputStream JavaDoc(
260                     httpURLConnection.getInputStream() );
261             httpResponse = (HttpServiceResponse) ois.readObject();
262             sessionId = httpURLConnection.getHeaderField( "jsessionid" );
263             ois.close();
264             httpURLConnection.disconnect();
265
266             log.debug( "Ending remote call " + currentRequestNb );
267
268
269             if ( httpResponse.isExceptionThrown() )
270                 throw httpResponse.getThrowable();
271             return httpResponse.getResult();
272
273         } catch( java.io.IOException JavaDoc e ) {
274             if (e instanceof RemoteException JavaDoc){
275                 throw e;
276             }else{
277                 String JavaDoc message = "Failure during the http remote call on http://"
278                         + host + ":" + port + file;
279                 log.fatal( message, e );
280                 throw new RemoteException JavaDoc( message, e );
281             }
282         } catch( ClassNotFoundException JavaDoc e ) {
283             String JavaDoc message = "Failure during the http remote call";
284             log.fatal( message, e );
285             throw new RemoteException JavaDoc( message, e );
286         }finally{
287             releaseThreadLock();
288         }
289     }
290
291
292
293     /**
294      * This method is used to limit the concurrent http call to the max
295      * fixed by maxThreadCount and to wait the end of the first call that
296      * will return the session id.
297      */

298     private synchronized void getThreadLock() {
299         // We wait the return of the first call
300
while ( sessionId == null && curUsedThread > 0 ) {
301             try {
302                 log.debug( "No session. Only one thread is authorized. Waiting ..." );
303                 wait();
304             } catch( InterruptedException JavaDoc e ) {
305                 log.fatal(e);
306                 throw new RuntimeException JavaDoc(e.getLocalizedMessage());
307             }
308         }
309
310         while ( curUsedThread >= maxThreadCount ) {
311             try {
312                 log.debug( "Max concurent http call reached. Waiting ..." );
313                 wait();
314             } catch( InterruptedException JavaDoc e ) {
315                 log.fatal(e);
316                 throw new RuntimeException JavaDoc(e.getLocalizedMessage());
317             }
318         }
319         curUsedThread++;
320     }
321
322     private synchronized void releaseThreadLock() {
323         curUsedThread--;
324
325         notify();
326     }
327
328
329     /**
330      * Singleton instanciation
331      */

332     public static HttpSessionClient getInstance() throws IllegalStateException JavaDoc {
333         if ( _instance == null ) {
334             _instance = new HttpSessionClient();
335         }
336         return _instance;
337     }
338
339     private boolean isAuthenticatedCall() {
340         return login != null && pass != null;
341     }
342
343
344     public void setHost( String JavaDoc host ) {
345         this.host = host;
346     }
347
348     /**
349      * sets the used protocol. The default one is http but you can set it to
350      * https to use a secure communication.
351      * @param protocol
352      */

353     public void setProtocol( String JavaDoc protocol) {
354         this.protocol = protocol;
355     }
356
357     /**
358      * @deprecated setServerContext should be used instead.
359      * @param serverFile the deployment context and the servlet. For instance /myApp/httpSession.
360      */

361     public void setServerFile( String JavaDoc serverFile ) {
362         if ( ! serverFile.endsWith("httpSession")){
363             throw new RuntimeException JavaDoc("You must use the method setContext() instead" +
364                     " of the method setServerFile to define your remote call.");
365         }
366         String JavaDoc context = serverFile.substring(0, serverFile.length() - 11);
367         setContext(context);
368     }
369
370     /**
371      * Set the context used at deployment time. For instance if the packaging of
372      * the remoting war inside of the ear use the context myApp, you should call
373      * setContext("myApp") on the HttpServiceFactory to reach the server.
374      *
375      * @param context
376      */

377     public void setContext(String JavaDoc context) {
378         while (context.endsWith("/")){
379             context = context.substring(0,context.length() -1 );
380         }
381         this.context = context;
382         log.debug( "Server context is " + context);
383     }
384
385     /**
386      * @return the server file to call for a unauthenticated call
387      */

388     private String JavaDoc getUnauthenticatedServerFile(){
389         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
390         sb.append('/');
391         if (context != null){
392             sb.append(context);
393         }else{
394             sb.append(SERVER_CONTEXT);
395         }
396         sb.append(UNAUTHENTICATED_SERVLET);
397         return sb.toString();
398     }
399
400     /**
401      * @return the server file to call for an authenticated call
402      */

403     private String JavaDoc getAuthenticatedServerFile(){
404         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
405         sb.append('/');
406         if (context != null){
407             sb.append(context);
408         }else{
409             sb.append(SERVER_CONTEXT);
410         }
411         sb.append(AUTHENTICATED_SERVLET);
412         return sb.toString();
413     }
414
415     public void setPort( int port ) {
416         this.port = port;
417     }
418
419     public int getThreadCount() {
420         return maxThreadCount;
421     }
422
423     public void setThreadCount( int threadCount ) {
424         this.maxThreadCount = threadCount;
425
426         log.debug( "Max concurrent thread set to " + threadCount );
427     }
428
429
430     public void setLogin(String JavaDoc login) {
431         this.login = login;
432     }
433
434     public void setPassword(String JavaDoc pass) {
435         this.pass = pass;
436     }
437 }
Popular Tags