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