001: package com.ibatis.sqlmap.engine.builder.xml;
002:
003: import com.ibatis.common.resources.Resources;
004: import com.ibatis.common.xml.Nodelet;
005: import com.ibatis.common.xml.NodeletParser;
006: import com.ibatis.common.xml.NodeletUtils;
007:
008: import com.ibatis.common.beans.ClassInfo;
009: import com.ibatis.sqlmap.client.SqlMapClient;
010: import com.ibatis.sqlmap.client.SqlMapException;
011: import com.ibatis.sqlmap.client.extensions.TypeHandlerCallback;
012: import com.ibatis.sqlmap.engine.accessplan.AccessPlanFactory;
013: import com.ibatis.sqlmap.engine.cache.CacheModel;
014: import com.ibatis.sqlmap.engine.cache.fifo.FifoCacheController;
015: import com.ibatis.sqlmap.engine.cache.lru.LruCacheController;
016: import com.ibatis.sqlmap.engine.cache.memory.MemoryCacheController;
017: import com.ibatis.sqlmap.engine.datasource.DataSourceFactory;
018: import com.ibatis.sqlmap.engine.datasource.DbcpDataSourceFactory;
019: import com.ibatis.sqlmap.engine.datasource.JndiDataSourceFactory;
020: import com.ibatis.sqlmap.engine.datasource.SimpleDataSourceFactory;
021: import com.ibatis.sqlmap.engine.impl.SqlMapClientImpl;
022: import com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate;
023: import com.ibatis.sqlmap.engine.mapping.result.ResultObjectFactory;
024: import com.ibatis.sqlmap.engine.mapping.statement.MappedStatement;
025: import com.ibatis.sqlmap.engine.transaction.TransactionConfig;
026: import com.ibatis.sqlmap.engine.transaction.TransactionManager;
027: import com.ibatis.sqlmap.engine.transaction.external.ExternalTransactionConfig;
028: import com.ibatis.sqlmap.engine.transaction.jdbc.JdbcTransactionConfig;
029: import com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig;
030: import com.ibatis.sqlmap.engine.type.*;
031:
032: import org.w3c.dom.Node;
033:
034: import java.io.InputStream;
035: import java.io.Reader;
036: import java.util.Iterator;
037: import java.util.Properties;
038:
039: public class SqlMapConfigParser extends BaseParser {
040:
041: protected final NodeletParser parser = new NodeletParser();
042: private boolean usingStreams;
043:
044: public SqlMapConfigParser() {
045: this (null, null);
046: }
047:
048: public SqlMapConfigParser(XmlConverter sqlMapConfigConv,
049: XmlConverter sqlMapConv) {
050: super (new Variables());
051: parser.setValidation(true);
052: parser.setEntityResolver(new SqlMapClasspathEntityResolver());
053:
054: vars.sqlMapConfigConv = sqlMapConfigConv;
055: vars.sqlMapConv = sqlMapConv;
056:
057: vars.delegate = new SqlMapExecutorDelegate();
058: vars.typeHandlerFactory = vars.delegate.getTypeHandlerFactory();
059: vars.client = new SqlMapClientImpl(vars.delegate);
060:
061: registerDefaultTypeAliases();
062:
063: addSqlMapConfigNodelets();
064: addGlobalPropNodelets();
065: addSettingsNodelets();
066: addTypeAliasNodelets();
067: addTypeHandlerNodelets();
068: addTransactionManagerNodelets();
069: addSqlMapNodelets();
070: addResultObjectFactoryNodelets();
071:
072: }
073:
074: public SqlMapClient parse(Reader reader, Properties props) {
075: vars.properties = props;
076: return parse(reader);
077: }
078:
079: public SqlMapClient parse(Reader reader) {
080: try {
081: if (vars.sqlMapConfigConv != null) {
082: reader = vars.sqlMapConfigConv.convertXml(reader);
083: }
084:
085: usingStreams = false;
086:
087: parser.parse(reader);
088: return vars.client;
089: } catch (Exception e) {
090: throw new RuntimeException("Error occurred. Cause: " + e,
091: e);
092: }
093: }
094:
095: public SqlMapClient parse(InputStream inputStream, Properties props) {
096: vars.properties = props;
097: return parse(inputStream);
098: }
099:
100: public SqlMapClient parse(InputStream inputStream) {
101: try {
102: if (vars.sqlMapConfigConv != null) {
103: inputStream = vars.sqlMapConfigConv
104: .convertXml(inputStream);
105: }
106:
107: usingStreams = true;
108:
109: parser.parse(inputStream);
110: return vars.client;
111: } catch (Exception e) {
112: throw new RuntimeException("Error occurred. Cause: " + e,
113: e);
114: }
115: }
116:
117: private void addSqlMapConfigNodelets() {
118: parser.addNodelet("/sqlMapConfig/end()", new Nodelet() {
119: public void process(Node node) throws Exception {
120: Iterator cacheNames = vars.client.getDelegate()
121: .getCacheModelNames();
122:
123: while (cacheNames.hasNext()) {
124: String cacheName = (String) cacheNames.next();
125: CacheModel cacheModel = vars.client.getDelegate()
126: .getCacheModel(cacheName);
127: Iterator statementNames = cacheModel
128: .getFlushTriggerStatementNames();
129: while (statementNames.hasNext()) {
130: String statementName = (String) statementNames
131: .next();
132: MappedStatement statement = vars.client
133: .getDelegate().getMappedStatement(
134: statementName);
135: if (statement != null) {
136: statement.addExecuteListener(cacheModel);
137: } else {
138: throw new RuntimeException(
139: "Could not find statement named '"
140: + statementName
141: + "' for use as a flush trigger for the cache model named '"
142: + cacheName + "'.");
143: }
144: }
145: }
146: }
147: });
148: }
149:
150: private void addGlobalPropNodelets() {
151: parser.addNodelet("/sqlMapConfig/properties", new Nodelet() {
152: public void process(Node node) throws Exception {
153: vars.errorCtx.setActivity("loading global properties");
154:
155: Properties attributes = NodeletUtils.parseAttributes(
156: node, vars.properties);
157: String resource = attributes.getProperty("resource");
158: String url = attributes.getProperty("url");
159:
160: try {
161: Properties props = null;
162: if (resource != null) {
163: vars.errorCtx.setResource(resource);
164: props = Resources
165: .getResourceAsProperties(resource);
166: } else if (url != null) {
167: vars.errorCtx.setResource(url);
168: props = Resources.getUrlAsProperties(url);
169: } else {
170: throw new RuntimeException(
171: "The "
172: + "properties"
173: + " element requires either a resource or a url attribute.");
174: }
175:
176: if (vars.properties == null) {
177: vars.properties = props;
178: } else {
179: props.putAll(vars.properties);
180: vars.properties = props;
181: }
182: } catch (Exception e) {
183: throw new RuntimeException(
184: "Error loading properties. Cause: " + e);
185: }
186: }
187: });
188: }
189:
190: private void addSettingsNodelets() {
191: parser.addNodelet("/sqlMapConfig/settings", new Nodelet() {
192: public void process(Node node) throws Exception {
193: vars.errorCtx
194: .setActivity("loading settings properties");
195:
196: Properties attributes = NodeletUtils.parseAttributes(
197: node, vars.properties);
198:
199: String classInfoCacheEnabledAttr = attributes
200: .getProperty("classInfoCacheEnabled");
201: boolean classInfoCacheEnabled = (classInfoCacheEnabledAttr == null || "true"
202: .equals(classInfoCacheEnabledAttr));
203: ClassInfo.setCacheEnabled(classInfoCacheEnabled);
204:
205: String lazyLoadingEnabledAttr = attributes
206: .getProperty("lazyLoadingEnabled");
207: boolean lazyLoadingEnabled = (lazyLoadingEnabledAttr == null || "true"
208: .equals(lazyLoadingEnabledAttr));
209: vars.client.getDelegate().setLazyLoadingEnabled(
210: lazyLoadingEnabled);
211:
212: String statementCachingEnabledAttr = attributes
213: .getProperty("statementCachingEnabled");
214: boolean statementCachingEnabled = (statementCachingEnabledAttr == null || "true"
215: .equals(statementCachingEnabledAttr));
216: vars.client.getDelegate().setStatementCacheEnabled(
217: statementCachingEnabled);
218:
219: String cacheModelsEnabledAttr = attributes
220: .getProperty("cacheModelsEnabled");
221: boolean cacheModelsEnabled = (cacheModelsEnabledAttr == null || "true"
222: .equals(cacheModelsEnabledAttr));
223: vars.client.getDelegate().setCacheModelsEnabled(
224: cacheModelsEnabled);
225:
226: String enhancementEnabledAttr = attributes
227: .getProperty("enhancementEnabled");
228: boolean enhancementEnabled = (enhancementEnabledAttr == null || "true"
229: .equals(enhancementEnabledAttr));
230: try {
231: enhancementEnabled = enhancementEnabled
232: && Resources
233: .classForName("net.sf.cglib.proxy.InvocationHandler") != null;
234: } catch (ClassNotFoundException e) {
235: enhancementEnabled = false;
236: }
237: vars.client.getDelegate().setEnhancementEnabled(
238: enhancementEnabled);
239:
240: String useStatementNamespacesAttr = attributes
241: .getProperty("useStatementNamespaces");
242: vars.useStatementNamespaces = ("true"
243: .equals(useStatementNamespacesAttr));
244:
245: String maxTransactions = attributes
246: .getProperty("maxTransactions");
247: if (maxTransactions != null
248: && Integer.parseInt(maxTransactions) > 0) {
249: vars.client.getDelegate().setMaxTransactions(
250: Integer.parseInt(maxTransactions));
251: }
252:
253: String maxRequests = attributes
254: .getProperty("maxRequests");
255: if (maxRequests != null
256: && Integer.parseInt(maxRequests) > 0) {
257: vars.client.getDelegate().setMaxRequests(
258: Integer.parseInt(maxRequests));
259: }
260:
261: String maxSessions = attributes
262: .getProperty("maxSessions");
263: if (maxSessions != null
264: && Integer.parseInt(maxSessions) > 0) {
265: vars.client.getDelegate().setMaxSessions(
266: Integer.parseInt(maxSessions));
267: }
268:
269: AccessPlanFactory
270: .setBytecodeEnhancementEnabled(vars.client
271: .getDelegate().isEnhancementEnabled());
272:
273: String defaultStatementTimeout = attributes
274: .getProperty("defaultStatementTimeout");
275: if (defaultStatementTimeout != null) {
276: try {
277: Integer defaultTimeout = Integer
278: .valueOf(defaultStatementTimeout);
279: vars.defaultStatementTimeout = defaultTimeout;
280: } catch (NumberFormatException e) {
281: throw new SqlMapException(
282: "Specified defaultStatementTimeout is not a valid integer");
283: }
284: }
285: }
286: });
287: }
288:
289: private void addTypeAliasNodelets() {
290: parser.addNodelet("/sqlMapConfig/typeAlias", new Nodelet() {
291: public void process(Node node) throws Exception {
292: Properties prop = NodeletUtils.parseAttributes(node,
293: vars.properties);
294: String alias = prop.getProperty("alias");
295: String type = prop.getProperty("type");
296: vars.typeHandlerFactory.putTypeAlias(alias, type);
297: }
298: });
299: }
300:
301: private void addTypeHandlerNodelets() {
302: parser.addNodelet("/sqlMapConfig/typeHandler", new Nodelet() {
303: public void process(Node node) throws Exception {
304: vars.errorCtx
305: .setActivity("building a building custom type handler");
306: try {
307: TypeHandlerFactory typeHandlerFactory = vars.client
308: .getDelegate().getTypeHandlerFactory();
309:
310: Properties prop = NodeletUtils.parseAttributes(
311: node, vars.properties);
312:
313: String jdbcType = prop.getProperty("jdbcType");
314: String javaType = prop.getProperty("javaType");
315: String callback = prop.getProperty("callback");
316: callback = typeHandlerFactory
317: .resolveAlias(callback);
318: javaType = typeHandlerFactory
319: .resolveAlias(javaType);
320:
321: vars.errorCtx
322: .setMoreInfo("Check the callback attribute '"
323: + callback
324: + "' (must be a classname).");
325:
326: TypeHandler typeHandler;
327: Object impl = Resources.classForName(callback)
328: .newInstance();
329: if (impl instanceof TypeHandlerCallback) {
330: typeHandler = new CustomTypeHandler(
331: (TypeHandlerCallback) impl);
332: } else if (impl instanceof TypeHandler) {
333: typeHandler = (TypeHandler) impl;
334: } else {
335: throw new RuntimeException(
336: "The class '' is not a valid implementation of TypeHandler or TypeHandlerCallback");
337: }
338:
339: vars.errorCtx
340: .setMoreInfo("Check the javaType attribute '"
341: + javaType
342: + "' (must be a classname) or the jdbcType '"
343: + jdbcType
344: + "' (must be a JDBC type name).");
345: if (jdbcType != null && jdbcType.length() > 0) {
346: typeHandlerFactory.register(Resources
347: .classForName(javaType), jdbcType,
348: typeHandler);
349: } else {
350: typeHandlerFactory.register(Resources
351: .classForName(javaType), typeHandler);
352: }
353: } catch (Exception e) {
354: throw new SqlMapException(
355: "Error registering occurred. Cause: " + e,
356: e);
357: }
358: vars.errorCtx.setMoreInfo(null);
359: vars.errorCtx.setObjectId(null);
360: }
361: });
362: }
363:
364: private void addTransactionManagerNodelets() {
365: parser.addNodelet("/sqlMapConfig/transactionManager/end()",
366: new Nodelet() {
367: public void process(Node node) throws Exception {
368: vars.errorCtx
369: .setActivity("configuring the transaction manager");
370:
371: Properties attributes = NodeletUtils
372: .parseAttributes(node, vars.properties);
373:
374: String type = attributes.getProperty("type");
375: type = vars.typeHandlerFactory
376: .resolveAlias(type);
377:
378: TransactionManager txManager = null;
379: try {
380: vars.errorCtx
381: .setMoreInfo("Check the transaction manager type or class.");
382: TransactionConfig config = (TransactionConfig) Resources
383: .instantiate(type);
384: config.setDataSource(vars.dataSource);
385: config
386: .setMaximumConcurrentTransactions(vars.client
387: .getDelegate()
388: .getMaxTransactions());
389: vars.errorCtx
390: .setMoreInfo("Check the transactio nmanager properties or configuration.");
391: config.initialize(vars.txProps);
392: vars.errorCtx.setMoreInfo(null);
393: txManager = new TransactionManager(config);
394: txManager
395: .setForceCommit("true"
396: .equals(attributes
397: .getProperty("commitRequired")));
398: } catch (Exception e) {
399: if (e instanceof SqlMapException) {
400: throw (SqlMapException) e;
401: } else {
402: throw new SqlMapException(
403: "Error initializing TransactionManager. Could not instantiate TransactionConfig. Cause: "
404: + e, e);
405: }
406: }
407:
408: vars.client.getDelegate().setTxManager(
409: txManager);
410: }
411: });
412: parser.addNodelet("/sqlMapConfig/transactionManager/property",
413: new Nodelet() {
414: public void process(Node node) throws Exception {
415: Properties attributes = NodeletUtils
416: .parseAttributes(node, vars.properties);
417: String name = attributes.getProperty("name");
418: String value = NodeletUtils
419: .parsePropertyTokens(attributes
420: .getProperty("value"),
421: vars.properties);
422: vars.txProps.setProperty(name, value);
423: }
424: });
425: parser.addNodelet(
426: "/sqlMapConfig/transactionManager/dataSource",
427: new Nodelet() {
428: public void process(Node node) throws Exception {
429: vars.dsProps = new Properties();
430: }
431: });
432: parser.addNodelet(
433: "/sqlMapConfig/transactionManager/dataSource/end()",
434: new Nodelet() {
435: public void process(Node node) throws Exception {
436: vars.errorCtx
437: .setActivity("configuring the data source");
438:
439: Properties attributes = NodeletUtils
440: .parseAttributes(node, vars.properties);
441:
442: String type = attributes.getProperty("type");
443: type = vars.typeHandlerFactory
444: .resolveAlias(type);
445:
446: try {
447: vars.errorCtx
448: .setMoreInfo("Check the data source type or class.");
449: DataSourceFactory dsFactory = (DataSourceFactory) Resources
450: .instantiate(type);
451: vars.errorCtx
452: .setMoreInfo("Check the data source properties or configuration.");
453: dsFactory.initialize(vars.dsProps);
454: vars.dataSource = dsFactory.getDataSource();
455: vars.errorCtx.setMoreInfo(null);
456: } catch (Exception e) {
457: if (e instanceof SqlMapException) {
458: throw (SqlMapException) e;
459: } else {
460: throw new SqlMapException(
461: "Error initializing DataSource. Could not instantiate DataSourceFactory. Cause: "
462: + e, e);
463: }
464: }
465: }
466: });
467: parser.addNodelet(
468: "/sqlMapConfig/transactionManager/dataSource/property",
469: new Nodelet() {
470: public void process(Node node) throws Exception {
471: Properties attributes = NodeletUtils
472: .parseAttributes(node, vars.properties);
473: String name = attributes.getProperty("name");
474: String value = NodeletUtils
475: .parsePropertyTokens(attributes
476: .getProperty("value"),
477: vars.properties);
478: vars.dsProps.setProperty(name, value);
479: }
480: });
481: }
482:
483: protected void addSqlMapNodelets() {
484: parser.addNodelet("/sqlMapConfig/sqlMap", new Nodelet() {
485: public void process(Node node) throws Exception {
486: vars.errorCtx
487: .setActivity("loading the SQL Map resource");
488:
489: Properties attributes = NodeletUtils.parseAttributes(
490: node, vars.properties);
491:
492: String resource = attributes.getProperty("resource");
493: String url = attributes.getProperty("url");
494:
495: if (usingStreams) {
496: InputStream inputStream = null;
497: if (resource != null) {
498: vars.errorCtx.setResource(resource);
499: inputStream = Resources
500: .getResourceAsStream(resource);
501: } else if (url != null) {
502: vars.errorCtx.setResource(url);
503: inputStream = Resources.getUrlAsStream(url);
504: } else {
505: throw new SqlMapException(
506: "The <sqlMap> element requires either a resource or a url attribute.");
507: }
508:
509: if (vars.sqlMapConv != null) {
510: inputStream = vars.sqlMapConv
511: .convertXml(inputStream);
512: }
513: new SqlMapParser(vars).parse(inputStream);
514: } else {
515: Reader reader = null;
516: if (resource != null) {
517: vars.errorCtx.setResource(resource);
518: reader = Resources
519: .getResourceAsReader(resource);
520: } else if (url != null) {
521: vars.errorCtx.setResource(url);
522: reader = Resources.getUrlAsReader(url);
523: } else {
524: throw new SqlMapException(
525: "The <sqlMap> element requires either a resource or a url attribute.");
526: }
527:
528: if (vars.sqlMapConv != null) {
529: reader = vars.sqlMapConv.convertXml(reader);
530: }
531: new SqlMapParser(vars).parse(reader);
532: }
533: }
534: });
535: }
536:
537: private void addResultObjectFactoryNodelets() {
538: parser.addNodelet("/sqlMapConfig/resultObjectFactory",
539: new Nodelet() {
540: public void process(Node node) throws Exception {
541: vars.errorCtx
542: .setActivity("configuring the Result Object Factory");
543:
544: Properties attributes = NodeletUtils
545: .parseAttributes(node, vars.properties);
546:
547: String type = attributes.getProperty("type");
548:
549: ResultObjectFactory rof;
550: try {
551: rof = (ResultObjectFactory) Resources
552: .instantiate(type);
553: vars.delegate.setResultObjectFactory(rof);
554: } catch (Exception e) {
555: throw new SqlMapException(
556: "Error instantiating resultObjectFactory: "
557: + type, e);
558: }
559: }
560: });
561: parser.addNodelet("/sqlMapConfig/resultObjectFactory/property",
562: new Nodelet() {
563: public void process(Node node) throws Exception {
564: Properties attributes = NodeletUtils
565: .parseAttributes(node, vars.properties);
566: String name = attributes.getProperty("name");
567: String value = NodeletUtils
568: .parsePropertyTokens(attributes
569: .getProperty("value"),
570: vars.properties);
571: vars.delegate.getResultObjectFactory()
572: .setProperty(name, value);
573: }
574: });
575: }
576:
577: private void registerDefaultTypeAliases() {
578: // TRANSACTION ALIASES
579: vars.typeHandlerFactory.putTypeAlias("JDBC",
580: JdbcTransactionConfig.class.getName());
581: vars.typeHandlerFactory.putTypeAlias("JTA",
582: JtaTransactionConfig.class.getName());
583: vars.typeHandlerFactory.putTypeAlias("EXTERNAL",
584: ExternalTransactionConfig.class.getName());
585:
586: // DATA SOURCE ALIASES
587: vars.typeHandlerFactory.putTypeAlias("SIMPLE",
588: SimpleDataSourceFactory.class.getName());
589: vars.typeHandlerFactory.putTypeAlias("DBCP",
590: DbcpDataSourceFactory.class.getName());
591: vars.typeHandlerFactory.putTypeAlias("JNDI",
592: JndiDataSourceFactory.class.getName());
593:
594: // CACHE ALIASES
595: vars.typeHandlerFactory.putTypeAlias("FIFO",
596: FifoCacheController.class.getName());
597: vars.typeHandlerFactory.putTypeAlias("LRU",
598: LruCacheController.class.getName());
599: vars.typeHandlerFactory.putTypeAlias("MEMORY",
600: MemoryCacheController.class.getName());
601: // use a string for OSCache to avoid unnecessary loading of properties upon init
602: vars.typeHandlerFactory
603: .putTypeAlias("OSCACHE",
604: "com.ibatis.sqlmap.engine.cache.oscache.OSCacheController");
605:
606: // TYPE ALIASEs
607: vars.typeHandlerFactory.putTypeAlias("dom", DomTypeMarker.class
608: .getName());
609: vars.typeHandlerFactory.putTypeAlias("domCollection",
610: DomCollectionTypeMarker.class.getName());
611: vars.typeHandlerFactory.putTypeAlias("xml", XmlTypeMarker.class
612: .getName());
613: vars.typeHandlerFactory.putTypeAlias("xmlCollection",
614: XmlCollectionTypeMarker.class.getName());
615: }
616:
617: }
|