001/* 002 * JKNIV, SQLegance keeping queries maintainable. 003 * 004 * Copyright (C) 2017, the original author or authors. 005 * 006 * This library is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 2.1 of the License. 010 * 011 * This library is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with this library; if not, write to the Free Software Foundation, Inc., 018 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 019 */ 020package net.sf.jkniv.sqlegance.builder; 021 022import java.util.Map.Entry; 023import java.util.Properties; 024 025import javax.naming.Context; 026import javax.naming.InitialContext; 027import javax.naming.NamingException; 028import javax.sql.DataSource; 029 030import org.slf4j.Logger; 031import org.slf4j.LoggerFactory; 032import org.w3c.dom.Element; 033import org.w3c.dom.Node; 034import org.w3c.dom.NodeList; 035 036import net.sf.jkniv.asserts.Assertable; 037import net.sf.jkniv.asserts.AssertsFactory; 038import net.sf.jkniv.reflect.beans.ObjectProxy; 039import net.sf.jkniv.reflect.beans.ObjectProxyFactory; 040import net.sf.jkniv.sqlegance.RepositoryProperty; 041import net.sf.jkniv.sqlegance.RepositoryType; 042import net.sf.jkniv.sqlegance.Statistical; 043import net.sf.jkniv.sqlegance.builder.xml.NoSqlStats; 044import net.sf.jkniv.sqlegance.builder.xml.SqlStats; 045import net.sf.jkniv.sqlegance.dialect.SqlDialect; 046import net.sf.jkniv.sqlegance.dialect.SqlFeatureFactory; 047import net.sf.jkniv.sqlegance.dialect.SqlFeatureSupport; 048import net.sf.jkniv.sqlegance.logger.DataMasking; 049import net.sf.jkniv.sqlegance.logger.SimpleDataMasking; 050import net.sf.jkniv.sqlegance.transaction.TransactionType; 051 052/** 053 * 054 * @author Alisson Gomes 055 * @since 0.6.0 056 */ 057public class RepositoryConfig 058{ 059 private static final Logger LOG = LoggerFactory 060 .getLogger(RepositoryConfig.class); 061 private static String defaultFileConfig = "/repository-config.xml"; 062 private static final String ATTR_NAME = "name"; 063 private static final String ATTR_VALUE = "value"; 064 private static final String ATTR_TX_TYPE = "transaction-type"; 065 private static final String XPATH_REPO_NODE = "/repository"; 066 static final String SCHEMA_XSD = "/net/sf/jkniv/sqlegance/builder/xml/sqlegance-config.xsd"; 067 static final String XPATH_ROOT_NODE = "repository-config"; 068 private static final String XPATH_PROPERTY_NODE = XPATH_ROOT_NODE + XPATH_REPO_NODE 069 + "[@name='%s']/properties/property"; 070 private static final String XPATH_DESCRIPTION_NODE = XPATH_ROOT_NODE + XPATH_REPO_NODE 071 + "[@name='%s']/description[1]"; 072 private static final String XPATH_JNDI_DS_NODE = XPATH_ROOT_NODE + XPATH_REPO_NODE 073 + "[@name='%s']/jndi-data-source[1]"; 074 075 private static final Assertable NOT_NULL = AssertsFactory.getNotNull(); 076 //private static final Map<String, SqlLogger> loggers = new HashMap<String, SqlLogger>(); 077 private String name; 078 private String description; 079 private String jndiDataSource; 080 private TransactionType transactionType; 081 private Properties properties; 082 //private SqlLogger sqlLogger; 083 private DataMasking masking; 084 private RepositoryType repositoryType; 085 private SqlDialect sqlDialect; 086 087 public RepositoryConfig() 088 { 089 this(null, true); 090 } 091 092 /** 093 * Build new configuration object that access to DataSource and your properties 094 * Default instance is created if parameter is a <code>null</code> name 095 * @param name from repository configuration 096 * @throws IllegalArgumentException if the name is <code>null</code> 097 */ 098 public RepositoryConfig(String name) 099 { 100 this(name, false); 101 } 102 103 private RepositoryConfig(String name, boolean defaultRepo) 104 { 105 if (!defaultRepo) 106 NOT_NULL.verify(name); 107 108 this.name = name; 109 this.properties = new Properties(); 110 this.load(); 111 setDataMasking(); 112 //setSqlLogger(); 113 //this.sqlDialectName = getProperty(RepositoryProperty.SQL_DIALECT); 114 if (this.transactionType == null) 115 this.transactionType = TransactionType.LOCAL; 116 if (isEmpty(this.jndiDataSource)) 117 this.jndiDataSource = name; 118 119 } 120 121 private void load() 122 { 123 XmlReader xmlReader = new XmlReader(defaultFileConfig); 124 if (xmlReader.load()) 125 { 126 NodeList nodes = xmlReader.evaluateXpath(XPATH_ROOT_NODE + XPATH_REPO_NODE); 127 if (nodes != null) 128 { 129 for (int i = 0; i < nodes.getLength(); i++) 130 { 131 Node node = nodes.item(i); 132 if (node.getNodeType() == Node.ELEMENT_NODE) 133 { 134 Element element = (Element) node; 135 String name = element.getAttribute(ATTR_NAME); 136 if (isEmpty(this.name) || this.name.equals(name))// first repository is default 137 { 138 if (LOG.isDebugEnabled()) 139 LOG.debug("Binding Sql Statements context [{}] with Repository config [{}]", this.name, 140 name); 141 this.name = name; 142 parseAttributes(element);// FIXME parser attributes 143 parseDescription(xmlReader); 144 parseJndiDataSource(xmlReader); 145 parseProperties(xmlReader); 146 break; 147 } 148 } 149 } 150 } 151 else if (!hasProperties()) 152 { 153 LOG.info("repository-config.xml does not found. Using [{}] as jndi datasource.", this.name); 154 this.jndiDataSource = this.name; 155 } 156 } 157 defineDialect(); 158 } 159 160 private void parseAttributes(Element element) 161 { 162 this.name = element.getAttribute(ATTR_NAME); 163 String txType = element.getAttribute(ATTR_TX_TYPE); 164 String type = element.getAttribute("type"); 165 this.transactionType = TransactionType.get(txType); 166 this.repositoryType = RepositoryType.get(type); 167 } 168 169 private void parseDescription(XmlReader xmlReader) 170 { 171 NodeList nodes = xmlReader.evaluateXpath(String.format(XPATH_DESCRIPTION_NODE, name)); 172 for (int i = 0; i < nodes.getLength(); i++) 173 { 174 Node node = nodes.item(i); 175 if (node.getNodeType() == Node.ELEMENT_NODE) 176 { 177 Element element = (Element) node; 178 this.description = xmlReader.getTextFromElement(element); 179 } 180 } 181 } 182 183 private void parseJndiDataSource(XmlReader xmlReader) 184 { 185 NodeList nodes = xmlReader.evaluateXpath(String.format(XPATH_JNDI_DS_NODE, name)); 186 if (nodes != null) 187 { 188 for (int i = 0; i < nodes.getLength(); i++) 189 { 190 Node node = nodes.item(i); 191 if (node.getNodeType() == Node.ELEMENT_NODE) 192 { 193 Element element = (Element) node; 194 this.jndiDataSource = xmlReader.getTextFromElement(element); 195 } 196 } 197 } 198 } 199 200 private void parseProperties(XmlReader xmlReader) 201 { 202 NodeList nodesProperties = xmlReader.evaluateXpath(String.format(XPATH_PROPERTY_NODE, name)); 203 if (nodesProperties != null) 204 { 205 for (int i = 0; i < nodesProperties.getLength(); i++) 206 { 207 Node nodes = nodesProperties.item(i); 208 if (nodes.getNodeType() == Node.ELEMENT_NODE) 209 { 210 Element element = (Element) nodes; 211 String properName = element.getAttribute(ATTR_NAME); 212 String properValue = element.getAttribute(ATTR_VALUE); 213 this.properties.put(properName, properValue); 214 } 215 } 216 } 217 } 218 219// private void setSqlLogger() 220// { 221// SqlLogger sqlLogger = loggers.get(name); 222// if (sqlLogger == null || sqlLogger.getLogLevel() == LogLevel.NONE) 223// { 224// LogLevel logLevel = LogLevel.get(getProperty(RepositoryProperty.DEBUG_SQL)); 225// sqlLogger = new SqlLogger(logLevel, this.masking); 226// loggers.put(this.name, sqlLogger); 227// } 228// } 229 230 private void setDataMasking() 231 { 232 if (masking == null) 233 { 234 masking = new SimpleDataMasking(); 235 String className = getProperty(RepositoryProperty.DATA_MASKING); 236 if (className != null) 237 { 238 ObjectProxy<DataMasking> proxy = ObjectProxyFactory.of(className); 239 masking = proxy.newInstance(); 240 } 241 } 242 } 243 244 public String getQueryNameStrategy() 245 { 246 return getProperty(RepositoryProperty.QUERY_NAME_STRATEGY); 247 } 248 249 public String getJdbcAdapterFactory() 250 { 251 String adpterClassName = getProperty(RepositoryProperty.JDBC_ADAPTER_FACTORY); 252 return adpterClassName; 253 } 254 255 public boolean isShotKeyEnable() 256 { 257 return Boolean.valueOf(getProperty(RepositoryProperty.SHORT_NAME_ENABLE)); 258 } 259 260 public boolean isReloadableXmlEnable() 261 { 262 return Boolean.valueOf(getProperty(RepositoryProperty.RELOADABLE_XML_ENABLE)); 263 } 264 265 public void add(String key, String value) 266 { 267 Properties props = new Properties(); 268 props.put(key, value); 269 add(props); 270 } 271 272 public void add(Properties props) 273 { 274 if (props != null) 275 { 276 for (Entry<Object, Object> entry : props.entrySet()) 277 { 278 Object old = this.properties.put(entry.getKey().toString(), entry.getValue()); 279 if (old != null) 280 LOG.info("The value of key [{}] with original value [{}] was replacement to [{}]", entry.getKey(), 281 old, entry.getValue()); 282 if(RepositoryProperty.SQL_DIALECT.key().equals(entry.getKey().toString())) 283 defineDialect(); 284 } 285 } 286 } 287 288 public boolean hasProperties() 289 { 290 return (!this.properties.isEmpty()); 291 } 292 293 public DataMasking getDataMasking() 294 { 295 return this.masking; 296 } 297 298 public String getDescription() 299 { 300 return description; 301 } 302 303 public String getName() 304 { 305 return name; 306 } 307 308 public TransactionType getTransactionType() 309 { 310 return transactionType; 311 } 312 313 public SqlDialect getSqlDialect() 314 { 315 return this.sqlDialect; 316 } 317 318 public void setSqlDialect(SqlDialect sqlDialect) 319 { 320 this.sqlDialect = sqlDialect; 321 } 322 323 public Statistical getStatistical() 324 { 325 String enable = getProperty(RepositoryProperty.SQL_STATS); 326 if ("true".equalsIgnoreCase(enable)) 327 return new SqlStats(); 328 329 return NoSqlStats.getInstance(); 330 } 331 332 public String getJndiDataSource() 333 { 334 return jndiDataSource; 335 } 336 337 public Properties getProperties() 338 { 339 return properties; 340 } 341 342 public String getProperty(RepositoryProperty proper) 343 { 344 return properties.getProperty(proper.key(), proper.defaultValue()); 345 } 346 347 public String getProperty(String proper) 348 { 349 return properties.getProperty(proper); 350 } 351 352 public DataSource lookup() 353 { 354 String jndi = getJndiDataSource(); 355 DataSource ds = null; 356 Context ctx = null; 357 try 358 { 359 //ctx = (Context) new InitialContext().lookup("java:comp/env"); 360 ctx = new InitialContext(); 361 if (LOG.isDebugEnabled()) 362 LOG.debug("Lookuping JNDI resource with: new InitialContext().lookup(\"{}\") ...", jndi); 363 ds = (DataSource) ctx.lookup(jndi); 364 } 365 catch (NamingException ne) 366 { 367 LOG.error("NamingException, cannot lookup jndi datasource [" + jndi + "]: " + ne.getMessage()); 368 } 369 return ds; 370 } 371 372 private boolean isEmpty(String value) 373 { 374 return (value == null || "".equals(value)); 375 } 376 377 public RepositoryType getRepositoryType() 378 { 379 return repositoryType; 380 } 381 382 private void defineDialect() 383 { 384 String sqlDialectName = getProperty(RepositoryProperty.SQL_DIALECT); 385 ObjectProxy<SqlDialect> proxy = ObjectProxyFactory.of(sqlDialectName); 386 sqlDialect = proxy.newInstance(); 387 for (SqlFeatureSupport feature : SqlFeatureSupport.values()) 388 { 389 String value = getProperty(RepositoryProperty.PREFIX+".feature."+feature.name().toLowerCase()); 390 if (value != null) 391 { 392 boolean supported = Boolean.valueOf(value); 393 sqlDialect.addFeature(SqlFeatureFactory.newInstance(feature, supported)); 394 } 395 } 396 String maxParameters = getProperty(RepositoryProperty.PREFIX+".jdbc.max_parameters"); 397 if (maxParameters != null) 398 sqlDialect.setMaxOfParameters(Integer.valueOf(maxParameters)); 399 } 400 401 @Override 402 public String toString() 403 { 404 return "RepositoryConfig [name=" + name + ", sqlDialectName=" + getSqlDialect() + ", transactionType=" 405 + transactionType + "]"; 406 } 407 408}