001 /*
002 * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package java.beans;
027
028 import com.sun.beans.finder.ClassFinder;
029
030 import java.applet.*;
031
032 import java.awt.*;
033
034 import java.beans.AppletInitializer;
035
036 import java.beans.beancontext.BeanContext;
037
038 import java.io.*;
039
040 import java.lang.reflect.Constructor;
041
042 import java.net.URL;
043 import java.lang.reflect.Array;
044
045 /**
046 * This class provides some general purpose beans control methods.
047 */
048
049 public class Beans {
050
051 /**
052 * <p>
053 * Instantiate a JavaBean.
054 * </p>
055 *
056 * @param cls the class-loader from which we should create
057 * the bean. If this is null, then the system
058 * class-loader is used.
059 * @param beanName the name of the bean within the class-loader.
060 * For example "sun.beanbox.foobah"
061 *
062 * @exception java.lang.ClassNotFoundException if the class of a serialized
063 * object could not be found.
064 * @exception java.io.IOException if an I/O error occurs.
065 */
066
067 public static Object instantiate(ClassLoader cls, String beanName)
068 throws java.io.IOException, ClassNotFoundException {
069 return Beans.instantiate(cls, beanName, null, null);
070 }
071
072 /**
073 * <p>
074 * Instantiate a JavaBean.
075 * </p>
076 *
077 * @param cls the class-loader from which we should create
078 * the bean. If this is null, then the system
079 * class-loader is used.
080 * @param beanName the name of the bean within the class-loader.
081 * For example "sun.beanbox.foobah"
082 * @param beanContext The BeanContext in which to nest the new bean
083 *
084 * @exception java.lang.ClassNotFoundException if the class of a serialized
085 * object could not be found.
086 * @exception java.io.IOException if an I/O error occurs.
087 */
088
089 public static Object instantiate(ClassLoader cls, String beanName,
090 BeanContext beanContext) throws java.io.IOException,
091 ClassNotFoundException {
092 return Beans.instantiate(cls, beanName, beanContext, null);
093 }
094
095 /**
096 * Instantiate a bean.
097 * <p>
098 * The bean is created based on a name relative to a class-loader.
099 * This name should be a dot-separated name such as "a.b.c".
100 * <p>
101 * In Beans 1.0 the given name can indicate either a serialized object
102 * or a class. Other mechanisms may be added in the future. In
103 * beans 1.0 we first try to treat the beanName as a serialized object
104 * name then as a class name.
105 * <p>
106 * When using the beanName as a serialized object name we convert the
107 * given beanName to a resource pathname and add a trailing ".ser" suffix.
108 * We then try to load a serialized object from that resource.
109 * <p>
110 * For example, given a beanName of "x.y", Beans.instantiate would first
111 * try to read a serialized object from the resource "x/y.ser" and if
112 * that failed it would try to load the class "x.y" and create an
113 * instance of that class.
114 * <p>
115 * If the bean is a subtype of java.applet.Applet, then it is given
116 * some special initialization. First, it is supplied with a default
117 * AppletStub and AppletContext. Second, if it was instantiated from
118 * a classname the applet's "init" method is called. (If the bean was
119 * deserialized this step is skipped.)
120 * <p>
121 * Note that for beans which are applets, it is the caller's responsiblity
122 * to call "start" on the applet. For correct behaviour, this should be done
123 * after the applet has been added into a visible AWT container.
124 * <p>
125 * Note that applets created via beans.instantiate run in a slightly
126 * different environment than applets running inside browsers. In
127 * particular, bean applets have no access to "parameters", so they may
128 * wish to provide property get/set methods to set parameter values. We
129 * advise bean-applet developers to test their bean-applets against both
130 * the JDK appletviewer (for a reference browser environment) and the
131 * BDK BeanBox (for a reference bean container).
132 *
133 * @param cls the class-loader from which we should create
134 * the bean. If this is null, then the system
135 * class-loader is used.
136 * @param beanName the name of the bean within the class-loader.
137 * For example "sun.beanbox.foobah"
138 * @param beanContext The BeanContext in which to nest the new bean
139 * @param initializer The AppletInitializer for the new bean
140 *
141 * @exception java.lang.ClassNotFoundException if the class of a serialized
142 * object could not be found.
143 * @exception java.io.IOException if an I/O error occurs.
144 */
145
146 public static Object instantiate(ClassLoader cls, String beanName,
147 BeanContext beanContext, AppletInitializer initializer)
148 throws java.io.IOException, ClassNotFoundException {
149
150 java.io.InputStream ins;
151 java.io.ObjectInputStream oins = null;
152 Object result = null;
153 boolean serialized = false;
154 java.io.IOException serex = null;
155
156 // If the given classloader is null, we check if an
157 // system classloader is available and (if so)
158 // use that instead.
159 // Note that calls on the system class loader will
160 // look in the bootstrap class loader first.
161 if (cls == null) {
162 try {
163 cls = ClassLoader.getSystemClassLoader();
164 } catch (SecurityException ex) {
165 // We're not allowed to access the system class loader.
166 // Drop through.
167 }
168 }
169
170 // Try to find a serialized object with this name
171 final String serName = beanName.replace('.', '/')
172 .concat(".ser");
173 final ClassLoader loader = cls;
174 ins = (InputStream) java.security.AccessController
175 .doPrivileged(new java.security.PrivilegedAction() {
176 public Object run() {
177 if (loader == null)
178 return ClassLoader
179 .getSystemResourceAsStream(serName);
180 else
181 return loader.getResourceAsStream(serName);
182 }
183 });
184 if (ins != null) {
185 try {
186 if (cls == null) {
187 oins = new ObjectInputStream(ins);
188 } else {
189 oins = new ObjectInputStreamWithLoader(ins, cls);
190 }
191 result = oins.readObject();
192 serialized = true;
193 oins.close();
194 } catch (java.io.IOException ex) {
195 ins.close();
196 // Drop through and try opening the class. But remember
197 // the exception in case we can't find the class either.
198 serex = ex;
199 } catch (ClassNotFoundException ex) {
200 ins.close();
201 throw ex;
202 }
203 }
204
205 if (result == null) {
206 // No serialized object, try just instantiating the class
207 Class cl;
208
209 try {
210 cl = ClassFinder.findClass(beanName, cls);
211 } catch (ClassNotFoundException ex) {
212 // There is no appropriate class. If we earlier tried to
213 // deserialize an object and got an IO exception, throw that,
214 // otherwise rethrow the ClassNotFoundException.
215 if (serex != null) {
216 throw serex;
217 }
218 throw ex;
219 }
220
221 /*
222 * Try to instantiate the class.
223 */
224
225 try {
226 result = cl.newInstance();
227 } catch (Exception ex) {
228 // We have to remap the exception to one in our signature.
229 // But we pass extra information in the detail message.
230 throw new ClassNotFoundException("" + cl + " : " + ex,
231 ex);
232 }
233 }
234
235 if (result != null) {
236
237 // Ok, if the result is an applet initialize it.
238
239 AppletStub stub = null;
240
241 if (result instanceof Applet) {
242 Applet applet = (Applet) result;
243 boolean needDummies = initializer == null;
244
245 if (needDummies) {
246
247 // Figure our the codebase and docbase URLs. We do this
248 // by locating the URL for a known resource, and then
249 // massaging the URL.
250
251 // First find the "resource name" corresponding to the bean
252 // itself. So a serialzied bean "a.b.c" would imply a
253 // resource name of "a/b/c.ser" and a classname of "x.y"
254 // would imply a resource name of "x/y.class".
255
256 final String resourceName;
257
258 if (serialized) {
259 // Serialized bean
260 resourceName = beanName.replace('.', '/')
261 .concat(".ser");
262 } else {
263 // Regular class
264 resourceName = beanName.replace('.', '/')
265 .concat(".class");
266 }
267
268 URL objectUrl = null;
269 URL codeBase = null;
270 URL docBase = null;
271
272 // Now get the URL correponding to the resource name.
273
274 final ClassLoader cloader = cls;
275 objectUrl = (URL) java.security.AccessController
276 .doPrivileged(new java.security.PrivilegedAction() {
277 public Object run() {
278 if (cloader == null)
279 return ClassLoader
280 .getSystemResource(resourceName);
281 else
282 return cloader
283 .getResource(resourceName);
284 }
285 });
286
287 // If we found a URL, we try to locate the docbase by taking
288 // of the final path name component, and the code base by taking
289 // of the complete resourceName.
290 // So if we had a resourceName of "a/b/c.class" and we got an
291 // objectURL of "file://bert/classes/a/b/c.class" then we would
292 // want to set the codebase to "file://bert/classes/" and the
293 // docbase to "file://bert/classes/a/b/"
294
295 if (objectUrl != null) {
296 String s = objectUrl.toExternalForm();
297
298 if (s.endsWith(resourceName)) {
299 int ix = s.length() - resourceName.length();
300 codeBase = new URL(s.substring(0, ix));
301 docBase = codeBase;
302
303 ix = s.lastIndexOf('/');
304
305 if (ix >= 0) {
306 docBase = new URL(s
307 .substring(0, ix + 1));
308 }
309 }
310 }
311
312 // Setup a default context and stub.
313 BeansAppletContext context = new BeansAppletContext(
314 applet);
315
316 stub = (AppletStub) new BeansAppletStub(applet,
317 context, codeBase, docBase);
318 applet.setStub(stub);
319 } else {
320 initializer.initialize(applet, beanContext);
321 }
322
323 // now, if there is a BeanContext, add the bean, if applicable.
324
325 if (beanContext != null) {
326 beanContext.add(result);
327 }
328
329 // If it was deserialized then it was already init-ed.
330 // Otherwise we need to initialize it.
331
332 if (!serialized) {
333 // We need to set a reasonable initial size, as many
334 // applets are unhappy if they are started without
335 // having been explicitly sized.
336 applet.setSize(100, 100);
337 applet.init();
338 }
339
340 if (needDummies) {
341 ((BeansAppletStub) stub).active = true;
342 } else
343 initializer.activate(applet);
344
345 } else if (beanContext != null)
346 beanContext.add(result);
347 }
348
349 return result;
350 }
351
352 /**
353 * From a given bean, obtain an object representing a specified
354 * type view of that source object.
355 * <p>
356 * The result may be the same object or a different object. If
357 * the requested target view isn't available then the given
358 * bean is returned.
359 * <p>
360 * This method is provided in Beans 1.0 as a hook to allow the
361 * addition of more flexible bean behaviour in the future.
362 *
363 * @param bean Object from which we want to obtain a view.
364 * @param targetType The type of view we'd like to get.
365 *
366 */
367 public static Object getInstanceOf(Object bean, Class<?> targetType) {
368 return bean;
369 }
370
371 /**
372 * Check if a bean can be viewed as a given target type.
373 * The result will be true if the Beans.getInstanceof method
374 * can be used on the given bean to obtain an object that
375 * represents the specified targetType type view.
376 *
377 * @param bean Bean from which we want to obtain a view.
378 * @param targetType The type of view we'd like to get.
379 * @return "true" if the given bean supports the given targetType.
380 *
381 */
382 public static boolean isInstanceOf(Object bean, Class<?> targetType) {
383 return Introspector.isSubclass(bean.getClass(), targetType);
384 }
385
386 /**
387 * Test if we are in design-mode.
388 *
389 * @return True if we are running in an application construction
390 * environment.
391 *
392 * @see java.beans.DesignMode
393 */
394 public static boolean isDesignTime() {
395 return designTime;
396 }
397
398 /**
399 * Determines whether beans can assume a GUI is available.
400 *
401 * @return True if we are running in an environment where beans
402 * can assume that an interactive GUI is available, so they
403 * can pop up dialog boxes, etc. This will normally return
404 * true in a windowing environment, and will normally return
405 * false in a server environment or if an application is
406 * running as part of a batch job.
407 *
408 * @see java.beans.Visibility
409 *
410 */
411 public static boolean isGuiAvailable() {
412 return guiAvailable;
413 }
414
415 /**
416 * Used to indicate whether of not we are running in an application
417 * builder environment.
418 *
419 * <p>Note that this method is security checked
420 * and is not available to (for example) untrusted applets.
421 * More specifically, if there is a security manager,
422 * its <code>checkPropertiesAccess</code>
423 * method is called. This could result in a SecurityException.
424 *
425 * @param isDesignTime True if we're in an application builder tool.
426 * @exception SecurityException if a security manager exists and its
427 * <code>checkPropertiesAccess</code> method doesn't allow setting
428 * of system properties.
429 * @see SecurityManager#checkPropertiesAccess
430 */
431
432 public static void setDesignTime(boolean isDesignTime)
433 throws SecurityException {
434 SecurityManager sm = System.getSecurityManager();
435 if (sm != null) {
436 sm.checkPropertiesAccess();
437 }
438 designTime = isDesignTime;
439 }
440
441 /**
442 * Used to indicate whether of not we are running in an environment
443 * where GUI interaction is available.
444 *
445 * <p>Note that this method is security checked
446 * and is not available to (for example) untrusted applets.
447 * More specifically, if there is a security manager,
448 * its <code>checkPropertiesAccess</code>
449 * method is called. This could result in a SecurityException.
450 *
451 * @param isGuiAvailable True if GUI interaction is available.
452 * @exception SecurityException if a security manager exists and its
453 * <code>checkPropertiesAccess</code> method doesn't allow setting
454 * of system properties.
455 * @see SecurityManager#checkPropertiesAccess
456 */
457
458 public static void setGuiAvailable(boolean isGuiAvailable)
459 throws SecurityException {
460 SecurityManager sm = System.getSecurityManager();
461 if (sm != null) {
462 sm.checkPropertiesAccess();
463 }
464 guiAvailable = isGuiAvailable;
465 }
466
467 private static boolean designTime;
468 private static boolean guiAvailable;
469 static {
470 guiAvailable = !GraphicsEnvironment.isHeadless();
471 }
472 }
473
474 /**
475 * This subclass of ObjectInputStream delegates loading of classes to
476 * an existing ClassLoader.
477 */
478
479 class ObjectInputStreamWithLoader extends ObjectInputStream {
480 private ClassLoader loader;
481
482 /**
483 * Loader must be non-null;
484 */
485
486 public ObjectInputStreamWithLoader(InputStream in,
487 ClassLoader loader) throws IOException,
488 StreamCorruptedException {
489
490 super (in);
491 if (loader == null) {
492 throw new IllegalArgumentException(
493 "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 protected Class resolveClass(ObjectStreamClass classDesc)
502 throws IOException, ClassNotFoundException {
503
504 String cname = classDesc.getName();
505 return ClassFinder.resolveClass(cname, this .loader);
506 }
507 }
508
509 /**
510 * Package private support class. This provides a default AppletContext
511 * for beans which are applets.
512 */
513
514 class BeansAppletContext implements AppletContext {
515 Applet target;
516 java.util.Hashtable imageCache = new java.util.Hashtable();
517
518 BeansAppletContext(Applet target) {
519 this .target = target;
520 }
521
522 public AudioClip getAudioClip(URL url) {
523 // We don't currently support audio clips in the Beans.instantiate
524 // applet context, unless by some luck there exists a URL content
525 // class that can generate an AudioClip from the audio URL.
526 try {
527 return (AudioClip) url.getContent();
528 } catch (Exception ex) {
529 return null;
530 }
531 }
532
533 public synchronized Image getImage(URL url) {
534 Object o = imageCache.get(url);
535 if (o != null) {
536 return (Image) o;
537 }
538 try {
539 o = url.getContent();
540 if (o == null) {
541 return null;
542 }
543 if (o instanceof Image) {
544 imageCache.put(url, o);
545 return (Image) o;
546 }
547 // Otherwise it must be an ImageProducer.
548 Image img = target
549 .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
558 public Applet getApplet(String name) {
559 return null;
560 }
561
562 public java.util.Enumeration getApplets() {
563 java.util.Vector applets = new java.util.Vector();
564 applets.addElement(target);
565 return applets.elements();
566 }
567
568 public void showDocument(URL url) {
569 // We do nothing.
570 }
571
572 public void showDocument(URL url, String target) {
573 // We do nothing.
574 }
575
576 public void showStatus(String status) {
577 // We do nothing.
578 }
579
580 public void setStream(String key, InputStream stream)
581 throws IOException {
582 // We do nothing.
583 }
584
585 public InputStream getStream(String key) {
586 // We do nothing.
587 return null;
588 }
589
590 public java.util.Iterator getStreamKeys() {
591 // We do nothing.
592 return null;
593 }
594 }
595
596 /**
597 * Package private support class. This provides an AppletStub
598 * for beans which are applets.
599 */
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
607 BeansAppletStub(Applet target, AppletContext context, URL codeBase,
608 URL docBase) {
609 this .target = target;
610 this .context = context;
611 this .codeBase = codeBase;
612 this .docBase = docBase;
613 }
614
615 public boolean isActive() {
616 return active;
617 }
618
619 public URL getDocumentBase() {
620 // use the root directory of the applet's class-loader
621 return docBase;
622 }
623
624 public URL getCodeBase() {
625 // use the directory where we found the class or serialized object.
626 return codeBase;
627 }
628
629 public String getParameter(String name) {
630 return null;
631 }
632
633 public AppletContext getAppletContext() {
634 return context;
635 }
636
637 public void appletResize(int width, int height) {
638 // we do nothing.
639 }
640 }
|