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.config;
031:
032: import com.caucho.config.attribute.*;
033: import com.caucho.config.type.*;
034: import com.caucho.config.types.*;
035: import com.caucho.el.EL;
036: import com.caucho.el.EnvironmentContext;
037: import com.caucho.relaxng.*;
038: import com.caucho.util.L10N;
039: import com.caucho.util.LruCache;
040: import com.caucho.vfs.*;
041: import com.caucho.webbeans.manager.WebBeansContainer;
042: import com.caucho.xml.DOMBuilder;
043: import com.caucho.xml.QDocument;
044: import com.caucho.xml.QName;
045: import com.caucho.xml.QAttr;
046: import com.caucho.xml.Xml;
047:
048: import org.w3c.dom.Node;
049: import org.xml.sax.InputSource;
050:
051: import javax.el.ELContext;
052: import javax.el.ELException;
053: import java.io.IOException;
054: import java.io.InputStream;
055: import java.lang.ref.SoftReference;
056: import java.lang.reflect.*;
057: import java.net.*;
058: import java.util.HashMap;
059: import java.util.Map;
060: import java.util.logging.Logger;
061:
062: /**
063: * Facade for Resin's configuration builder.
064: */
065: public class Config {
066: private static final L10N L = new L10N(Config.class);
067: private static final Logger log = Logger.getLogger(Config.class
068: .getName());
069:
070: private HashMap<String, Object> _vars = new HashMap<String, Object>();
071:
072: // the context class loader of the config
073: private ClassLoader _classLoader;
074:
075: private boolean _isEL = true;
076: private boolean _isIgnoreEnvironment;
077: private boolean _allowResinInclude;
078:
079: public Config() {
080: this (Thread.currentThread().getContextClassLoader());
081: }
082:
083: /**
084: * @param loader the class loader environment to use.
085: */
086: public Config(ClassLoader loader) {
087: _classLoader = loader;
088: }
089:
090: /**
091: * Set true if resin:include should be allowed.
092: */
093: public void setResinInclude(boolean useResinInclude) {
094: _allowResinInclude = useResinInclude;
095: }
096:
097: /**
098: * True if EL expressions are allowed
099: */
100: public boolean isEL() {
101: return _isEL;
102: }
103:
104: /**
105: * True if EL expressions are allowed
106: */
107: public void setEL(boolean isEL) {
108: _isEL = isEL;
109: }
110:
111: /**
112: * True if environment tags are ignored
113: */
114: public boolean isIgnoreEnvironment() {
115: return _isIgnoreEnvironment;
116: }
117:
118: /**
119: * True if environment tags are ignored
120: */
121: public void setIgnoreEnvironment(boolean isIgnore) {
122: _isIgnoreEnvironment = isIgnore;
123: }
124:
125: /**
126: * Configures a bean with a configuration file.
127: */
128: public Object configure(Object obj, Path path)
129: throws ConfigException {
130: try {
131: QDocument doc = parseDocument(path, null);
132:
133: return configure(obj, doc.getDocumentElement());
134: } catch (RuntimeException e) {
135: throw e;
136: } catch (Exception e) {
137: throw ConfigException.create(e);
138: }
139: }
140:
141: /**
142: * Configures a bean with a configuration file.
143: */
144: public Object configure(Object obj, InputStream is)
145: throws Exception {
146: QDocument doc = parseDocument(is, null);
147:
148: return configure(obj, doc.getDocumentElement());
149: }
150:
151: /**
152: * Configures a bean with a configuration file and schema.
153: */
154: public Object configure(Object obj, Path path, String schemaLocation)
155: throws ConfigException {
156: try {
157: Schema schema = findCompactSchema(schemaLocation);
158:
159: QDocument doc = parseDocument(path, schema);
160:
161: return configure(obj, doc.getDocumentElement());
162: } catch (RuntimeException e) {
163: throw e;
164: } catch (Exception e) {
165: throw LineConfigException.create(e);
166: }
167: }
168:
169: /**
170: * Configures a bean with a configuration file and schema.
171: */
172: public Object configure(Object obj, Path path, Schema schema)
173: throws ConfigException {
174: try {
175: QDocument doc = parseDocument(path, schema);
176:
177: return configure(obj, doc.getDocumentElement());
178: } catch (RuntimeException e) {
179: throw e;
180: } catch (Exception e) {
181: throw ConfigException.create(e);
182: }
183: }
184:
185: /**
186: * Configures a bean with a configuration file.
187: */
188: public Object configure(Object obj, InputStream is,
189: String schemaLocation) throws Exception {
190: Schema schema = findCompactSchema(schemaLocation);
191:
192: QDocument doc = parseDocument(is, schema);
193:
194: return configure(obj, doc.getDocumentElement());
195: }
196:
197: /**
198: * Configures a bean with a configuration file.
199: */
200: public Object configure(Object obj, InputStream is, Schema schema)
201: throws Exception {
202: QDocument doc = parseDocument(is, schema);
203:
204: return configure(obj, doc.getDocumentElement());
205: }
206:
207: /**
208: * Configures a bean with a DOM.
209: */
210: public Object configure(Object obj, Node topNode) throws Exception {
211: Thread thread = Thread.currentThread();
212: ClassLoader oldLoader = thread.getContextClassLoader();
213:
214: try {
215: thread.setContextClassLoader(_classLoader);
216:
217: ConfigContext builder = createBuilder();
218:
219: WebBeansContainer webBeans = WebBeansContainer.create();
220:
221: if (webBeans != null
222: && webBeans.findByName("__FILE__") == null)
223: webBeans.addSingleton(FileVar.__FILE__, "__FILE__");
224:
225: return builder.configure(obj, topNode);
226: } finally {
227: thread.setContextClassLoader(oldLoader);
228: }
229: }
230:
231: /**
232: * Configures a bean with a configuration file and schema.
233: */
234: public void configureBean(Object obj, Path path,
235: String schemaLocation) throws Exception {
236: Schema schema = findCompactSchema(schemaLocation);
237:
238: QDocument doc = parseDocument(path, schema);
239:
240: configureBean(obj, doc.getDocumentElement());
241: }
242:
243: /**
244: * Configures a bean with a configuration file and schema.
245: */
246: public void configureBean(Object obj, Path path) throws Exception {
247: QDocument doc = parseDocument(path, null);
248:
249: configureBean(obj, doc.getDocumentElement());
250: }
251:
252: /**
253: * Configures a bean with a DOM. configureBean does not
254: * apply init() or replaceObject().
255: */
256: public void configureBean(Object obj, Node topNode)
257: throws Exception {
258: Thread thread = Thread.currentThread();
259: ClassLoader oldLoader = thread.getContextClassLoader();
260:
261: try {
262: thread.setContextClassLoader(_classLoader);
263:
264: ConfigContext builder = createBuilder();
265:
266: builder.configureBean(obj, topNode);
267: } finally {
268: thread.setContextClassLoader(oldLoader);
269: }
270: }
271:
272: private ConfigContext createBuilder() {
273: return new ConfigContext(this );
274: }
275:
276: /**
277: * Configures a bean with a configuration file and schema.
278: */
279: public void configureBean(Object obj, Path path, Schema schema)
280: throws Exception {
281: QDocument doc = parseDocument(path, schema);
282:
283: configureBean(obj, doc.getDocumentElement());
284: }
285:
286: /**
287: * Configures the bean from a path
288: */
289: private QDocument parseDocument(Path path, Schema schema)
290: throws LineConfigException, IOException,
291: org.xml.sax.SAXException {
292: // server/2d33
293: SoftReference<QDocument> docRef = null;//_parseCache.get(path);
294: QDocument doc;
295:
296: if (docRef != null) {
297: doc = docRef.get();
298:
299: if (doc != null && !doc.isModified())
300: return doc;
301: }
302:
303: ReadStream is = path.openRead();
304:
305: try {
306: doc = parseDocument(is, schema);
307:
308: // _parseCache.put(path, new SoftReference<QDocument>(doc));
309:
310: return doc;
311: } finally {
312: is.close();
313: }
314: }
315:
316: /**
317: * Configures the bean from an input stream.
318: */
319: private QDocument parseDocument(InputStream is, Schema schema)
320: throws LineConfigException, IOException,
321: org.xml.sax.SAXException {
322: QDocument doc = new QDocument();
323: DOMBuilder builder = new DOMBuilder();
324:
325: builder.init(doc);
326: String systemId = null;
327: String filename = null;
328: if (is instanceof ReadStream) {
329: systemId = ((ReadStream) is).getPath().getURL();
330: filename = ((ReadStream) is).getPath().getUserPath();
331: }
332:
333: doc.setSystemId(systemId);
334: builder.setSystemId(systemId);
335: doc.setRootFilename(filename);
336: builder.setFilename(filename);
337: builder.setSkipWhitespace(true);
338:
339: InputSource in = new InputSource();
340: in.setByteStream(is);
341: in.setSystemId(systemId);
342:
343: Xml xml = new Xml();
344: xml.setOwner(doc);
345: xml.setResinInclude(_allowResinInclude);
346: xml.setFilename(filename);
347:
348: if (schema != null) {
349: Verifier verifier = schema.newVerifier();
350: VerifierFilter filter = verifier.getVerifierFilter();
351:
352: filter.setParent(xml);
353: filter.setContentHandler(builder);
354: filter.setErrorHandler(builder);
355:
356: filter.parse(in);
357: } else {
358: xml.setContentHandler(builder);
359: xml.parse(in);
360: }
361:
362: return doc;
363: }
364:
365: private Schema findCompactSchema(String location)
366: throws IOException, ConfigException {
367: try {
368: if (location == null)
369: return null;
370:
371: Thread thread = Thread.currentThread();
372: ClassLoader loader = thread.getContextClassLoader();
373:
374: if (loader == null)
375: loader = ClassLoader.getSystemClassLoader();
376:
377: URL url = loader.getResource(location);
378:
379: if (url == null)
380: return null;
381:
382: Path path = Vfs.lookup(URLDecoder.decode(url.toString()));
383:
384: // VerifierFactory factory = VerifierFactory.newInstance("http://caucho.com/ns/compact-relax-ng/1.0");
385:
386: CompactVerifierFactoryImpl factory;
387: factory = new CompactVerifierFactoryImpl();
388:
389: return factory.compileSchema(path);
390: } catch (IOException e) {
391: throw e;
392: } catch (Exception e) {
393: throw ConfigException.create(e);
394: }
395: }
396:
397: /**
398: * Returns true if the class can be instantiated.
399: */
400: public static void checkCanInstantiate(Class beanClass)
401: throws ConfigException {
402: if (beanClass == null)
403: throw new ConfigException(L
404: .l("null classes can't be instantiated."));
405: else if (beanClass.isInterface())
406: throw new ConfigException(
407: L
408: .l(
409: "`{0}' must be a concrete class. Interfaces cannot be instantiated.",
410: beanClass.getName()));
411: else if (!Modifier.isPublic(beanClass.getModifiers()))
412: throw new ConfigException(
413: L
414: .l(
415: "Custom bean class `{0}' is not public. Bean classes must be public, concrete, and have a zero-argument constructor.",
416: beanClass.getName()));
417: else if (Modifier.isAbstract(beanClass.getModifiers()))
418: throw new ConfigException(
419: L
420: .l(
421: "Custom bean class `{0}' is abstract. Bean classes must be public, concrete, and have a zero-argument constructor.",
422: beanClass.getName()));
423:
424: Constructor[] constructors = beanClass
425: .getDeclaredConstructors();
426:
427: Constructor constructor = null;
428:
429: for (int i = 0; i < constructors.length; i++) {
430: if (constructors[i].getParameterTypes().length == 0) {
431: constructor = constructors[i];
432: break;
433: }
434: }
435:
436: if (constructor == null)
437: throw new ConfigException(
438: L
439: .l(
440: "Custom bean class `{0}' doesn't have a zero-arg constructor. Bean classes must be have a zero-argument constructor.",
441: beanClass.getName()));
442:
443: if (!Modifier.isPublic(constructor.getModifiers())) {
444: throw new ConfigException(
445: L
446: .l(
447: "The zero-argument constructor for `{0}' isn't public. Bean classes must have a public zero-argument constructor.",
448: beanClass.getName()));
449: }
450: }
451:
452: /**
453: * Returns true if the class can be instantiated.
454: */
455: public static void validate(Class cl, Class api)
456: throws ConfigException {
457: checkCanInstantiate(cl);
458:
459: if (!api.isAssignableFrom(cl)) {
460: throw new ConfigException(L.l("{0} must implement {1}.", cl
461: .getName(), api.getName()));
462: }
463: }
464:
465: /**
466: * Sets an attribute with a value.
467: *
468: * @param obj the bean to be set
469: * @param attr the attribute name
470: * @param value the attribute value
471: */
472: public static void setAttribute(Object obj, String attr,
473: Object value) throws Exception {
474: ConfigType type = TypeFactory.getType(obj.getClass());
475:
476: QName attrName = new QName(attr);
477: Attribute attrStrategy = type.getAttribute(attrName);
478: if (attrStrategy == null)
479: throw new ConfigException(L.l(
480: "{0}: '{1}' is an unknown attribute.", obj
481: .getClass().getName(), attrName.getName()));
482:
483: value = attrStrategy.getConfigType().valueOf(value);
484:
485: attrStrategy.setValue(obj, attrName, value);
486: }
487:
488: /**
489: * Sets an attribute with a value.
490: *
491: * @param obj the bean to be set
492: * @param attr the attribute name
493: * @param value the attribute value
494: */
495: public static void setStringAttribute(Object obj, String attr,
496: String value) throws Exception {
497: ConfigContext builder = new ConfigContext();
498: QAttr qAttr = new QAttr(attr);
499: qAttr.setValue(value);
500:
501: builder.configureAttribute(obj, qAttr);
502: }
503:
504: public static void init(Object bean) throws ConfigException {
505: try {
506: ConfigType type = TypeFactory.getType(bean.getClass());
507:
508: type.init(bean);
509: } catch (RuntimeException e) {
510: throw e;
511: } catch (Exception e) {
512: throw ConfigException.create(e);
513: }
514: }
515:
516: public static void inject(Object bean) throws ConfigException {
517: try {
518: ConfigType type = TypeFactory.getType(bean.getClass());
519:
520: type.inject(bean);
521: } catch (RuntimeException e) {
522: throw e;
523: } catch (Exception e) {
524: throw ConfigException.create(e);
525: }
526: }
527:
528: public static Object replaceObject(Object bean) throws Exception {
529: ConfigType type = TypeFactory.getType(bean.getClass());
530:
531: return type.replaceObject(bean);
532: }
533:
534: /**
535: * Returns the variable resolver.
536: */
537: public static ELContext getEnvironment() {
538: ConfigContext builder = ConfigContext.getCurrentBuilder();
539:
540: if (builder != null) {
541: return builder.getELContext();
542: } else
543: return EL.getEnvironment();
544: }
545:
546: /**
547: * Returns the variable resolver.
548: */
549: public static ConfigELContext getELContext() {
550: ConfigContext builder = ConfigContext.getCurrentBuilder();
551:
552: if (builder != null) {
553: return builder.getELContext();
554: } else
555: return null;
556: }
557:
558: /**
559: * Sets an EL configuration variable.
560: */
561: public static Object getCurrentVar(String var) {
562: return WebBeansContainer.create().findByName(var);
563: }
564:
565: /**
566: * Evaluates an EL string in the context.
567: */
568: public static String evalString(String str) throws ELException {
569: return EL.evalString(str, getEnvironment());
570: }
571:
572: /**
573: * Evaluates an EL string in the context.
574: */
575: public static String evalString(String str,
576: HashMap<String, Object> varMap) throws ELException {
577: return EL.evalString(str, getEnvironment(varMap));
578: }
579:
580: /**
581: * Evaluates an EL boolean in the context.
582: */
583: public static boolean evalBoolean(String str) throws ELException {
584: return EL.evalBoolean(str, getEnvironment());
585: }
586:
587: public static ELContext getEnvironment(
588: HashMap<String, Object> varMap) {
589: if (varMap != null)
590: return new EnvironmentContext(varMap);
591: else
592: return new EnvironmentContext();
593: }
594:
595: public static ConfigException error(Field field, String msg) {
596: return new ConfigException(location(field) + msg);
597: }
598:
599: public static ConfigException error(Method method, String msg) {
600: return new ConfigException(location(method) + msg);
601: }
602:
603: public static String location(Field field) {
604: String className = field.getDeclaringClass().getName();
605:
606: return className + "." + field.getName() + ": ";
607: }
608:
609: public static String location(Method method) {
610: String className = method.getDeclaringClass().getName();
611:
612: return className + "." + method.getName() + ": ";
613: }
614: }
|