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 
instantiate(ClassLoader cls, String beanName)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")
instantiate(ClassLoader cls, String beanName, BeanContext beanContext)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 is recommended to use
160      * {@link #instantiate(ClassLoader, String, BeanContext)},
161      * because the 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")
instantiate(ClassLoader cls, String beanName, BeanContext beanContext, AppletInitializer initializer)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")
unsafeBeanContextAdd(BeanContext beanContext, Object res)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      */
getInstanceOf(Object bean, Class<?> targetType)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      */
isInstanceOf(Object bean, Class<?> targetType)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      */
isDesignTime()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      */
isGuiAvailable()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 
setDesignTime(boolean isDesignTime)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 
setGuiAvailable(boolean isGuiAvailable)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 
ObjectInputStreamWithLoader(InputStream in, ClassLoader loader)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")
resolveClass(ObjectStreamClass classDesc)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 
BeansAppletContext(Applet target)519     BeansAppletContext(Applet target) {
520         this.target = target;
521     }
522 
getAudioClip(URL url)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 
getImage(URL url)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 
getApplet(String name)558     public Applet getApplet(String name) {
559         return null;
560     }
561 
getApplets()562     public Enumeration<Applet> getApplets() {
563         Vector<Applet> applets = new Vector<>();
564         applets.addElement(target);
565         return applets.elements();
566     }
567 
showDocument(URL url)568     public void showDocument(URL url) {
569         // We do nothing.
570     }
571 
showDocument(URL url, String target)572     public void showDocument(URL url, String target) {
573         // We do nothing.
574     }
575 
showStatus(String status)576     public void showStatus(String status) {
577         // We do nothing.
578     }
579 
setStream(String key, InputStream stream)580     public void setStream(String key, InputStream stream)throws IOException{
581         // We do nothing.
582     }
583 
getStream(String key)584     public InputStream getStream(String key){
585         // We do nothing.
586         return null;
587     }
588 
getStreamKeys()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 
BeansAppletStub(Applet target, AppletContext context, URL codeBase, URL docBase)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 
isActive()616     public boolean isActive() {
617         return active;
618     }
619 
getDocumentBase()620     public URL getDocumentBase() {
621         // use the root directory of the applet's class-loader
622         return docBase;
623     }
624 
getCodeBase()625     public URL getCodeBase() {
626         // use the directory where we found the class or serialized object.
627         return codeBase;
628     }
629 
getParameter(String name)630     public String getParameter(String name) {
631         return null;
632     }
633 
getAppletContext()634     public AppletContext getAppletContext() {
635         return context;
636     }
637 
appletResize(int width, int height)638     public void appletResize(int width, int height) {
639         // we do nothing.
640     }
641 }
642