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