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