1 /* 2 * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.beans; 27 28 import com.sun.beans.finder.ClassFinder; 29 30 import java.applet.Applet; 31 import java.applet.AppletContext; 32 import java.applet.AppletStub; 33 import java.applet.AudioClip; 34 35 import java.awt.Image; 36 37 import java.beans.beancontext.BeanContext; 38 39 import java.io.IOException; 40 import java.io.InputStream; 41 import java.io.ObjectInputStream; 42 import java.io.ObjectStreamClass; 43 import java.io.StreamCorruptedException; 44 45 import java.net.URL; 46 47 import java.security.AccessController; 48 import java.security.PrivilegedAction; 49 50 import java.util.Enumeration; 51 import java.util.Hashtable; 52 import java.util.Iterator; 53 import java.util.Vector; 54 55 /** 56 * This class provides some general purpose beans control methods. 57 */ 58 59 public class Beans { 60 61 /** 62 * <p> 63 * Instantiate a JavaBean. 64 * </p> 65 * @return a JavaBean 66 * @param cls the class-loader from which we should create 67 * the bean. If this is null, then the system 68 * class-loader is used. 69 * @param beanName the name of the bean within the class-loader. 70 * For example "sun.beanbox.foobah" 71 * 72 * @exception ClassNotFoundException if the class of a serialized 73 * object could not be found. 74 * @exception IOException if an I/O error occurs. 75 */ 76 77 public static Object instantiate(ClassLoader cls, String beanName) throws IOException, ClassNotFoundException { 78 return Beans.instantiate(cls, beanName, null, null); 79 } 80 81 /** 82 * <p> 83 * Instantiate a JavaBean. 84 * </p> 85 * @return a JavaBean 86 * 87 * @param cls the class-loader from which we should create 88 * the bean. If this is null, then the system 89 * class-loader is used. 90 * @param beanName the name of the bean within the class-loader. 91 * For example "sun.beanbox.foobah" 92 * @param beanContext The BeanContext in which to nest the new bean 93 * 94 * @exception ClassNotFoundException if the class of a serialized 95 * object could not be found. 96 * @exception IOException if an I/O error occurs. 97 */ 98 99 public static Object instantiate(ClassLoader cls, String beanName, BeanContext beanContext) throws IOException, ClassNotFoundException { 100 return Beans.instantiate(cls, beanName, beanContext, null); 101 } 102 103 /** 104 * Instantiate a bean. 105 * <p> 106 * The bean is created based on a name relative to a class-loader. 107 * This name should be a dot-separated name such as "a.b.c". 108 * <p> 109 * In Beans 1.0 the given name can indicate either a serialized object 110 * or a class. Other mechanisms may be added in the future. In 111 * beans 1.0 we first try to treat the beanName as a serialized object 112 * name then as a class name. 113 * <p> 114 * When using the beanName as a serialized object name we convert the 115 * given beanName to a resource pathname and add a trailing ".ser" suffix. 116 * We then try to load a serialized object from that resource. 117 * <p> 118 * For example, given a beanName of "x.y", Beans.instantiate would first 119 * try to read a serialized object from the resource "x/y.ser" and if 120 * that failed it would try to load the class "x.y" and create an 121 * instance of that class. 122 * <p> 123 * If the bean is a subtype of java.applet.Applet, then it is given 124 * some special initialization. First, it is supplied with a default 125 * AppletStub and AppletContext. Second, if it was instantiated from 126 * a classname the applet's "init" method is called. (If the bean was 127 * deserialized this step is skipped.) 128 * <p> 129 * Note that for beans which are applets, it is the caller's responsiblity 130 * to call "start" on the applet. For correct behaviour, this should be done 131 * after the applet has been added into a visible AWT container. 132 * <p> 133 * Note that applets created via beans.instantiate run in a slightly 134 * different environment than applets running inside browsers. In 135 * particular, bean applets have no access to "parameters", so they may 136 * wish to provide property get/set methods to set parameter values. We 137 * advise bean-applet developers to test their bean-applets against both 138 * the JDK appletviewer (for a reference browser environment) and the 139 * BDK BeanBox (for a reference bean container). 140 * 141 * @return a JavaBean 142 * @param cls the class-loader from which we should create 143 * the bean. If this is null, then the system 144 * class-loader is used. 145 * @param beanName the name of the bean within the class-loader. 146 * For example "sun.beanbox.foobah" 147 * @param beanContext The BeanContext in which to nest the new bean 148 * @param initializer The AppletInitializer for the new bean 149 * 150 * @exception ClassNotFoundException if the class of a serialized 151 * object could not be found. 152 * @exception IOException if an I/O error occurs. 153 */ 154 155 public static Object instantiate(ClassLoader cls, String beanName, BeanContext beanContext, AppletInitializer initializer) 156 throws IOException, ClassNotFoundException { 157 158 InputStream ins; 159 ObjectInputStream oins = null; 160 Object result = null; 161 boolean serialized = false; 162 IOException serex = null; 163 164 // If the given classloader is null, we check if an 165 // system classloader is available and (if so) 166 // use that instead. 167 // Note that calls on the system class loader will 168 // look in the bootstrap class loader first. 169 if (cls == null) { 170 try { 171 cls = ClassLoader.getSystemClassLoader(); 172 } catch (SecurityException ex) { 173 // We're not allowed to access the system class loader. 174 // Drop through. 175 } 176 } 177 178 // Try to find a serialized object with this name 179 final String serName = beanName.replace('.','/').concat(".ser"); 180 final ClassLoader loader = cls; 181 ins = AccessController.doPrivileged 182 (new PrivilegedAction<InputStream>() { 183 public InputStream run() { 184 if (loader == null) 185 return ClassLoader.getSystemResourceAsStream(serName); 186 else 187 return loader.getResourceAsStream(serName); 188 } 189 }); 190 if (ins != null) { 191 try { 192 if (cls == null) { 193 oins = new ObjectInputStream(ins); 194 } else { 195 oins = new ObjectInputStreamWithLoader(ins, cls); 196 } 197 result = oins.readObject(); 198 serialized = true; 199 oins.close(); 200 } catch (IOException ex) { 201 ins.close(); 202 // Drop through and try opening the class. But remember 203 // the exception in case we can't find the class either. 204 serex = ex; 205 } catch (ClassNotFoundException ex) { 206 ins.close(); 207 throw ex; 208 } 209 } 210 211 if (result == null) { 212 // No serialized object, try just instantiating the class 213 Class<?> cl; 214 215 try { 216 cl = ClassFinder.findClass(beanName, cls); 217 } catch (ClassNotFoundException ex) { 218 // There is no appropriate class. If we earlier tried to 219 // deserialize an object and got an IO exception, throw that, 220 // otherwise rethrow the ClassNotFoundException. 221 if (serex != null) { 222 throw serex; 223 } 224 throw ex; 225 } 226 227 /* 228 * Try to instantiate the class. 229 */ 230 231 try { 232 result = cl.newInstance(); 233 } catch (Exception ex) { 234 // We have to remap the exception to one in our signature. 235 // But we pass extra information in the detail message. 236 throw new ClassNotFoundException("" + cl + " : " + ex, ex); 237 } 238 } 239 240 if (result != null) { 241 242 // Ok, if the result is an applet initialize it. 243 244 AppletStub stub = null; 245 246 if (result instanceof Applet) { 247 Applet applet = (Applet) result; 248 boolean needDummies = initializer == null; 249 250 if (needDummies) { 251 252 // Figure our the codebase and docbase URLs. We do this 253 // by locating the URL for a known resource, and then 254 // massaging the URL. 255 256 // First find the "resource name" corresponding to the bean 257 // itself. So a serialzied bean "a.b.c" would imply a 258 // resource name of "a/b/c.ser" and a classname of "x.y" 259 // would imply a resource name of "x/y.class". 260 261 final String resourceName; 262 263 if (serialized) { 264 // Serialized bean 265 resourceName = beanName.replace('.','/').concat(".ser"); 266 } else { 267 // Regular class 268 resourceName = beanName.replace('.','/').concat(".class"); 269 } 270 271 URL objectUrl = null; 272 URL codeBase = null; 273 URL docBase = null; 274 275 // Now get the URL correponding to the resource name. 276 277 final ClassLoader cloader = cls; 278 objectUrl = 279 AccessController.doPrivileged 280 (new PrivilegedAction<URL>() { 281 public URL run() { 282 if (cloader == null) 283 return ClassLoader.getSystemResource 284 (resourceName); 285 else 286 return cloader.getResource(resourceName); 287 } 288 }); 289 290 // If we found a URL, we try to locate the docbase by taking 291 // of the final path name component, and the code base by taking 292 // of the complete resourceName. 293 // So if we had a resourceName of "a/b/c.class" and we got an 294 // objectURL of "file://bert/classes/a/b/c.class" then we would 295 // want to set the codebase to "file://bert/classes/" and the 296 // docbase to "file://bert/classes/a/b/" 297 298 if (objectUrl != null) { 299 String s = objectUrl.toExternalForm(); 300 301 if (s.endsWith(resourceName)) { 302 int ix = s.length() - resourceName.length(); 303 codeBase = new URL(s.substring(0,ix)); 304 docBase = codeBase; 305 306 ix = s.lastIndexOf('/'); 307 308 if (ix >= 0) { 309 docBase = new URL(s.substring(0,ix+1)); 310 } 311 } 312 } 313 314 // Setup a default context and stub. 315 BeansAppletContext context = new BeansAppletContext(applet); 316 317 stub = (AppletStub)new BeansAppletStub(applet, context, codeBase, docBase); 318 applet.setStub(stub); 319 } else { 320 initializer.initialize(applet, beanContext); 321 } 322 323 // now, if there is a BeanContext, add the bean, if applicable. 324 325 if (beanContext != null) { 326 unsafeBeanContextAdd(beanContext, result); 327 } 328 329 // If it was deserialized then it was already init-ed. 330 // Otherwise we need to initialize it. 331 332 if (!serialized) { 333 // We need to set a reasonable initial size, as many 334 // applets are unhappy if they are started without 335 // having been explicitly sized. 336 applet.setSize(100,100); 337 applet.init(); 338 } 339 340 if (needDummies) { 341 ((BeansAppletStub)stub).active = true; 342 } else initializer.activate(applet); 343 344 } else if (beanContext != null) unsafeBeanContextAdd(beanContext, result); 345 } 346 347 return result; 348 } 349 350 @SuppressWarnings("unchecked") 351 private static void unsafeBeanContextAdd(BeanContext beanContext, Object res) { 352 beanContext.add(res); 353 } 354 355 /** 356 * From a given bean, obtain an object representing a specified 357 * type view of that source object. 358 * <p> 359 * The result may be the same object or a different object. If 360 * the requested target view isn't available then the given 361 * bean is returned. 362 * <p> 363 * This method is provided in Beans 1.0 as a hook to allow the 364 * addition of more flexible bean behaviour in the future. 365 * 366 * @return an object representing a specified type view of the 367 * source object 368 * @param bean Object from which we want to obtain a view. 369 * @param targetType The type of view we'd like to get. 370 * 371 */ 372 public static Object getInstanceOf(Object bean, Class<?> targetType) { 373 return bean; 374 } 375 376 /** 377 * Check if a bean can be viewed as a given target type. 378 * The result will be true if the Beans.getInstanceof method 379 * can be used on the given bean to obtain an object that 380 * represents the specified targetType type view. 381 * 382 * @param bean Bean from which we want to obtain a view. 383 * @param targetType The type of view we'd like to get. 384 * @return "true" if the given bean supports the given targetType. 385 * 386 */ 387 public static boolean isInstanceOf(Object bean, Class<?> targetType) { 388 return Introspector.isSubclass(bean.getClass(), targetType); 389 } 390 391 /** 392 * Test if we are in design-mode. 393 * 394 * @return True if we are running in an application construction 395 * environment. 396 * 397 * @see DesignMode 398 */ 399 public static boolean isDesignTime() { 400 return ThreadGroupContext.getContext().isDesignTime(); 401 } 402 403 /** 404 * Determines whether beans can assume a GUI is available. 405 * 406 * @return True if we are running in an environment where beans 407 * can assume that an interactive GUI is available, so they 408 * can pop up dialog boxes, etc. This will normally return 409 * true in a windowing environment, and will normally return 410 * false in a server environment or if an application is 411 * running as part of a batch job. 412 * 413 * @see Visibility 414 * 415 */ 416 public static boolean isGuiAvailable() { 417 return ThreadGroupContext.getContext().isGuiAvailable(); 418 } 419 420 /** 421 * Used to indicate whether of not we are running in an application 422 * builder environment. 423 * 424 * <p>Note that this method is security checked 425 * and is not available to (for example) untrusted applets. 426 * More specifically, if there is a security manager, 427 * its <code>checkPropertiesAccess</code> 428 * method is called. This could result in a SecurityException. 429 * 430 * @param isDesignTime True if we're in an application builder tool. 431 * @exception SecurityException if a security manager exists and its 432 * <code>checkPropertiesAccess</code> method doesn't allow setting 433 * of system properties. 434 * @see SecurityManager#checkPropertiesAccess 435 */ 436 437 public static void setDesignTime(boolean isDesignTime) 438 throws SecurityException { 439 SecurityManager sm = System.getSecurityManager(); 440 if (sm != null) { 441 sm.checkPropertiesAccess(); 442 } 443 ThreadGroupContext.getContext().setDesignTime(isDesignTime); 444 } 445 446 /** 447 * Used to indicate whether of not we are running in an environment 448 * where GUI interaction is available. 449 * 450 * <p>Note that this method is security checked 451 * and is not available to (for example) untrusted applets. 452 * More specifically, if there is a security manager, 453 * its <code>checkPropertiesAccess</code> 454 * method is called. This could result in a SecurityException. 455 * 456 * @param isGuiAvailable True if GUI interaction is available. 457 * @exception SecurityException if a security manager exists and its 458 * <code>checkPropertiesAccess</code> method doesn't allow setting 459 * of system properties. 460 * @see SecurityManager#checkPropertiesAccess 461 */ 462 463 public static void setGuiAvailable(boolean isGuiAvailable) 464 throws SecurityException { 465 SecurityManager sm = System.getSecurityManager(); 466 if (sm != null) { 467 sm.checkPropertiesAccess(); 468 } 469 ThreadGroupContext.getContext().setGuiAvailable(isGuiAvailable); 470 } 471 } 472 473 /** 474 * This subclass of ObjectInputStream delegates loading of classes to 475 * an existing ClassLoader. 476 */ 477 478 class ObjectInputStreamWithLoader extends ObjectInputStream 479 { 480 private ClassLoader loader; 481 482 /** 483 * Loader must be non-null; 484 */ 485 486 public ObjectInputStreamWithLoader(InputStream in, ClassLoader loader) 487 throws IOException, StreamCorruptedException { 488 489 super(in); 490 if (loader == null) { 491 throw new IllegalArgumentException("Illegal null argument to ObjectInputStreamWithLoader"); 492 } 493 this.loader = loader; 494 } 495 496 /** 497 * Use the given ClassLoader rather than using the system class 498 */ 499 @SuppressWarnings("rawtypes") 500 protected Class resolveClass(ObjectStreamClass classDesc) 501 throws IOException, ClassNotFoundException { 502 503 String cname = classDesc.getName(); 504 return ClassFinder.resolveClass(cname, this.loader); 505 } 506 } 507 508 /** 509 * Package private support class. This provides a default AppletContext 510 * for beans which are applets. 511 */ 512 513 class BeansAppletContext implements AppletContext { 514 Applet target; 515 Hashtable<URL,Object> imageCache = new Hashtable<>(); 516 517 BeansAppletContext(Applet target) { 518 this.target = target; 519 } 520 521 public AudioClip getAudioClip(URL url) { 522 // We don't currently support audio clips in the Beans.instantiate 523 // applet context, unless by some luck there exists a URL content 524 // class that can generate an AudioClip from the audio URL. 525 try { 526 return (AudioClip) url.getContent(); 527 } catch (Exception ex) { 528 return null; 529 } 530 } 531 532 public synchronized Image getImage(URL url) { 533 Object o = imageCache.get(url); 534 if (o != null) { 535 return (Image)o; 536 } 537 try { 538 o = url.getContent(); 539 if (o == null) { 540 return null; 541 } 542 if (o instanceof Image) { 543 imageCache.put(url, o); 544 return (Image) o; 545 } 546 // Otherwise it must be an ImageProducer. 547 Image img = target.createImage((java.awt.image.ImageProducer)o); 548 imageCache.put(url, img); 549 return img; 550 551 } catch (Exception ex) { 552 return null; 553 } 554 } 555 556 public Applet getApplet(String name) { 557 return null; 558 } 559 560 public Enumeration<Applet> getApplets() { 561 Vector<Applet> applets = new Vector<>(); 562 applets.addElement(target); 563 return applets.elements(); 564 } 565 566 public void showDocument(URL url) { 567 // We do nothing. 568 } 569 570 public void showDocument(URL url, String target) { 571 // We do nothing. 572 } 573 574 public void showStatus(String status) { 575 // We do nothing. 576 } 577 578 public void setStream(String key, InputStream stream)throws IOException{ 579 // We do nothing. 580 } 581 582 public InputStream getStream(String key){ 583 // We do nothing. 584 return null; 585 } 586 587 public Iterator<String> getStreamKeys(){ 588 // We do nothing. 589 return null; 590 } 591 } 592 593 /** 594 * Package private support class. This provides an AppletStub 595 * for beans which are applets. 596 */ 597 class BeansAppletStub implements AppletStub { 598 transient boolean active; 599 transient Applet target; 600 transient AppletContext context; 601 transient URL codeBase; 602 transient URL docBase; 603 604 BeansAppletStub(Applet target, 605 AppletContext context, URL codeBase, 606 URL docBase) { 607 this.target = target; 608 this.context = context; 609 this.codeBase = codeBase; 610 this.docBase = docBase; 611 } 612 613 public boolean isActive() { 614 return active; 615 } 616 617 public URL getDocumentBase() { 618 // use the root directory of the applet's class-loader 619 return docBase; 620 } 621 622 public URL getCodeBase() { 623 // use the directory where we found the class or serialized object. 624 return codeBase; 625 } 626 627 public String getParameter(String name) { 628 return null; 629 } 630 631 public AppletContext getAppletContext() { 632 return context; 633 } 634 635 public void appletResize(int width, int height) { 636 // we do nothing. 637 } 638 }