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