001/* 002 * (C) Copyright 2006-2007 Nuxeo SAS (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Nuxeo - initial API and implementation 016 * 017 * $Id$ 018 */ 019 020package org.nuxeo.ecm.directory.sql; 021 022import java.util.ArrayList; 023import java.util.List; 024 025import org.apache.commons.logging.Log; 026import org.apache.commons.logging.LogFactory; 027import org.nuxeo.common.xmap.annotation.XNode; 028import org.nuxeo.common.xmap.annotation.XNodeList; 029import org.nuxeo.common.xmap.annotation.XObject; 030import org.nuxeo.ecm.directory.DirectoryException; 031import org.nuxeo.ecm.directory.InverseReference; 032import org.nuxeo.ecm.directory.PermissionDescriptor; 033import org.nuxeo.ecm.directory.Reference; 034 035@XObject(value = "directory") 036public class SQLDirectoryDescriptor { 037 038 private static final Log log = LogFactory.getLog(SQLDirectoryDescriptor.class); 039 040 public enum SubstringMatchType { 041 subinitial, subfinal, subany 042 } 043 044 public static final int QUERY_SIZE_LIMIT_DEFAULT = 0; 045 046 public static final boolean AUTO_INCREMENT_ID_FIELD_DEFAULT = false; 047 048 public static final boolean READ_ONY_DEFAULT = false; 049 050 protected static final char DEFAULT_CHARACTER_SEPARATOR = ','; 051 052 private static final String[] SCRIPT_POLICIES = { "never", "on_missing_columns", "always", }; 053 054 private static final String DEFAULT_POLICY = "never"; 055 056 @XNode("@name") 057 public String name; 058 059 @XNode("schema") 060 public String schemaName; 061 062 @XNode("parentDirectory") 063 public String parentDirectory; 064 065 @XNode("dataSource") 066 public String dataSourceName; 067 068 @XNode("dbDriver") 069 public String dbDriver; 070 071 @XNode("dbUrl") 072 public String dbUrl; 073 074 @XNode("dbUser") 075 public String dbUser; 076 077 @XNode("dbPassword") 078 public String dbPassword; 079 080 @XNode("table") 081 public String tableName; 082 083 @XNodeList(value = "init-dependencies/dependency", type = ArrayList.class, componentType = String.class) 084 public List<String> initDependencies; 085 086 @XNode("idField") 087 public String idField; 088 089 @XNode("dataFile") 090 public String dataFileName; 091 092 @XNode(value = "dataFileCharacterSeparator", trim = false) 093 public String dataFileCharacterSeparator = ","; 094 095 public String createTablePolicy; 096 097 public SubstringMatchType substringMatchType; 098 099 @XNode("autoincrementIdField") 100 public Boolean autoincrementIdField; 101 102 @XNode("readOnly") 103 public Boolean readOnly; 104 105 @XNode("passwordField") 106 private String passwordField; 107 108 @XNode("passwordHashAlgorithm") 109 public String passwordHashAlgorithm; 110 111 @XNode("querySizeLimit") 112 private Integer querySizeLimit; 113 114 @XNodeList(value = "references/tableReference", type = TableReference[].class, componentType = TableReference.class) 115 private TableReference[] tableReferences; 116 117 @XNodeList(value = "references/inverseReference", type = InverseReference[].class, componentType = InverseReference.class) 118 private InverseReference[] inverseReferences; 119 120 @XNodeList(value = "permissions/permission", type = PermissionDescriptor[].class, componentType = PermissionDescriptor.class) 121 public PermissionDescriptor[] permissions = null; 122 123 @XNode("@remove") 124 private boolean remove = false; 125 126 @XNode("cacheEntryName") 127 public String cacheEntryName = null; 128 129 @XNode("cacheEntryWithoutReferencesName") 130 public String cacheEntryWithoutReferencesName = null; 131 132 @XNode("negativeCaching") 133 public Boolean negativeCaching; 134 135 @XNodeList(value = "filters/staticFilter", type = SQLStaticFilter[].class, componentType = SQLStaticFilter.class) 136 private SQLStaticFilter[] staticFilters; 137 138 @XNode("nativeCase") 139 public Boolean nativeCase; 140 141 @XNode("computeMultiTenantId") 142 private boolean computeMultiTenantId = true; 143 144 public String getDataSourceName() { 145 return dataSourceName; 146 } 147 148 public void setDataSourceName(String dataSourceName) { 149 this.dataSourceName = dataSourceName; 150 } 151 152 public void setIdField(String idField) { 153 this.idField = idField; 154 } 155 156 public String getName() { 157 return name; 158 } 159 160 public void setName(String name) { 161 this.name = name; 162 } 163 164 public String getSchemaName() { 165 return schemaName; 166 } 167 168 public void setSchemaName(String schemaName) { 169 this.schemaName = schemaName; 170 } 171 172 // XXX never used: is it supposed to help determining an entry full id 173 // using 174 // the parent directory id? 175 public String getParentDirectory() { 176 return parentDirectory; 177 } 178 179 public void setParentDirectory(String parentDirectory) { 180 this.parentDirectory = parentDirectory; 181 } 182 183 public String getTableName() { 184 return tableName; 185 } 186 187 public void setTableName(String tableName) { 188 this.tableName = tableName; 189 } 190 191 public String getDbDriver() { 192 return dbDriver; 193 } 194 195 public String getDbPassword() { 196 return dbPassword; 197 } 198 199 public String getDbUrl() { 200 return dbUrl; 201 } 202 203 public String getDbUser() { 204 return dbUser; 205 } 206 207 public String getDataFileName() { 208 return dataFileName; 209 } 210 211 public char getDataFileCharacterSeparator() { 212 if (dataFileCharacterSeparator == null || dataFileCharacterSeparator.length() == 0) { 213 log.info("Character separator not well set will " + "take the default value, \"" 214 + DEFAULT_CHARACTER_SEPARATOR + "\""); 215 return DEFAULT_CHARACTER_SEPARATOR; 216 } 217 218 if (dataFileCharacterSeparator.length() > 1) { 219 log.warn("More than one character found for character separator, " + "will take the first one \"" 220 + dataFileCharacterSeparator.charAt(0) + "\""); 221 } 222 223 return dataFileCharacterSeparator.charAt(0); 224 } 225 226 public String getPasswordField() { 227 return passwordField; 228 } 229 230 public void setPasswordField(String passwordField) { 231 this.passwordField = passwordField; 232 } 233 234 public String getIdField() { 235 return idField; 236 } 237 238 public String getCreateTablePolicy() { 239 return createTablePolicy; 240 } 241 242 @XNode("createTablePolicy") 243 public void setCreateTablePolicy(String createTablePolicy) throws DirectoryException { 244 if (createTablePolicy == null) { 245 this.createTablePolicy = DEFAULT_POLICY; 246 return; 247 } 248 createTablePolicy = createTablePolicy.toLowerCase(); 249 boolean validPolicy = false; 250 for (String policy : SCRIPT_POLICIES) { 251 if (createTablePolicy.equals(policy)) { 252 validPolicy = true; 253 break; 254 } 255 } 256 if (!validPolicy) { 257 throw new DirectoryException("invalid value for createTablePolicy: " + createTablePolicy 258 + ". It should be one of 'never', " + "'on_missing_columns', or 'always'."); 259 } 260 this.createTablePolicy = createTablePolicy; 261 } 262 263 @XNode("substringMatchType") 264 public void setSubstringMatchType(String substringMatchType) { 265 if (substringMatchType != null) { 266 try { 267 this.substringMatchType = Enum.valueOf(SubstringMatchType.class, substringMatchType); 268 } catch (IllegalArgumentException iae) { 269 log.error("Invalid substring match type: " + substringMatchType 270 + ". Valid options: subinitial, subfinal, subany"); 271 this.substringMatchType = SubstringMatchType.subinitial; 272 } 273 } 274 } 275 276 public Reference[] getInverseReferences() { 277 return inverseReferences; 278 } 279 280 public Reference[] getTableReferences() { 281 return tableReferences; 282 } 283 284 public Boolean getReadOnly() { 285 return readOnly == null ? Boolean.valueOf(READ_ONY_DEFAULT) : readOnly; 286 } 287 288 public void setReadOnly(Boolean readOnly) { 289 this.readOnly = readOnly; 290 } 291 292 public boolean isAutoincrementIdField() { 293 return autoincrementIdField == null ? AUTO_INCREMENT_ID_FIELD_DEFAULT : autoincrementIdField.booleanValue(); 294 } 295 296 public void setAutoincrementIdField(boolean autoincrementIdField) { 297 this.autoincrementIdField = Boolean.valueOf(autoincrementIdField); 298 } 299 300 public void setDbDriver(String dbDriver) { 301 this.dbDriver = dbDriver; 302 } 303 304 public void setDbPassword(String dbPassword) { 305 this.dbPassword = dbPassword; 306 } 307 308 public void setDbUrl(String dbUrl) { 309 this.dbUrl = dbUrl; 310 } 311 312 public void setDbUser(String dbUser) { 313 this.dbUser = dbUser; 314 } 315 316 public void setInverseReferences(InverseReference[] inverseReferences) { 317 this.inverseReferences = inverseReferences; 318 } 319 320 public void setDataFileName(String dataFile) { 321 this.dataFileName = dataFile; 322 } 323 324 public void setTableReferences(TableReference[] tableReferences) { 325 this.tableReferences = tableReferences; 326 } 327 328 public int getQuerySizeLimit() { 329 return querySizeLimit == null ? QUERY_SIZE_LIMIT_DEFAULT : querySizeLimit.intValue(); 330 } 331 332 public void setQuerySizeLimit(int querySizeLimit) { 333 this.querySizeLimit = Integer.valueOf(querySizeLimit); 334 } 335 336 public void setRemove(boolean delete) { 337 this.remove = delete; 338 } 339 340 public boolean getRemove() { 341 return this.remove; 342 } 343 344 public SubstringMatchType getSubstringMatchType() { 345 return substringMatchType == null ? SubstringMatchType.subinitial : substringMatchType; 346 } 347 348 public void setSubstringMatchType(SubstringMatchType substringMatchType) { 349 this.substringMatchType = substringMatchType; 350 } 351 352 public SQLStaticFilter[] getStaticFilters() { 353 if (staticFilters == null) { 354 return new SQLStaticFilter[0]; 355 } 356 return staticFilters; 357 } 358 359 /** 360 * Returns {@code true} if a multi tenant id should be computed for this directory, if the directory has support for 361 * multi tenancy, {@code false} otherwise. 362 * 363 * @since 5.6 364 */ 365 public boolean isComputeMultiTenantId() { 366 return computeMultiTenantId; 367 } 368 369 /** 370 * Merge re-written since 5.6 to comply to hot reload needs, omitting to merge properties initialized by xmap) 371 */ 372 public void merge(SQLDirectoryDescriptor other) { 373 merge(other, false); 374 } 375 376 public void merge(SQLDirectoryDescriptor other, boolean overwite) { 377 if (other.dataSourceName != null || overwite) { 378 dataSourceName = other.dataSourceName; 379 } 380 if (other.dbDriver != null || overwite) { 381 dbDriver = other.dbDriver; 382 } 383 if (other.dbUrl != null || overwite) { 384 dbUrl = other.dbUrl; 385 } 386 if (other.dbUser != null || overwite) { 387 dbUser = other.dbUser; 388 } 389 if (other.dbPassword != null || overwite) { 390 dbPassword = other.dbPassword; 391 } 392 if (other.tableName != null || overwite) { 393 tableName = other.tableName; 394 } 395 if (other.schemaName != null || overwite) { 396 schemaName = other.schemaName; 397 } 398 if (other.parentDirectory != null || overwite) { 399 parentDirectory = other.parentDirectory; 400 } 401 if ((other.initDependencies != null && other.initDependencies.size() != 0) || overwite) { 402 initDependencies = other.initDependencies; 403 } 404 if (other.idField != null || overwite) { 405 idField = other.idField; 406 } 407 if (other.dataFileName != null || overwite) { 408 dataFileName = other.dataFileName; 409 } 410 if (other.dataFileCharacterSeparator != null || overwite) { 411 dataFileCharacterSeparator = other.dataFileCharacterSeparator; 412 } 413 if (other.createTablePolicy != null || overwite) { 414 createTablePolicy = other.createTablePolicy; 415 } 416 if (other.substringMatchType != null || overwite) { 417 substringMatchType = other.substringMatchType; 418 } 419 if (other.autoincrementIdField != null || overwite) { 420 autoincrementIdField = other.autoincrementIdField; 421 } 422 if (other.readOnly != null || overwite) { 423 readOnly = other.readOnly; 424 } 425 if (other.passwordField != null || overwite) { 426 passwordField = other.passwordField; 427 } 428 if (other.passwordHashAlgorithm != null || overwite) { 429 passwordHashAlgorithm = other.passwordHashAlgorithm; 430 } 431 if (other.querySizeLimit != null || overwite) { 432 querySizeLimit = other.querySizeLimit; 433 } 434 435 if ((other.inverseReferences != null && other.inverseReferences.length != 0) || overwite) { 436 inverseReferences = other.inverseReferences; 437 } 438 if ((other.tableReferences != null && other.tableReferences.length != 0) || overwite) { 439 tableReferences = other.tableReferences; 440 } 441 if ((other.permissions != null && other.permissions.length != 0) || overwite) { 442 permissions = other.permissions; 443 } 444 445 remove = other.remove; 446 447 if (other.cacheEntryName != null || overwite) { 448 cacheEntryName = other.cacheEntryName; 449 } 450 if (other.cacheEntryWithoutReferencesName != null || overwite) { 451 cacheEntryWithoutReferencesName = other.cacheEntryWithoutReferencesName; 452 } 453 if (other.negativeCaching != null || overwite) { 454 negativeCaching = other.negativeCaching; 455 } 456 if ((other.staticFilters != null && other.staticFilters.length != 0) || overwite) { 457 staticFilters = other.staticFilters; 458 } 459 if (other.nativeCase != null || overwite) { 460 nativeCase = other.nativeCase; 461 } 462 463 computeMultiTenantId = other.computeMultiTenantId; 464 } 465 466 public SQLDirectoryDescriptor clone() { 467 SQLDirectoryDescriptor clone = new SQLDirectoryDescriptor(); 468 clone.name = name; 469 clone.schemaName = schemaName; 470 clone.parentDirectory = parentDirectory; 471 clone.dataSourceName = dataSourceName; 472 clone.dbDriver = dbDriver; 473 clone.dbUrl = dbUrl; 474 clone.dbUser = dbUser; 475 clone.dbPassword = dbPassword; 476 clone.tableName = tableName; 477 if (initDependencies != null) { 478 clone.initDependencies = new ArrayList<String>(initDependencies); 479 } 480 clone.idField = idField; 481 clone.dataFileName = dataFileName; 482 clone.dataFileCharacterSeparator = dataFileCharacterSeparator; 483 clone.createTablePolicy = createTablePolicy; 484 clone.substringMatchType = substringMatchType; 485 clone.autoincrementIdField = autoincrementIdField; 486 clone.readOnly = readOnly; 487 clone.passwordField = passwordField; 488 clone.passwordHashAlgorithm = passwordHashAlgorithm; 489 clone.querySizeLimit = querySizeLimit; 490 if (tableReferences != null) { 491 clone.tableReferences = new TableReference[tableReferences.length]; 492 for (int i = 0; i < tableReferences.length; i++) { 493 clone.tableReferences[i] = tableReferences[i].clone(); 494 } 495 } 496 if (inverseReferences != null) { 497 clone.inverseReferences = new InverseReference[inverseReferences.length]; 498 for (int i = 0; i < inverseReferences.length; i++) { 499 clone.inverseReferences[i] = inverseReferences[i].clone(); 500 } 501 } 502 if (permissions != null) { 503 clone.permissions = new PermissionDescriptor[permissions.length]; 504 for (int i = 0; i < permissions.length; i++) { 505 clone.permissions[i] = permissions[i].clone(); 506 } 507 } 508 clone.remove = remove; 509 clone.cacheEntryName = cacheEntryName; 510 clone.cacheEntryWithoutReferencesName = cacheEntryWithoutReferencesName; 511 clone.negativeCaching = negativeCaching; 512 if (staticFilters != null) { 513 clone.staticFilters = new SQLStaticFilter[staticFilters.length]; 514 for (int i = 0; i < staticFilters.length; i++) { 515 clone.staticFilters[i] = staticFilters[i].clone(); 516 } 517 } 518 clone.nativeCase = nativeCase; 519 clone.computeMultiTenantId = computeMultiTenantId; 520 return clone; 521 } 522}