001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.jca;
031:
032: import com.caucho.config.program.ConfigProgram;
033: import com.caucho.config.Config;
034: import com.caucho.config.ConfigException;
035: import com.caucho.config.program.ContainerProgram;
036: import com.caucho.jca.cfg.JavaMailConfig;
037: import com.caucho.jmx.IntrospectionMBean;
038: import com.caucho.jmx.Jmx;
039: import com.caucho.loader.ClassLoaderListener;
040: import com.caucho.loader.CloseListener;
041: import com.caucho.loader.Environment;
042: import com.caucho.loader.EnvironmentListener;
043: import com.caucho.loader.StartListener;
044: import com.caucho.naming.Jndi;
045: import com.caucho.util.CharBuffer;
046: import com.caucho.util.L10N;
047: import com.caucho.webbeans.manager.WebBeansContainer;
048:
049: import javax.annotation.PostConstruct;
050: import javax.management.Attribute;
051: import javax.management.MBeanAttributeInfo;
052: import javax.management.MBeanInfo;
053: import javax.management.MBeanServer;
054: import javax.management.NotificationFilter;
055: import javax.management.ObjectName;
056: import javax.naming.Context;
057: import javax.naming.InitialContext;
058: import javax.resource.spi.ManagedConnectionFactory;
059: import javax.resource.spi.ResourceAdapter;
060: import java.lang.reflect.Constructor;
061: import java.lang.reflect.Method;
062: import java.util.ArrayList;
063: import java.util.logging.Level;
064: import java.util.logging.Logger;
065:
066: /**
067: * Configuration for the init-param pattern.
068: */
069: public class Resource {
070: private static final Logger log = Logger.getLogger(Resource.class
071: .getName());
072:
073: private static L10N L = new L10N(Resource.class);
074:
075: private Class _type;
076:
077: private String _var;
078: private String _name;
079: private String _jndiName;
080:
081: private String _mbeanName;
082:
083: private Class _mbeanInterface;
084:
085: // private ArrayList<BuilderProgram> _args = new ArrayList<BuilderProgram>();
086: private ArrayList<Object> _args = new ArrayList<Object>();
087:
088: private boolean _isPreInit;
089: private boolean _localTransactionOptimization = true;
090: private boolean _shareable = true;
091:
092: private Object _object;
093: private MBeanInfo _mbeanInfo;
094:
095: /**
096: * Sets the config variable name.
097: */
098: public void setVar(String var) {
099: _var = var;
100: }
101:
102: /**
103: * Sets the JNDI name
104: */
105: public void setJndiName(String name) {
106: _jndiName = name;
107: }
108:
109: /**
110: * Gets the JNDI name
111: */
112: public String getJndiName() {
113: return _jndiName;
114: }
115:
116: /**
117: * Sets the WebBeans name
118: */
119: public void setName(String name) {
120: _name = name;
121: }
122:
123: /**
124: * Gets the WebBeans name
125: */
126: public String getName() {
127: return _name;
128: }
129:
130: /**
131: * Sets the mbean name
132: */
133: public void setMbeanName(String name) {
134: _mbeanName = name;
135: }
136:
137: /**
138: * Gets the mbean name
139: */
140: public String getMbeanName() {
141: return _mbeanName;
142: }
143:
144: /**
145: * Sets the class
146: */
147: public void setType(Class resourceClass) {
148: if (resourceClass.getName().equals("javax.mail.Session"))
149: _type = JavaMailConfig.class;
150: else
151: _type = resourceClass;
152: }
153:
154: /**
155: * Sets the class
156: */
157: public void setClass(Class resourceClass) {
158: _type = resourceClass;
159: }
160:
161: /**
162: * Gets the type;
163: */
164: public Class getType() {
165: return _type;
166: }
167:
168: /**
169: * Sets the class
170: */
171: public void setMbeanInterface(Class cl) {
172: _mbeanInterface = cl;
173: }
174:
175: /**
176: * Adds an argument.
177: */
178: /*
179: public void addArg(BuilderProgram builder)
180: {
181: _args.add(builder);
182: }
183: */
184: public void addArg(Object arg) {
185: _args.add(arg);
186: }
187:
188: /**
189: * Sets the local-transaction-optimization flag
190: */
191: public void setLocalTransactionOptimization(boolean enable) {
192: _localTransactionOptimization = enable;
193: }
194:
195: /**
196: * Sets the shareable
197: */
198: public void setShareable(boolean shareable) {
199: _shareable = shareable;
200: }
201:
202: /**
203: * Adds the init program
204: */
205: public void addInit(ContainerProgram init) {
206: preInit();
207:
208: init.configure(_object);
209: }
210:
211: /**
212: * Adds the listener program
213: */
214: public Object createListener() throws Exception {
215: return createMbeanListener();
216: }
217:
218: /**
219: * Adds the listener program
220: */
221: public Object createMbeanListener() throws Exception {
222: preInit();
223:
224: if (_mbeanName != null)
225: return new MBeanListener();
226: else
227: throw new ConfigException(
228: L
229: .l("<listener> needs a <resource> with an mbean-name."));
230: }
231:
232: ObjectName getObjectName() throws Exception {
233: preInit();
234:
235: if (_mbeanName != null)
236: return Jmx.getObjectName(_mbeanName);
237: else
238: return null;
239: }
240:
241: MBeanInfo getMBeanInfo() throws Exception {
242: preInit();
243:
244: return _mbeanInfo;
245: }
246:
247: /**
248: * Initialize the resource.
249: */
250: private void preInit() {
251: try {
252: if (_isPreInit)
253: return;
254: _isPreInit = true;
255:
256: Object oldObject = null;
257:
258: if (_jndiName != null) {
259: try {
260: String jndiName = Jndi.getFullName(_jndiName);
261:
262: Context ic = new InitialContext();
263: oldObject = ic.lookup(_jndiName);
264: } catch (Exception e) {
265: }
266: }
267:
268: MBeanServer mbeanServer = Jmx.getMBeanServer();
269:
270: ObjectName mbeanName = null;
271:
272: if (_mbeanName != null)
273: mbeanName = Jmx.getObjectName(_mbeanName);
274:
275: if (_type != null) {
276: } else if (oldObject != null) {
277: _object = oldObject;
278: return;
279: } else if (mbeanName != null
280: && mbeanServer.getMBeanInfo(mbeanName) != null) {
281: return;
282: } else
283: throw new ConfigException(
284: L
285: .l("<resource> configuration needs a <type>. The <type> is the class name of the resource bean."));
286:
287: Constructor constructor = getConstructor(_args.size());
288:
289: Class[] params = constructor.getParameterTypes();
290:
291: Object[] args = new Object[_args.size()];
292:
293: /*
294: for (int i = 0; i < args.length; i++)
295: args[i] = _args.get(i).configure(params[i]);
296: */
297: for (int i = 0; i < args.length; i++)
298: args[i] = _args.get(i);
299:
300: _object = constructor.newInstance(args);
301:
302: if (mbeanName != null) {
303: Object mbean = _object;
304:
305: if (_mbeanInterface != null)
306: mbean = new IntrospectionMBean(mbean,
307: _mbeanInterface);
308:
309: Jmx.register(mbean, mbeanName);
310: _mbeanInfo = mbeanServer.getMBeanInfo(mbeanName);
311: }
312: } catch (Exception e) {
313: throw ConfigException.create(e);
314: }
315: }
316:
317: /**
318: * Returns the constructor based on the length.
319: */
320: private Constructor getConstructor(int len) throws Exception {
321: Constructor[] constructors = _type.getConstructors();
322:
323: for (int i = 0; i < constructors.length; i++) {
324: if (constructors[i].getParameterTypes().length == len)
325: return constructors[i];
326: }
327:
328: throw new ConfigException(L.l(
329: "`{0}' has no matching constructors.", _type.getName()));
330: }
331:
332: /**
333: * Initialize the resource.
334: */
335: @PostConstruct
336: public void init() throws Throwable {
337: preInit();
338:
339: if (_type == null || _object == null)
340: return;
341:
342: Config.init(_object);
343: _object = Config.replaceObject(_object);
344:
345: if (_object instanceof ClassLoaderListener) {
346: ClassLoaderListener listener = (ClassLoaderListener) _object;
347:
348: Environment.addClassLoaderListener(listener);
349: }
350:
351: if (_object instanceof EnvironmentListener) {
352: EnvironmentListener listener = (EnvironmentListener) _object;
353:
354: Environment.addEnvironmentListener(listener);
355: }
356:
357: Object jndiObject = _object;
358: boolean isStart = false;
359:
360: if (_object instanceof ResourceAdapter) {
361: ResourceManagerImpl.addResource((ResourceAdapter) _object);
362: isStart = true;
363: }
364:
365: if (_object instanceof ManagedConnectionFactory) {
366: ResourceManagerImpl rm = ResourceManagerImpl
367: .createLocalManager();
368:
369: ManagedConnectionFactory mcf;
370: mcf = (ManagedConnectionFactory) _object;
371:
372: ConnectionPool cm = rm.createConnectionPool();
373:
374: cm.setShareable(_shareable);
375: cm
376: .setLocalTransactionOptimization(_localTransactionOptimization);
377: Object connectionFactory = cm.init(mcf);
378: cm.start();
379:
380: jndiObject = connectionFactory;
381:
382: isStart = true;
383: }
384:
385: Method start = null;
386: try {
387: start = _object.getClass().getMethod("start", new Class[0]);
388: } catch (Throwable e) {
389: }
390:
391: Method stop = null;
392: try {
393: stop = _object.getClass().getMethod("stop", new Class[0]);
394: } catch (Throwable e) {
395: }
396:
397: if (_jndiName != null)
398: Jndi.bindDeepShort(_jndiName, jndiObject);
399:
400: if (isStart) {
401: } else if (start != null || stop != null)
402: Environment.addEnvironmentListener(new StartListener(
403: _object));
404: else if (CloseListener.getDestroyMethod(_object.getClass()) != null)
405: Environment.addClassLoaderListener(new CloseListener(
406: _object));
407:
408: String name = _name;
409:
410: if (_name == null)
411: name = _var;
412:
413: if (_name == null)
414: name = _jndiName;
415:
416: WebBeansContainer webBeans = WebBeansContainer.create();
417:
418: if (name != null)
419: webBeans.addSingleton(_object, name);
420: else
421: webBeans.addSingleton(_object);
422:
423: if (log.isLoggable(Level.CONFIG))
424: logConfig();
425: }
426:
427: private void logConfig() {
428: StringBuilder sb = new StringBuilder();
429:
430: if (_object instanceof ResourceAdapter)
431: sb.append("jca-resource");
432: else if (_object instanceof ManagedConnectionFactory)
433: sb.append("jca-resource");
434: else
435: sb.append("resource");
436:
437: sb.append("[");
438: boolean hasValue = false;
439:
440: if (_jndiName != null) {
441: if (hasValue)
442: sb.append(", ");
443: hasValue = true;
444: sb.append("jndi-name=" + _jndiName);
445: }
446:
447: if (_var != null) {
448: if (hasValue)
449: sb.append(", ");
450: hasValue = true;
451: sb.append("var=" + _var);
452: }
453:
454: if (_mbeanName != null) {
455: if (hasValue)
456: sb.append(", ");
457: hasValue = true;
458: sb.append("mbean-name=" + _mbeanName);
459: }
460:
461: if (_type != null) {
462: if (hasValue)
463: sb.append(", ");
464: hasValue = true;
465: sb.append("type=" + _type);
466: }
467:
468: sb.append("]");
469:
470: log.config(sb.toString() + " configured");
471: }
472:
473: public String toString() {
474: if (_mbeanName != null)
475: return "Resource[" + _mbeanName + "]";
476: else
477: return "Resource[" + _jndiName + "]";
478: }
479:
480: public class MBeanInit {
481: public void setProperty(String attrName, ConfigProgram program)
482: throws Throwable {
483: MBeanAttributeInfo attr = getAttribute(attrName);
484: if (attr == null)
485: throw new ConfigException(L.l(
486: "`{0}' is an unknown attribute for {1}",
487: attrName, _mbeanName));
488:
489: String typeName = attr.getType();
490: ClassLoader loader = Thread.currentThread()
491: .getContextClassLoader();
492: Class type = Class.forName(typeName, false, loader);
493:
494: Object value = program.configure(type);
495:
496: MBeanServer server = Jmx.getMBeanServer();
497:
498: server.setAttribute(getObjectName(), new Attribute(attr
499: .getName(), value));
500: }
501:
502: private MBeanAttributeInfo getAttribute(String key)
503: throws Throwable {
504: MBeanInfo info = getMBeanInfo();
505:
506: MBeanAttributeInfo[] attrs = info.getAttributes();
507:
508: if (attrs == null)
509: return null;
510:
511: for (int i = 0; i < attrs.length; i++) {
512: if (attrs[i].getName().equals(key))
513: return attrs[i];
514: }
515:
516: for (int i = 0; i < attrs.length; i++) {
517: if (convertName(attrs[i].getName()).equals(key))
518: return attrs[i];
519: }
520:
521: return null;
522: }
523:
524: private String convertName(String key) {
525: CharBuffer cb = CharBuffer.allocate();
526:
527: for (int i = 0; i < key.length(); i++) {
528: char ch = key.charAt(i);
529:
530: if (!Character.isUpperCase(ch))
531: cb.append(ch);
532: else if (i == 0)
533: cb.append(Character.toLowerCase(ch));
534: else if (Character.isLowerCase(key.charAt(i - 1))) {
535: cb.append('-');
536: cb.append(Character.toLowerCase(ch));
537: } else if (i + 1 != key.length()
538: && Character.isLowerCase(key.charAt(i + 1))) {
539: cb.append('-');
540: cb.append(Character.toLowerCase(ch));
541: } else
542: cb.append(Character.toLowerCase(ch));
543: }
544:
545: return cb.close();
546: }
547: }
548:
549: public class MBeanListener {
550: private String _mbeanName;
551: private Object _handback;
552: private NotificationFilter _filter;
553:
554: public void setMbeanName(String name) {
555: _mbeanName = name;
556: }
557:
558: public String getMBeanName() {
559: return _mbeanName;
560: }
561:
562: public void setHandback(Object handback) {
563: _handback = handback;
564: }
565:
566: public Object getHandback() {
567: return _handback;
568: }
569:
570: @PostConstruct
571: public void init() {
572: try {
573: if (_mbeanName != null) {
574: ObjectName mbeanName = Jmx
575: .getObjectName(_mbeanName);
576:
577: ObjectName listenerName = getObjectName();
578:
579: MBeanServer server = Jmx.getMBeanServer();
580:
581: server.addNotificationListener(mbeanName,
582: listenerName, _filter, _handback);
583:
584: } else
585: throw new ConfigException(L
586: .l("mbean name is required"));
587: } catch (Exception e) {
588: throw ConfigException.create(e);
589: }
590: }
591: }
592: }
|