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