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 }