src/share/classes/java/beans/Beans.java
author Jaroslav Tulach <jtulach@netbeans.org>
Tue, 16 Jun 2009 17:53:32 +0200
changeset 1238 57914fd9382f
parent 1077 27dabbdfdcac
child 1239 0b5692417910
permissions -rw-r--r--
Separatelly compiling Beans and Applet modules. Using 'code injection' to allow Beans class to perform its Applet related functionality if Applet module is around.
     1 /*
     2  * Copyright 1996-2009 Sun Microsystems, Inc.  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.  Sun designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
    23  * have any questions.
    24  */
    25 
    26 package java.beans;
    27 
    28 import com.sun.beans.finder.ClassFinder;
    29 
    30 import java.awt.GraphicsEnvironment;
    31 
    32 import java.beans.beancontext.BeanContext;
    33 
    34 import java.io.IOException;
    35 import java.io.InputStream;
    36 import java.io.ObjectInputStream;
    37 import java.io.ObjectStreamClass;
    38 import java.io.StreamCorruptedException;
    39 
    40 
    41 import java.security.AccessController;
    42 import java.security.PrivilegedAction;
    43 
    44 import java.util.Iterator;
    45 import java.util.ServiceLoader;
    46 
    47 import sun.awt.AppContext;
    48 import sun.beans.AppletProxy;
    49 
    50 /**
    51  * This class provides some general purpose beans control methods.
    52  */
    53 
    54 public class Beans {
    55     private static final Object DESIGN_TIME = new Object();
    56     private static final Object GUI_AVAILABLE = new Object();
    57 
    58     /**
    59      * <p>
    60      * Instantiate a JavaBean.
    61      * </p>
    62      *
    63      * @param     cls         the class-loader from which we should create
    64      *                        the bean.  If this is null, then the system
    65      *                        class-loader is used.
    66      * @param     beanName    the name of the bean within the class-loader.
    67      *                        For example "sun.beanbox.foobah"
    68      *
    69      * @exception ClassNotFoundException if the class of a serialized
    70      *              object could not be found.
    71      * @exception IOException if an I/O error occurs.
    72      */
    73 
    74     public static Object instantiate(ClassLoader cls, String beanName) throws IOException, ClassNotFoundException {
    75         return Beans.instantiate(cls, beanName, null, null);
    76     }
    77 
    78     /**
    79      * <p>
    80      * Instantiate a JavaBean.
    81      * </p>
    82      *
    83      * @param     cls         the class-loader from which we should create
    84      *                        the bean.  If this is null, then the system
    85      *                        class-loader is used.
    86      * @param     beanName    the name of the bean within the class-loader.
    87      *                        For example "sun.beanbox.foobah"
    88      * @param     beanContext The BeanContext in which to nest the new bean
    89      *
    90      * @exception ClassNotFoundException if the class of a serialized
    91      *              object could not be found.
    92      * @exception IOException if an I/O error occurs.
    93      */
    94 
    95     public static Object instantiate(ClassLoader cls, String beanName, BeanContext beanContext) throws IOException, ClassNotFoundException {
    96         return Beans.instantiate(cls, beanName, beanContext, null);
    97     }
    98 
    99     /**
   100      * Instantiate a bean.
   101      * <p>
   102      * The bean is created based on a name relative to a class-loader.
   103      * This name should be a dot-separated name such as "a.b.c".
   104      * <p>
   105      * In Beans 1.0 the given name can indicate either a serialized object
   106      * or a class.  Other mechanisms may be added in the future.  In
   107      * beans 1.0 we first try to treat the beanName as a serialized object
   108      * name then as a class name.
   109      * <p>
   110      * When using the beanName as a serialized object name we convert the
   111      * given beanName to a resource pathname and add a trailing ".ser" suffix.
   112      * We then try to load a serialized object from that resource.
   113      * <p>
   114      * For example, given a beanName of "x.y", Beans.instantiate would first
   115      * try to read a serialized object from the resource "x/y.ser" and if
   116      * that failed it would try to load the class "x.y" and create an
   117      * instance of that class.
   118      * <p>
   119      * If the bean is a subtype of java.applet.Applet, then it is given
   120      * some special initialization.  First, it is supplied with a default
   121      * AppletStub and AppletContext.  Second, if it was instantiated from
   122      * a classname the applet's "init" method is called.  (If the bean was
   123      * deserialized this step is skipped.)
   124      * <p>
   125      * Note that for beans which are applets, it is the caller's responsiblity
   126      * to call "start" on the applet.  For correct behaviour, this should be done
   127      * after the applet has been added into a visible AWT container.
   128      * <p>
   129      * Note that applets created via beans.instantiate run in a slightly
   130      * different environment than applets running inside browsers.  In
   131      * particular, bean applets have no access to "parameters", so they may
   132      * wish to provide property get/set methods to set parameter values.  We
   133      * advise bean-applet developers to test their bean-applets against both
   134      * the JDK appletviewer (for a reference browser environment) and the
   135      * BDK BeanBox (for a reference bean container).
   136      *
   137      * @param     cls         the class-loader from which we should create
   138      *                        the bean.  If this is null, then the system
   139      *                        class-loader is used.
   140      * @param     beanName    the name of the bean within the class-loader.
   141      *                        For example "sun.beanbox.foobah"
   142      * @param     beanContext The BeanContext in which to nest the new bean
   143      * @param     initializer The AppletInitializer for the new bean
   144      *
   145      * @exception ClassNotFoundException if the class of a serialized
   146      *              object could not be found.
   147      * @exception IOException if an I/O error occurs.
   148      */
   149 
   150     public static Object instantiate(ClassLoader cls, String beanName, BeanContext beanContext,
   151             /** TBD: Oops , this is bad. The AppletInitializer is used from
   152              * public API. I have not noticed that sooner. Opps.
   153             AppletInitializer initializer
   154              * meanwhile turning into object, but this cannot be final solution:
   155              */
   156             Object initializer
   157     ) throws IOException, ClassNotFoundException {
   158 
   159         InputStream ins;
   160         ObjectInputStream oins = null;
   161         Object result = null;
   162         boolean serialized = false;
   163         IOException serex = null;
   164 
   165         // If the given classloader is null, we check if an
   166         // system classloader is available and (if so)
   167         // use that instead.
   168         // Note that calls on the system class loader will
   169         // look in the bootstrap class loader first.
   170         if (cls == null) {
   171             try {
   172                 cls = ClassLoader.getSystemClassLoader();
   173             } catch (SecurityException ex) {
   174                 // We're not allowed to access the system class loader.
   175                 // Drop through.
   176             }
   177         }
   178 
   179         // Try to find a serialized object with this name
   180         final String serName = beanName.replace('.','/').concat(".ser");
   181         final ClassLoader loader = cls;
   182         ins = (InputStream)AccessController.doPrivileged
   183             (new PrivilegedAction() {
   184                 public Object run() {
   185                     if (loader == null)
   186                         return ClassLoader.getSystemResourceAsStream(serName);
   187                     else
   188                         return loader.getResourceAsStream(serName);
   189                 }
   190         });
   191         if (ins != null) {
   192             try {
   193                 if (cls == null) {
   194                     oins = new ObjectInputStream(ins);
   195                 } else {
   196                     oins = new ObjectInputStreamWithLoader(ins, cls);
   197                 }
   198                 result = oins.readObject();
   199                 serialized = true;
   200                 oins.close();
   201             } catch (IOException ex) {
   202                 ins.close();
   203                 // Drop through and try opening the class.  But remember
   204                 // the exception in case we can't find the class either.
   205                 serex = ex;
   206             } catch (ClassNotFoundException ex) {
   207                 ins.close();
   208                 throw ex;
   209             }
   210         }
   211 
   212         if (result == null) {
   213             // No serialized object, try just instantiating the class
   214             Class cl;
   215 
   216             try {
   217                 cl = ClassFinder.findClass(beanName, cls);
   218             } catch (ClassNotFoundException ex) {
   219                 // There is no appropriate class.  If we earlier tried to
   220                 // deserialize an object and got an IO exception, throw that,
   221                 // otherwise rethrow the ClassNotFoundException.
   222                 if (serex != null) {
   223                     throw serex;
   224                 }
   225                 throw ex;
   226             }
   227 
   228             /*
   229              * Try to instantiate the class.
   230              */
   231 
   232                 try {
   233                 result = cl.newInstance();
   234             } catch (Exception ex) {
   235                 // We have to remap the exception to one in our signature.
   236                 // But we pass extra information in the detail message.
   237                 throw new ClassNotFoundException("" + cl + " : " + ex, ex);
   238             }
   239         }
   240 
   241         if (result != null) {
   242 
   243             // Ok, if the result is an applet initialize it.
   244             Iterator<AppletProxy> it = ServiceLoader.load(AppletProxy.class).iterator();
   245             AppletProxy ap = it.hasNext() ? it.next() : null;
   246             if (ap != null || !ap.initialize(
   247                 result, initializer, serialized, beanName, beanContext, cls
   248             )) {
   249                 if (beanContext != null) beanContext.add(result);
   250             }
   251         }
   252 
   253         return result;
   254     }
   255 
   256 
   257     /**
   258      * From a given bean, obtain an object representing a specified
   259      * type view of that source object.
   260      * <p>
   261      * The result may be the same object or a different object.  If
   262      * the requested target view isn't available then the given
   263      * bean is returned.
   264      * <p>
   265      * This method is provided in Beans 1.0 as a hook to allow the
   266      * addition of more flexible bean behaviour in the future.
   267      *
   268      * @param bean        Object from which we want to obtain a view.
   269      * @param targetType  The type of view we'd like to get.
   270      *
   271      */
   272     public static Object getInstanceOf(Object bean, Class<?> targetType) {
   273         return bean;
   274     }
   275 
   276     /**
   277      * Check if a bean can be viewed as a given target type.
   278      * The result will be true if the Beans.getInstanceof method
   279      * can be used on the given bean to obtain an object that
   280      * represents the specified targetType type view.
   281      *
   282      * @param bean  Bean from which we want to obtain a view.
   283      * @param targetType  The type of view we'd like to get.
   284      * @return "true" if the given bean supports the given targetType.
   285      *
   286      */
   287     public static boolean isInstanceOf(Object bean, Class<?> targetType) {
   288         return Introspector.isSubclass(bean.getClass(), targetType);
   289     }
   290 
   291 
   292     /**
   293      * Test if we are in design-mode.
   294      *
   295      * @return  True if we are running in an application construction
   296      *          environment.
   297      *
   298      * @see DesignMode
   299      */
   300     public static boolean isDesignTime() {
   301         Object value = AppContext.getAppContext().get(DESIGN_TIME);
   302         return (value instanceof Boolean) && (Boolean) value;
   303     }
   304 
   305     /**
   306      * Determines whether beans can assume a GUI is available.
   307      *
   308      * @return  True if we are running in an environment where beans
   309      *     can assume that an interactive GUI is available, so they
   310      *     can pop up dialog boxes, etc.  This will normally return
   311      *     true in a windowing environment, and will normally return
   312      *     false in a server environment or if an application is
   313      *     running as part of a batch job.
   314      *
   315      * @see Visibility
   316      *
   317      */
   318     public static boolean isGuiAvailable() {
   319         Object value = AppContext.getAppContext().get(GUI_AVAILABLE);
   320         return (value instanceof Boolean) ? (Boolean) value : !GraphicsEnvironment.isHeadless();
   321     }
   322 
   323     /**
   324      * Used to indicate whether of not we are running in an application
   325      * builder environment.
   326      *
   327      * <p>Note that this method is security checked
   328      * and is not available to (for example) untrusted applets.
   329      * More specifically, if there is a security manager,
   330      * its <code>checkPropertiesAccess</code>
   331      * method is called. This could result in a SecurityException.
   332      *
   333      * @param isDesignTime  True if we're in an application builder tool.
   334      * @exception  SecurityException  if a security manager exists and its
   335      *             <code>checkPropertiesAccess</code> method doesn't allow setting
   336      *              of system properties.
   337      * @see SecurityManager#checkPropertiesAccess
   338      */
   339 
   340     public static void setDesignTime(boolean isDesignTime)
   341                         throws SecurityException {
   342         SecurityManager sm = System.getSecurityManager();
   343         if (sm != null) {
   344             sm.checkPropertiesAccess();
   345         }
   346         AppContext.getAppContext().put(DESIGN_TIME, Boolean.valueOf(isDesignTime));
   347     }
   348 
   349     /**
   350      * Used to indicate whether of not we are running in an environment
   351      * where GUI interaction is available.
   352      *
   353      * <p>Note that this method is security checked
   354      * and is not available to (for example) untrusted applets.
   355      * More specifically, if there is a security manager,
   356      * its <code>checkPropertiesAccess</code>
   357      * method is called. This could result in a SecurityException.
   358      *
   359      * @param isGuiAvailable  True if GUI interaction is available.
   360      * @exception  SecurityException  if a security manager exists and its
   361      *             <code>checkPropertiesAccess</code> method doesn't allow setting
   362      *              of system properties.
   363      * @see SecurityManager#checkPropertiesAccess
   364      */
   365 
   366     public static void setGuiAvailable(boolean isGuiAvailable)
   367                         throws SecurityException {
   368         SecurityManager sm = System.getSecurityManager();
   369         if (sm != null) {
   370             sm.checkPropertiesAccess();
   371         }
   372         AppContext.getAppContext().put(GUI_AVAILABLE, Boolean.valueOf(isGuiAvailable));
   373     }
   374 }
   375 
   376 /**
   377  * This subclass of ObjectInputStream delegates loading of classes to
   378  * an existing ClassLoader.
   379  */
   380 
   381 class ObjectInputStreamWithLoader extends ObjectInputStream
   382 {
   383     private ClassLoader loader;
   384 
   385     /**
   386      * Loader must be non-null;
   387      */
   388 
   389     public ObjectInputStreamWithLoader(InputStream in, ClassLoader loader)
   390             throws IOException, StreamCorruptedException {
   391 
   392         super(in);
   393         if (loader == null) {
   394             throw new IllegalArgumentException("Illegal null argument to ObjectInputStreamWithLoader");
   395         }
   396         this.loader = loader;
   397     }
   398 
   399     /**
   400      * Use the given ClassLoader rather than using the system class
   401      */
   402     protected Class resolveClass(ObjectStreamClass classDesc)
   403         throws IOException, ClassNotFoundException {
   404 
   405         String cname = classDesc.getName();
   406         return ClassFinder.resolveClass(cname, this.loader);
   407     }
   408 }
   409