1 25 26 package org.objectweb.easybeans.component.smartclient.client; 27 28 import static org.objectweb.easybeans.component.smartclient.api.ProtocolConstants.PROTOCOL_VERSION; 29 30 import java.io.File ; 31 import java.io.FileOutputStream ; 32 import java.io.IOException ; 33 import java.net.InetSocketAddress ; 34 import java.net.URL ; 35 import java.net.URLClassLoader ; 36 import java.nio.ByteBuffer ; 37 import java.nio.channels.SocketChannel ; 38 import java.util.logging.Level ; 39 import java.util.logging.Logger ; 40 41 import org.objectweb.easybeans.component.smartclient.api.Message; 42 import org.objectweb.easybeans.component.smartclient.api.ProtocolConstants; 43 import org.objectweb.easybeans.component.smartclient.message.ClassAnswer; 44 import org.objectweb.easybeans.component.smartclient.message.ClassNotFound; 45 import org.objectweb.easybeans.component.smartclient.message.ClassRequest; 46 import org.objectweb.easybeans.component.smartclient.message.ProviderURLAnswer; 47 import org.objectweb.easybeans.component.smartclient.message.ProviderURLRequest; 48 import org.objectweb.easybeans.component.smartclient.message.ResourceAnswer; 49 import org.objectweb.easybeans.component.smartclient.message.ResourceRequest; 50 51 55 public class AskingClassLoader extends URLClassLoader { 56 57 60 private static Logger logger = Logger.getLogger(AskingClassLoader.class.getName()); 61 62 65 private static final int DEFAULT_BUFFER_SIZE = 1024; 66 67 70 private InetSocketAddress socketAddress = null; 71 72 75 private static int nbClasses = 0; 76 77 80 private static int nbResources = 0; 81 82 85 private static long nbBytes = 0; 86 87 90 private static long timeToDownload = 0; 91 92 97 public AskingClassLoader(final String host, final int portNumber) { 98 super(new URL [0]); 99 100 socketAddress = new InetSocketAddress (host, portNumber); 102 103 Runtime.getRuntime().addShutdownHook(new ShutdownHook()); 105 106 } 107 108 112 private SocketChannel getChannel() { 113 SocketChannel channel = null; 114 115 try { 117 channel = SocketChannel.open(); 118 } catch (IOException e) { 119 cleanChannel(channel); 120 throw new IllegalStateException ("Cannot open a channel", e); 121 } 122 123 try { 125 channel.connect(socketAddress); 126 } catch (IOException e) { 127 cleanChannel(channel); 128 throw new IllegalStateException ("Cannot connect the channel", e); 129 } 130 131 return channel; 132 } 133 134 138 private void cleanChannel(final SocketChannel channel) { 139 if (channel != null) { 140 try { 141 channel.close(); 142 } catch (IOException e) { 143 logger.log(Level.FINE, "Cannot close the given channel", e); 144 } 145 } 146 } 147 148 154 public ByteBuffer sendRequest(final Message message, final SocketChannel channel) { 155 try { 157 channel.write(message.getMessage()); 158 } catch (IOException e) { 159 cleanChannel(channel); 160 throw new IllegalStateException ("Cannot send the given message '" + message + "'.", e); 161 } 162 163 ByteBuffer buffer = ByteBuffer.allocateDirect(DEFAULT_BUFFER_SIZE); 165 166 ByteBuffer completeBuffer = null; 167 try { 168 int length = 0; 169 boolean finished = false; 170 while (!finished && (channel.read(buffer)) != -1) { 171 if (buffer.position() >= Message.HEADER_SIZE) { 173 if (completeBuffer == null) { 175 length = buffer.getInt(2); 176 completeBuffer = ByteBuffer.allocate(Message.HEADER_SIZE + length + DEFAULT_BUFFER_SIZE); 179 180 } 181 } 182 buffer.flip(); 184 completeBuffer.put(buffer); 185 186 buffer.clear(); 188 189 if (completeBuffer.position() >= Message.HEADER_SIZE + length) { 190 completeBuffer.limit(Message.HEADER_SIZE + length); 191 completeBuffer.position(Message.HEADER_SIZE); 193 finished = true; 194 break; 195 } 196 } 197 } catch (Exception e) { 198 cleanChannel(channel); 199 throw new IllegalStateException ("Cannot read the answer from the server.", e); 200 } 201 202 return completeBuffer; 203 204 } 205 206 215 @Override 216 protected synchronized Class <?> findClass(final String name) throws ClassNotFoundException { 217 Class <?> clazz = null; 219 220 try { 221 super.findClass(name); 222 } catch (ClassNotFoundException cnfe) { 223 SocketChannel channel = null; 224 try { 225 long tStart = System.currentTimeMillis(); 226 channel = getChannel(); 228 ByteBuffer answerBuffer = sendRequest(new ClassRequest(name), channel); 229 230 byte opCode = getOpCode(answerBuffer, channel); 232 233 timeToDownload = timeToDownload + (System.currentTimeMillis() - tStart); 235 236 switch (opCode) { 238 case ProtocolConstants.CLASS_ANSWER: 239 ClassAnswer classAnswer = new ClassAnswer(answerBuffer); 240 try { 241 clazz = loadClass(name, classAnswer.getByteCode()); 242 } catch (IOException e) { 243 throw new ClassNotFoundException ("Cannot find the class", e); 244 } 245 nbClasses++; 246 nbBytes = nbBytes + classAnswer.getByteCode().length; 247 break; 248 case ProtocolConstants.CLASS_NOT_FOUND: 249 ClassNotFound classNotFound = new ClassNotFound(answerBuffer); 250 throw new ClassNotFoundException ("The class '" + classNotFound.getName() 251 + "' was not found on the remote side"); 252 default: 253 throw new ClassNotFoundException ("Invalid opCode '" + opCode + "' received"); 254 } 255 } finally { 256 cleanChannel(channel); 258 } 259 } 260 261 return clazz; 262 263 } 264 265 269 public String getProviderURL() { 270 String providerURL = null; 271 SocketChannel channel = null; 272 try { 273 long tStart = System.currentTimeMillis(); 274 channel = getChannel(); 276 ByteBuffer answerBuffer = sendRequest(new ProviderURLRequest(), channel); 277 278 byte opCode = getOpCode(answerBuffer, channel); 280 281 timeToDownload = timeToDownload + (System.currentTimeMillis() - tStart); 283 284 switch (opCode) { 286 case ProtocolConstants.PROVIDER_URL_ANSWER: 287 ProviderURLAnswer providerURLAnswer = new ProviderURLAnswer(answerBuffer); 288 providerURL = providerURLAnswer.getProviderURL(); 289 break; 290 default: 291 throw new IllegalStateException ("Invalid opCode '" + opCode + "' received"); 292 } 293 } finally { 294 cleanChannel(channel); 296 } 297 return providerURL; 298 } 299 300 306 private byte getOpCode(final ByteBuffer buffer, final SocketChannel channel) { 307 byte version = buffer.get(0); 309 if (version != PROTOCOL_VERSION) { 310 cleanChannel(channel); 311 throw new IllegalStateException ("Invalid protocol version : waiting '" + PROTOCOL_VERSION + "', got '" + version 312 + "'."); 313 } 314 315 byte opCode = buffer.get(1); 317 int length = buffer.getInt(2); 319 if (length < 0) { 320 cleanChannel(channel); 321 throw new IllegalStateException ("Invalid length for client '" + length + "'."); 322 } 323 return opCode; 324 } 325 326 333 @Override 334 public synchronized URL findResource(final String name) { 335 URL url = null; 336 url = super.findResource(name); 337 338 if (url != null) { 339 return url; 340 } 341 342 if (name.startsWith("META-INF")) { 343 return null; 344 } 345 346 SocketChannel channel = null; 347 try { 348 long tStart = System.currentTimeMillis(); 349 350 channel = getChannel(); 352 ByteBuffer answerBuffer = sendRequest(new ResourceRequest(name), channel); 353 354 byte opCode = getOpCode(answerBuffer, channel); 356 357 timeToDownload = timeToDownload + (System.currentTimeMillis() - tStart); 359 360 switch (opCode) { 362 case ProtocolConstants.RESOURCE_ANSWER: 363 ResourceAnswer resourceAnswer = new ResourceAnswer(answerBuffer); 364 String resourceName = resourceAnswer.getResourceName(); 365 byte[] bytes = resourceAnswer.getBytes(); 366 367 nbResources++; 368 nbBytes = nbBytes + resourceAnswer.getBytes().length; 369 370 File fConfDir = new File (System.getProperty("java.io.tmpdir") + File.separator + "easybeans-smart"); 371 if (!fConfDir.exists()) { 372 fConfDir.mkdir(); 373 } 374 375 String [] tokens = resourceName.split("/"); 377 StringBuilder sb = new StringBuilder (); 378 for (String token : tokens) { 379 if (sb.length() > 0) { 380 sb.append(File.separator); 381 } 382 sb.append(token); 383 } 384 385 File urlFile = new File (fConfDir, sb.toString()); 387 if (!urlFile.getParentFile().exists()) { 388 urlFile.getParentFile().mkdir(); 389 } 390 391 FileOutputStream fos = new FileOutputStream (urlFile); 393 fos.write(bytes); 394 fos.close(); 395 url = urlFile.toURI().toURL(); 396 break; 397 case ProtocolConstants.RESOURCE_NOT_FOUND: 398 url = null; 399 break; 400 default: 401 throw new IllegalStateException ("Invalid opCode '" + opCode + "' received"); 402 } 403 } catch (Exception e) { 404 logger.log(Level.SEVERE, "Cannot handle : findResource '" + name + "'", e); 405 } finally { 406 cleanChannel(channel); 408 } 409 410 return url; 411 } 412 413 420 421 private Class loadClass(final String className, final byte[] bytecode) throws IOException { 422 Class clazz = null; 424 try { 425 ClassLoader loader = this; 426 Class cls = Class.forName("java.lang.ClassLoader"); 427 java.lang.reflect.Method method = cls.getDeclaredMethod("defineClass", new Class [] {String .class, byte[].class, 428 int.class, int.class }); 429 430 method.setAccessible(true); 432 try { 433 Object [] args = new Object [] {className, bytecode, new Integer (0), new Integer (bytecode.length) }; 434 clazz = (Class ) method.invoke(loader, args); 435 } finally { 436 method.setAccessible(false); 437 } 438 } catch (Exception e) { 439 IOException ioe = new IOException ("Cannt define class with name '" + className + "'."); 440 ioe.initCause(e); 441 throw ioe; 442 } 443 return clazz; 444 } 445 446 450 static class ShutdownHook extends Thread { 451 452 455 @Override 456 public void run() { 457 System.out.println("Downloaded '" + nbClasses + "' classes, '" + nbResources + "' resources for a total of '" 459 + nbBytes + "' bytes and it took '" + timeToDownload + "' ms."); 460 } 461 } 462 } 463 | Popular Tags |