KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > easybeans > component > smartclient > server > SmartClientEndPointComponent


1 /**
2  * EasyBeans
3  * Copyright (C) 2006 Bull S.A.S.
4  * Contact: easybeans@objectweb.org
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19  * USA
20  *
21  * --------------------------------------------------------------------------
22  * $Id:$
23  * --------------------------------------------------------------------------
24  */

25
26 package org.objectweb.easybeans.component.smartclient.server;
27
28 import static org.objectweb.easybeans.component.smartclient.api.ProtocolConstants.PROTOCOL_VERSION;
29
30 import java.io.IOException JavaDoc;
31 import java.io.InputStream JavaDoc;
32 import java.net.URL JavaDoc;
33 import java.nio.ByteBuffer JavaDoc;
34 import java.nio.channels.ClosedChannelException JavaDoc;
35 import java.nio.channels.SelectionKey JavaDoc;
36 import java.nio.channels.Selector JavaDoc;
37 import java.nio.channels.ServerSocketChannel JavaDoc;
38 import java.nio.channels.SocketChannel JavaDoc;
39 import java.util.Iterator JavaDoc;
40 import java.util.Set JavaDoc;
41 import java.util.logging.Level JavaDoc;
42 import java.util.logging.Logger JavaDoc;
43
44 import org.objectweb.easybeans.component.api.EZBComponent;
45 import org.objectweb.easybeans.component.api.EZBComponentException;
46 import org.objectweb.easybeans.component.itf.RegistryComponent;
47 import org.objectweb.easybeans.component.smartclient.api.ProtocolConstants;
48 import org.objectweb.easybeans.component.smartclient.message.AbsMessage;
49 import org.objectweb.easybeans.component.smartclient.message.ChannelAttachment;
50 import org.objectweb.easybeans.component.smartclient.message.ClassAnswer;
51 import org.objectweb.easybeans.component.smartclient.message.ClassNotFound;
52 import org.objectweb.easybeans.component.smartclient.message.ClassRequest;
53 import org.objectweb.easybeans.component.smartclient.message.ProviderURLAnswer;
54 import org.objectweb.easybeans.component.smartclient.message.ResourceAnswer;
55 import org.objectweb.easybeans.component.smartclient.message.ResourceNotFound;
56 import org.objectweb.easybeans.component.smartclient.message.ResourceRequest;
57
58 /**
59  * This endpoint receives the request from clients, handle them and send an
60  * answer.<br>
61  * For example, it send the bytecode for a given class.
62  * @author Florent Benoit
63  */

64 public class SmartClientEndPointComponent implements EZBComponent, Runnable JavaDoc {
65
66     /**
67      * Use the JDK logger (to avoid any dependency).
68      */

69     private static Logger JavaDoc logger = Logger.getLogger(SmartClientEndPointComponent.class.getName());
70
71
72     /**
73      * Maximum length of messages that we accept.
74      */

75     private static final int MAX_LENGTH_INCOMING_MSG = 500;
76
77     /**
78      * Default port number.
79      */

80     private static final int DEFAULT_PORT_NUMBER = 2503;
81
82     /**
83      * Buffer length.
84      */

85     private static final int BUF_APPEND = 1000;
86
87     /**
88      * Listening port number.
89      */

90     private int portNumber = DEFAULT_PORT_NUMBER;
91
92     /**
93      * Nio Selector.
94      */

95     private Selector JavaDoc selector = null;
96
97     /**
98      * The selection key of the server (accepting clients).
99      */

100     private SelectionKey JavaDoc serverkey = null;
101
102     /**
103      * Server socket channel (listening).
104      */

105     private ServerSocketChannel JavaDoc server = null;
106
107     /**
108      * Waiting ?
109      */

110     private boolean waitingSelector = true;
111
112     /**
113      * Link to the RMI component used to get the provider URL.
114      */

115     private RegistryComponent registryComponent = null;
116
117     /**
118      * Init method.<br/> This method is called before the start method.
119      * @throws EZBComponentException if the initialization has failed.
120      */

121     public void init() throws EZBComponentException {
122
123         // Creates a new selector
124
try {
125             selector = Selector.open();
126         } catch (IOException JavaDoc e) {
127             throw new EZBComponentException("Cannot open a new selector.", e);
128         }
129         // Server socket
130
try {
131             server = ServerSocketChannel.open();
132         } catch (IOException JavaDoc e) {
133             throw new EZBComponentException("Cannot open a new server socket channel.", e);
134         }
135
136         // no blocking
137
try {
138             server.configureBlocking(false);
139         } catch (IOException JavaDoc e) {
140             throw new EZBComponentException("Cannot configure the server socket with non-blocking mode.", e);
141         }
142     }
143
144     /**
145      * Start method.<br/> This method is called after the init method.
146      * @throws EZBComponentException if the start has failed.
147      */

148     public void start() throws EZBComponentException {
149
150         // port number listener
151
try {
152             server.socket().bind(new java.net.InetSocketAddress JavaDoc(portNumber));
153         } catch (IOException JavaDoc e) {
154             throw new EZBComponentException("Cannot listen on the port number '" + portNumber
155                     + "', maybe the port is already used.", e);
156         }
157
158         // registering
159
try {
160             serverkey = server.register(selector, SelectionKey.OP_ACCEPT);
161         } catch (ClosedChannelException JavaDoc e) {
162             throw new EZBComponentException("Cannot register the current selector as an accepting selector waiting clients.", e);
163         }
164
165         // now wait clients
166
waitingSelector = true;
167
168         // infinite loop
169
new Thread JavaDoc(this).start();
170
171         logger.info("SmartClient Endpoint listening on port '" + portNumber + "'.");
172
173     }
174
175     /**
176      * Stop method.<br/> This method is called when component needs to be
177      * stopped.
178      * @throws EZBComponentException if the stop is failing.
179      */

180     public void stop() throws EZBComponentException {
181         // break infinite loop
182
waitingSelector = false;
183         selector.wakeup();
184     }
185
186     /**
187      * Infinite loop (until the end of the component) that handle the selectors.
188      */

189     public void handleSelectors() {
190
191         // infinite loop
192
while (waitingSelector) {
193
194             // wait new stuff
195
int updatedKeys = 0;
196             try {
197                 updatedKeys = selector.select();
198             } catch (IOException JavaDoc e) {
199                 logger.log(Level.SEVERE, "Selector has been closed, stopping listener", e);
200                 waitingSelector = false;
201             }
202
203             // No update, then go ahead
204
if (updatedKeys == 0) {
205                 continue;
206             }
207
208             // Get selected keys
209
Set JavaDoc<SelectionKey JavaDoc> selectedKeys = selector.selectedKeys();
210
211             for (Iterator JavaDoc<SelectionKey JavaDoc> itSelectedKeys = selectedKeys.iterator(); itSelectedKeys.hasNext();) {
212                 SelectionKey JavaDoc selectionKey = itSelectedKeys.next();
213                 itSelectedKeys.remove(); // remove it has it was handled
214

215                 // Server key ?
216
if (selectionKey == serverkey) {
217                     // New client ?
218
if (selectionKey.isAcceptable()) {
219                         try {
220                             handleAccept();
221                         } catch (Exception JavaDoc e) {
222                             // all exception (including runtime)
223
logger.log(Level.SEVERE, "Unable to accept a new connection.", e);
224                         }
225                     }
226                 } else if (selectionKey.isReadable()) {
227                     // get request from client
228
try {
229                         handleRead(selectionKey);
230                     } catch (Exception JavaDoc e) {
231                         // all exception (including runtime)
232
logger.log(Level.SEVERE, "Unable to read data from the client.", e);
233                     }
234                 } else if (selectionKey.isWritable()) {
235                     // answer to the client
236
try {
237                         handleWrite(selectionKey);
238                     } catch (Exception JavaDoc e) {
239                         // all exception (including runtime)
240
logger.log(Level.SEVERE, "Unable to write data to the client.", e);
241                     }
242                 }
243             }
244         }
245     }
246
247     /**
248      * Handle a new client that is being connected.
249      * @throws IOException if cannot accept the client
250      */

251     private void handleAccept() throws IOException JavaDoc {
252         // new incoming connection
253
SocketChannel JavaDoc client = server.accept();
254
255         // Non blocking client
256
client.configureBlocking(false);
257
258         // Register client (with an empty channel attachment)
259
client.register(selector, SelectionKey.OP_READ, new ChannelAttachment());
260     }
261
262     /**
263      * Handle all read operations on channels.
264      * @param selectionKey the selected key.
265      * @throws IOException if cannot read from the channel.
266      */

267     private void handleRead(final SelectionKey JavaDoc selectionKey) throws IOException JavaDoc {
268         // Get the client channel that has data to read
269
SocketChannel JavaDoc client = (SocketChannel JavaDoc) selectionKey.channel();
270
271         // current bytecode read
272
ChannelAttachment channAttachment = (ChannelAttachment) selectionKey.attachment();
273         ByteBuffer JavaDoc channBuffer = channAttachment.getByteBuffer();
274
275         // Read again
276
int bytesread = client.read(channBuffer);
277         if (bytesread == -1) {
278             // close (as the client has been disconnected)
279
selectionKey.cancel();
280             client.close();
281         }
282
283         // Client send data, analyze data
284

285         // Got header ?
286
if (channBuffer.position() >= AbsMessage.HEADER_SIZE) {
287
288             // Yes, got header
289
// Check if it is a protocol that we manage
290
byte version = channBuffer.get(0);
291             if (version != PROTOCOL_VERSION) {
292                 selectionKey.cancel();
293                 client.close();
294                 throw new IllegalStateException JavaDoc("Invalid protocol version : waiting '" + PROTOCOL_VERSION + "', got '" + version
295                         + "'.");
296             }
297
298             // Get operation asked by client
299
byte opCode = channBuffer.get(1);
300
301             // Length
302
int length = channBuffer.getInt(2);
303             if (length < 0) {
304                 selectionKey.cancel();
305                 client.close();
306                 throw new IllegalStateException JavaDoc("Invalid length for client '" + length + "'.");
307             }
308
309             if (length > MAX_LENGTH_INCOMING_MSG) {
310                 selectionKey.cancel();
311                 client.close();
312                 throw new IllegalStateException JavaDoc("Length too big, max length = '" + MAX_LENGTH_INCOMING_MSG + "', current = '"
313                         + length + "'.");
314             }
315
316             // Correct header and correct length ?
317
if (channBuffer.position() >= AbsMessage.HEADER_SIZE + length) {
318                 // set the limit (specified in the length), else we have a
319
// default buffer limit
320
channBuffer.limit(AbsMessage.HEADER_SIZE + length);
321
322                 // duplicate this buffer
323
ByteBuffer JavaDoc dataBuffer = channBuffer.duplicate();
324
325                 // skip header (already analyzed)
326
dataBuffer.position(AbsMessage.HEADER_SIZE);
327
328                 // Switch on operations :
329
try {
330                     switch (opCode) {
331                     case ProtocolConstants.CLASS_REQUEST:
332                         handleReadClassRequest(selectionKey, dataBuffer);
333                         break;
334                     case ProtocolConstants.RESOURCE_REQUEST:
335                         handleReadResourceRequest(selectionKey, dataBuffer);
336                         break;
337                     case ProtocolConstants.PROVIDER_URL_REQUEST:
338                         handleReadProviderURLRequest(selectionKey, dataBuffer);
339                         break;
340                     default:
341                         // nothing to do
342
}
343                 } catch (Exception JavaDoc e) {
344                     // clean
345
selectionKey.cancel();
346                     client.close();
347                     throw new IllegalStateException JavaDoc("Cannot handle request with opCode '" + opCode + "'.", e);
348                 }
349             }
350         }
351
352     }
353
354     /**
355      * Handle the client's request asking for a class.
356      * @param selectionKey key for exchanging with the client.
357      * @param dataBuffer the buffer that contains request.
358      * @throws IOException if operation fails
359      */

360     private void handleReadClassRequest(final SelectionKey JavaDoc selectionKey, final ByteBuffer JavaDoc dataBuffer) throws IOException JavaDoc {
361         // Build object (from input)
362
ClassRequest classRequest = new ClassRequest(dataBuffer);
363         String JavaDoc className = classRequest.getClassName();
364
365         // Answer to the client (go in write mode)
366
selectionKey.interestOps(SelectionKey.OP_WRITE);
367         String JavaDoc encodedClassName = className.replaceAll("\\.", "/").concat(".class");
368
369         // Find the resource from the classloader
370
InputStream JavaDoc inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(encodedClassName);
371
372         if (inputStream == null) {
373             ClassNotFound classNotFound = new ClassNotFound(className);
374             selectionKey.attach(classNotFound.getMessage());
375             logger.log(Level.FINE, "Class '" + className + "' not found");
376             return;
377         }
378         byte[] bytecode = null;
379         try {
380             // Get bytecode of the class
381
bytecode = readClass(inputStream);
382
383         } finally {
384             inputStream.close();
385         }
386
387         // Create answer object
388
ClassAnswer classAnswer = new ClassAnswer(className, bytecode);
389
390         // Attach the answer on the key
391
selectionKey.attach(classAnswer.getMessage());
392
393     }
394
395     /**
396      * Handle the client's request asking for a resource.
397      * @param selectionKey key for exchanging with the client.
398      * @param dataBuffer the buffer that contains request.
399      * @throws IOException if operation fails
400      */

401     private void handleReadResourceRequest(final SelectionKey JavaDoc selectionKey, final ByteBuffer JavaDoc dataBuffer) throws IOException JavaDoc {
402
403         // Build object (from input)
404
ResourceRequest resourceRequest = new ResourceRequest(dataBuffer);
405         String JavaDoc resourceName = resourceRequest.getResourceName();
406
407         // Answer to the client
408
selectionKey.interestOps(SelectionKey.OP_WRITE);
409
410         // Find the resource from the classloader
411
URL JavaDoc url = Thread.currentThread().getContextClassLoader().getResource(resourceName);
412         if (url == null) {
413             ResourceNotFound resourceNotFound = new ResourceNotFound(resourceName);
414             selectionKey.attach(resourceNotFound.getMessage());
415             logger.log(Level.FINE, "Resource '" + resourceName + "' not found");
416             return;
417         }
418         InputStream JavaDoc inputStream = url.openStream();
419
420         byte[] bytes = null;
421         try {
422             // Get bytecode of the class
423
bytes = readClass(inputStream);
424
425         } finally {
426             inputStream.close();
427         }
428
429         // Create answer object
430
ResourceAnswer resourceAnswer = new ResourceAnswer(resourceName, bytes);
431
432         // Attach the answer on the key
433
selectionKey.attach(resourceAnswer.getMessage());
434
435     }
436
437     /**
438      * Handle the client's request asking for the default provider URL.
439      * @param selectionKey key for exchanging with the client.
440      * @param dataBuffer the buffer that contains request.
441      * @throws IOException if operation fails
442      */

443     private void handleReadProviderURLRequest(final SelectionKey JavaDoc selectionKey, final ByteBuffer JavaDoc dataBuffer) throws IOException JavaDoc {
444
445         // Answer to the client (go in write mode)
446
selectionKey.interestOps(SelectionKey.OP_WRITE);
447
448         String JavaDoc providerURL = registryComponent.getProviderURL();
449
450         logger.log(Level.INFO, "Provider URL asked by client : '" + providerURL + "'.");
451
452         // Create answer object
453
ProviderURLAnswer providerURLAnswer = new ProviderURLAnswer(providerURL);
454
455         // Attach the answer on the key
456
selectionKey.attach(providerURLAnswer.getMessage());
457
458     }
459
460     /**
461      * Handle all write operations on channels.
462      * @param selectionKey the selected key.
463      * @throws IOException if cannot write to the channel.
464      */

465     private void handleWrite(final SelectionKey JavaDoc selectionKey) throws IOException JavaDoc {
466         SocketChannel JavaDoc channel = (SocketChannel JavaDoc) selectionKey.channel();
467
468         // Write the data that was attached on the selection key
469
ByteBuffer JavaDoc buffer = (ByteBuffer JavaDoc) selectionKey.attachment();
470         if (buffer.hasRemaining()) {
471             channel.write(buffer);
472         } else {
473             // finished to write, close
474
channel.close();
475         }
476     }
477
478     /**
479      * Gets the bytes from the given input stream.
480      * @param is given input stream.
481      * @return the array of bytes for the given input stream.
482      * @throws IOException if class cannot be read.
483      */

484     private static byte[] readClass(final InputStream JavaDoc is) throws IOException JavaDoc {
485         if (is == null) {
486             throw new IOException JavaDoc("Given input stream is null");
487         }
488         byte[] b = new byte[is.available()];
489         int len = 0;
490         while (true) {
491             int n = is.read(b, len, b.length - len);
492             if (n == -1) {
493                 if (len < b.length) {
494                     byte[] c = new byte[len];
495                     System.arraycopy(b, 0, c, 0, len);
496                     b = c;
497                 }
498                 return b;
499             }
500             len += n;
501             if (len == b.length) {
502                 byte[] c = new byte[b.length + BUF_APPEND];
503                 System.arraycopy(b, 0, c, 0, len);
504                 b = c;
505             }
506         }
507     }
508
509     /**
510      * Launch the thread looking at the selectors.
511      */

512     public void run() {
513         handleSelectors();
514     }
515
516     /**
517      * Sets the port number of this protocol.
518      * @param portNumber the port for listening
519      */

520     public void setPortNumber(final int portNumber) {
521         this.portNumber = portNumber;
522     }
523
524     /**
525      * Sets the registry component.
526      * @param registryComponent the given component.
527      */

528     public void setRegistryComponent(final RegistryComponent registryComponent) {
529         this.registryComponent = registryComponent;
530     }
531
532 }
533
Popular Tags