1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.ibatis.builder.xml;
17
18 import java.io.InputStream;
19 import java.io.Reader;
20 import java.util.Properties;
21 import javax.sql.DataSource;
22
23 import org.apache.ibatis.builder.BaseBuilder;
24 import org.apache.ibatis.builder.BuilderException;
25 import org.apache.ibatis.datasource.DataSourceFactory;
26 import org.apache.ibatis.executor.ErrorContext;
27 import org.apache.ibatis.executor.loader.ProxyFactory;
28 import org.apache.ibatis.io.Resources;
29 import org.apache.ibatis.io.VFS;
30 import org.apache.ibatis.logging.Log;
31 import org.apache.ibatis.mapping.DatabaseIdProvider;
32 import org.apache.ibatis.mapping.Environment;
33 import org.apache.ibatis.parsing.XNode;
34 import org.apache.ibatis.parsing.XPathParser;
35 import org.apache.ibatis.plugin.Interceptor;
36 import org.apache.ibatis.reflection.DefaultReflectorFactory;
37 import org.apache.ibatis.reflection.MetaClass;
38 import org.apache.ibatis.reflection.ReflectorFactory;
39 import org.apache.ibatis.reflection.factory.ObjectFactory;
40 import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
41 import org.apache.ibatis.session.AutoMappingBehavior;
42 import org.apache.ibatis.session.AutoMappingUnknownColumnBehavior;
43 import org.apache.ibatis.session.Configuration;
44 import org.apache.ibatis.session.ExecutorType;
45 import org.apache.ibatis.session.LocalCacheScope;
46 import org.apache.ibatis.transaction.TransactionFactory;
47 import org.apache.ibatis.type.JdbcType;
48
49
50
51
52
53 public class XMLConfigBuilder extends BaseBuilder {
54
55 private boolean parsed;
56 private final XPathParser parser;
57 private String environment;
58 private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
59
60 public XMLConfigBuilder(Reader reader) {
61 this(reader, null, null);
62 }
63
64 public XMLConfigBuilder(Reader reader, String environment) {
65 this(reader, environment, null);
66 }
67
68 public XMLConfigBuilder(Reader reader, String environment, Properties props) {
69 this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
70 }
71
72 public XMLConfigBuilder(InputStream inputStream) {
73 this(inputStream, null, null);
74 }
75
76 public XMLConfigBuilder(InputStream inputStream, String environment) {
77 this(inputStream, environment, null);
78 }
79
80 public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
81 this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
82 }
83
84 private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
85 super(new Configuration());
86 ErrorContext.instance().resource("SQL Mapper Configuration");
87 this.configuration.setVariables(props);
88 this.parsed = false;
89 this.environment = environment;
90 this.parser = parser;
91 }
92
93 public Configuration parse() {
94 if (parsed) {
95 throw new BuilderException("Each XMLConfigBuilder can only be used once.");
96 }
97 parsed = true;
98 parseConfiguration(parser.evalNode("/configuration"));
99 return configuration;
100 }
101
102 private void parseConfiguration(XNode root) {
103 try {
104
105 propertiesElement(root.evalNode("properties"));
106 Properties settings = settingsAsProperties(root.evalNode("settings"));
107 loadCustomVfs(settings);
108 loadCustomLogImpl(settings);
109 typeAliasesElement(root.evalNode("typeAliases"));
110 pluginElement(root.evalNode("plugins"));
111 objectFactoryElement(root.evalNode("objectFactory"));
112 objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
113 reflectorFactoryElement(root.evalNode("reflectorFactory"));
114 settingsElement(settings);
115
116 environmentsElement(root.evalNode("environments"));
117 databaseIdProviderElement(root.evalNode("databaseIdProvider"));
118 typeHandlerElement(root.evalNode("typeHandlers"));
119 mapperElement(root.evalNode("mappers"));
120 } catch (Exception e) {
121 throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
122 }
123 }
124
125 private Properties settingsAsProperties(XNode context) {
126 if (context == null) {
127 return new Properties();
128 }
129 Properties props = context.getChildrenAsProperties();
130
131 MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
132 for (Object key : props.keySet()) {
133 if (!metaConfig.hasSetter(String.valueOf(key))) {
134 throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive).");
135 }
136 }
137 return props;
138 }
139
140 private void loadCustomVfs(Properties props) throws ClassNotFoundException {
141 String value = props.getProperty("vfsImpl");
142 if (value != null) {
143 String[] clazzes = value.split(",");
144 for (String clazz : clazzes) {
145 if (!clazz.isEmpty()) {
146 @SuppressWarnings("unchecked")
147 Class<? extends VFS> vfsImpl = (Class<? extends VFS>)Resources.classForName(clazz);
148 configuration.setVfsImpl(vfsImpl);
149 }
150 }
151 }
152 }
153
154 private void loadCustomLogImpl(Properties props) {
155 Class<? extends Log> logImpl = resolveClass(props.getProperty("logImpl"));
156 configuration.setLogImpl(logImpl);
157 }
158
159 private void typeAliasesElement(XNode parent) {
160 if (parent != null) {
161 for (XNode child : parent.getChildren()) {
162 if ("package".equals(child.getName())) {
163 String typeAliasPackage = child.getStringAttribute("name");
164 configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
165 } else {
166 String alias = child.getStringAttribute("alias");
167 String type = child.getStringAttribute("type");
168 try {
169 Class<?> clazz = Resources.classForName(type);
170 if (alias == null) {
171 typeAliasRegistry.registerAlias(clazz);
172 } else {
173 typeAliasRegistry.registerAlias(alias, clazz);
174 }
175 } catch (ClassNotFoundException e) {
176 throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
177 }
178 }
179 }
180 }
181 }
182
183 private void pluginElement(XNode parent) throws Exception {
184 if (parent != null) {
185 for (XNode child : parent.getChildren()) {
186 String interceptor = child.getStringAttribute("interceptor");
187 Properties properties = child.getChildrenAsProperties();
188 Interceptor/apache/ibatis/plugin/Interceptor.html#Interceptor">Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();
189 interceptorInstance.setProperties(properties);
190 configuration.addInterceptor(interceptorInstance);
191 }
192 }
193 }
194
195 private void objectFactoryElement(XNode context) throws Exception {
196 if (context != null) {
197 String type = context.getStringAttribute("type");
198 Properties properties = context.getChildrenAsProperties();
199 ObjectFactory/../../org/apache/ibatis/reflection/factory/ObjectFactory.html#ObjectFactory">ObjectFactory factory = (ObjectFactory) resolveClass(type).getDeclaredConstructor().newInstance();
200 factory.setProperties(properties);
201 configuration.setObjectFactory(factory);
202 }
203 }
204
205 private void objectWrapperFactoryElement(XNode context) throws Exception {
206 if (context != null) {
207 String type = context.getStringAttribute("type");
208 ObjectWrapperFactoryorg/apache/ibatis/reflection/wrapper/ObjectWrapperFactory.html#ObjectWrapperFactory">ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).getDeclaredConstructor().newInstance();
209 configuration.setObjectWrapperFactory(factory);
210 }
211 }
212
213 private void reflectorFactoryElement(XNode context) throws Exception {
214 if (context != null) {
215 String type = context.getStringAttribute("type");
216 ReflectorFactory/../org/apache/ibatis/reflection/ReflectorFactory.html#ReflectorFactory">ReflectorFactory factory = (ReflectorFactory) resolveClass(type).getDeclaredConstructor().newInstance();
217 configuration.setReflectorFactory(factory);
218 }
219 }
220
221 private void propertiesElement(XNode context) throws Exception {
222 if (context != null) {
223 Properties defaults = context.getChildrenAsProperties();
224 String resource = context.getStringAttribute("resource");
225 String url = context.getStringAttribute("url");
226 if (resource != null && url != null) {
227 throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
228 }
229 if (resource != null) {
230 defaults.putAll(Resources.getResourceAsProperties(resource));
231 } else if (url != null) {
232 defaults.putAll(Resources.getUrlAsProperties(url));
233 }
234 Properties vars = configuration.getVariables();
235 if (vars != null) {
236 defaults.putAll(vars);
237 }
238 parser.setVariables(defaults);
239 configuration.setVariables(defaults);
240 }
241 }
242
243 private void settingsElement(Properties props) {
244 configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
245 configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
246 configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
247 configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
248 configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
249 configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
250 configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
251 configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
252 configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
253 configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
254 configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
255 configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
256 configuration.setDefaultResultSetType(resolveResultSetType(props.getProperty("defaultResultSetType")));
257 configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
258 configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
259 configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
260 configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
261 configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
262 configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
263 configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
264 configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler")));
265 configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
266 configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));
267 configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
268 configuration.setLogPrefix(props.getProperty("logPrefix"));
269 configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
270 }
271
272 private void environmentsElement(XNode context) throws Exception {
273 if (context != null) {
274 if (environment == null) {
275 environment = context.getStringAttribute("default");
276 }
277 for (XNode child : context.getChildren()) {
278 String id = child.getStringAttribute("id");
279 if (isSpecifiedEnvironment(id)) {
280 TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
281 DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
282 DataSource dataSource = dsFactory.getDataSource();
283 Environment.Builder environmentBuilder = new Environment.Builder(id)
284 .transactionFactory(txFactory)
285 .dataSource(dataSource);
286 configuration.setEnvironment(environmentBuilder.build());
287 }
288 }
289 }
290 }
291
292 private void databaseIdProviderElement(XNode context) throws Exception {
293 DatabaseIdProvider databaseIdProvider = null;
294 if (context != null) {
295 String type = context.getStringAttribute("type");
296
297 if ("VENDOR".equals(type)) {
298 type = "DB_VENDOR";
299 }
300 Properties properties = context.getChildrenAsProperties();
301 databaseIdProvider = (DatabaseIdProvider) resolveClass(type).getDeclaredConstructor().newInstance();
302 databaseIdProvider.setProperties(properties);
303 }
304 Environment environment = configuration.getEnvironment();
305 if (environment != null && databaseIdProvider != null) {
306 String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
307 configuration.setDatabaseId(databaseId);
308 }
309 }
310
311 private TransactionFactory transactionManagerElement(XNode context) throws Exception {
312 if (context != null) {
313 String type = context.getStringAttribute("type");
314 Properties props = context.getChildrenAsProperties();
315 TransactionFactory./org/apache/ibatis/transaction/TransactionFactory.html#TransactionFactory">TransactionFactory factory = (TransactionFactory) resolveClass(type).getDeclaredConstructor().newInstance();
316 factory.setProperties(props);
317 return factory;
318 }
319 throw new BuilderException("Environment declaration requires a TransactionFactory.");
320 }
321
322 private DataSourceFactory dataSourceElement(XNode context) throws Exception {
323 if (context != null) {
324 String type = context.getStringAttribute("type");
325 Properties props = context.getChildrenAsProperties();
326 DataSourceFactory../org/apache/ibatis/datasource/DataSourceFactory.html#DataSourceFactory">DataSourceFactory factory = (DataSourceFactory) resolveClass(type).getDeclaredConstructor().newInstance();
327 factory.setProperties(props);
328 return factory;
329 }
330 throw new BuilderException("Environment declaration requires a DataSourceFactory.");
331 }
332
333 private void typeHandlerElement(XNode parent) {
334 if (parent != null) {
335 for (XNode child : parent.getChildren()) {
336 if ("package".equals(child.getName())) {
337 String typeHandlerPackage = child.getStringAttribute("name");
338 typeHandlerRegistry.register(typeHandlerPackage);
339 } else {
340 String javaTypeName = child.getStringAttribute("javaType");
341 String jdbcTypeName = child.getStringAttribute("jdbcType");
342 String handlerTypeName = child.getStringAttribute("handler");
343 Class<?> javaTypeClass = resolveClass(javaTypeName);
344 JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
345 Class<?> typeHandlerClass = resolveClass(handlerTypeName);
346 if (javaTypeClass != null) {
347 if (jdbcType == null) {
348 typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
349 } else {
350 typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
351 }
352 } else {
353 typeHandlerRegistry.register(typeHandlerClass);
354 }
355 }
356 }
357 }
358 }
359
360 private void mapperElement(XNode parent) throws Exception {
361 if (parent != null) {
362 for (XNode child : parent.getChildren()) {
363 if ("package".equals(child.getName())) {
364 String mapperPackage = child.getStringAttribute("name");
365 configuration.addMappers(mapperPackage);
366 } else {
367 String resource = child.getStringAttribute("resource");
368 String url = child.getStringAttribute("url");
369 String mapperClass = child.getStringAttribute("class");
370 if (resource != null && url == null && mapperClass == null) {
371 ErrorContext.instance().resource(resource);
372 InputStream inputStream = Resources.getResourceAsStream(resource);
373 XMLMapperBuilderilder.html#XMLMapperBuilder">XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
374 mapperParser.parse();
375 } else if (resource == null && url != null && mapperClass == null) {
376 ErrorContext.instance().resource(url);
377 InputStream inputStream = Resources.getUrlAsStream(url);
378 XMLMapperBuilderilder.html#XMLMapperBuilder">XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
379 mapperParser.parse();
380 } else if (resource == null && url == null && mapperClass != null) {
381 Class<?> mapperInterface = Resources.classForName(mapperClass);
382 configuration.addMapper(mapperInterface);
383 } else {
384 throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
385 }
386 }
387 }
388 }
389 }
390
391 private boolean isSpecifiedEnvironment(String id) {
392 if (environment == null) {
393 throw new BuilderException("No environment specified.");
394 } else if (id == null) {
395 throw new BuilderException("Environment requires an id attribute.");
396 } else if (environment.equals(id)) {
397 return true;
398 }
399 return false;
400 }
401
402 }