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