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