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 @XNodeList(value = "filters/staticFilter", type = SQLStaticFilter[].class, componentType = SQLStaticFilter.class) 133 private SQLStaticFilter[] staticFilters; 134 135 @XNode("nativeCase") 136 public Boolean nativeCase; 137 138 @XNode("computeMultiTenantId") 139 private boolean computeMultiTenantId = true; 140 141 public String getDataSourceName() { 142 return dataSourceName; 143 } 144 145 public void setDataSourceName(String dataSourceName) { 146 this.dataSourceName = dataSourceName; 147 } 148 149 public void setIdField(String idField) { 150 this.idField = idField; 151 } 152 153 public String getName() { 154 return name; 155 } 156 157 public void setName(String name) { 158 this.name = name; 159 } 160 161 public String getSchemaName() { 162 return schemaName; 163 } 164 165 public void setSchemaName(String schemaName) { 166 this.schemaName = schemaName; 167 } 168 169 // XXX never used: is it supposed to help determining an entry full id 170 // using 171 // the parent directory id? 172 public String getParentDirectory() { 173 return parentDirectory; 174 } 175 176 public void setParentDirectory(String parentDirectory) { 177 this.parentDirectory = parentDirectory; 178 } 179 180 public String getTableName() { 181 return tableName; 182 } 183 184 public void setTableName(String tableName) { 185 this.tableName = tableName; 186 } 187 188 public String getDbDriver() { 189 return dbDriver; 190 } 191 192 public String getDbPassword() { 193 return dbPassword; 194 } 195 196 public String getDbUrl() { 197 return dbUrl; 198 } 199 200 public String getDbUser() { 201 return dbUser; 202 } 203 204 public String getDataFileName() { 205 return dataFileName; 206 } 207 208 public char getDataFileCharacterSeparator() { 209 if (dataFileCharacterSeparator == null || dataFileCharacterSeparator.length() == 0) { 210 log.info("Character separator not well set will " + "take the default value, \"" 211 + DEFAULT_CHARACTER_SEPARATOR + "\""); 212 return DEFAULT_CHARACTER_SEPARATOR; 213 } 214 215 if (dataFileCharacterSeparator.length() > 1) { 216 log.warn("More than one character found for character separator, " + "will take the first one \"" 217 + dataFileCharacterSeparator.charAt(0) + "\""); 218 } 219 220 return dataFileCharacterSeparator.charAt(0); 221 } 222 223 public String getPasswordField() { 224 return passwordField; 225 } 226 227 public void setPasswordField(String passwordField) { 228 this.passwordField = passwordField; 229 } 230 231 public String getIdField() { 232 return idField; 233 } 234 235 public String getCreateTablePolicy() { 236 return createTablePolicy; 237 } 238 239 @XNode("createTablePolicy") 240 public void setCreateTablePolicy(String createTablePolicy) throws DirectoryException { 241 if (createTablePolicy == null) { 242 this.createTablePolicy = DEFAULT_POLICY; 243 return; 244 } 245 createTablePolicy = createTablePolicy.toLowerCase(); 246 boolean validPolicy = false; 247 for (String policy : SCRIPT_POLICIES) { 248 if (createTablePolicy.equals(policy)) { 249 validPolicy = true; 250 break; 251 } 252 } 253 if (!validPolicy) { 254 throw new DirectoryException("invalid value for createTablePolicy: " + createTablePolicy 255 + ". It should be one of 'never', " + "'on_missing_columns', or 'always'."); 256 } 257 this.createTablePolicy = createTablePolicy; 258 } 259 260 @XNode("substringMatchType") 261 public void setSubstringMatchType(String substringMatchType) { 262 if (substringMatchType != null) { 263 try { 264 this.substringMatchType = Enum.valueOf(SubstringMatchType.class, substringMatchType); 265 } catch (IllegalArgumentException iae) { 266 log.error("Invalid substring match type: " + substringMatchType 267 + ". Valid options: subinitial, subfinal, subany"); 268 this.substringMatchType = SubstringMatchType.subinitial; 269 } 270 } 271 } 272 273 public Reference[] getInverseReferences() { 274 return inverseReferences; 275 } 276 277 public Reference[] getTableReferences() { 278 return tableReferences; 279 } 280 281 public Boolean getReadOnly() { 282 return readOnly == null ? Boolean.valueOf(READ_ONY_DEFAULT) : readOnly; 283 } 284 285 public void setReadOnly(Boolean readOnly) { 286 this.readOnly = readOnly; 287 } 288 289 public boolean isAutoincrementIdField() { 290 return autoincrementIdField == null ? AUTO_INCREMENT_ID_FIELD_DEFAULT : autoincrementIdField.booleanValue(); 291 } 292 293 public void setAutoincrementIdField(boolean autoincrementIdField) { 294 this.autoincrementIdField = Boolean.valueOf(autoincrementIdField); 295 } 296 297 public void setDbDriver(String dbDriver) { 298 this.dbDriver = dbDriver; 299 } 300 301 public void setDbPassword(String dbPassword) { 302 this.dbPassword = dbPassword; 303 } 304 305 public void setDbUrl(String dbUrl) { 306 this.dbUrl = dbUrl; 307 } 308 309 public void setDbUser(String dbUser) { 310 this.dbUser = dbUser; 311 } 312 313 public void setInverseReferences(InverseReference[] inverseReferences) { 314 this.inverseReferences = inverseReferences; 315 } 316 317 public void setDataFileName(String dataFile) { 318 this.dataFileName = dataFile; 319 } 320 321 public void setTableReferences(TableReference[] tableReferences) { 322 this.tableReferences = tableReferences; 323 } 324 325 public int getQuerySizeLimit() { 326 return querySizeLimit == null ? QUERY_SIZE_LIMIT_DEFAULT : querySizeLimit.intValue(); 327 } 328 329 public void setQuerySizeLimit(int querySizeLimit) { 330 this.querySizeLimit = Integer.valueOf(querySizeLimit); 331 } 332 333 public void setRemove(boolean delete) { 334 this.remove = delete; 335 } 336 337 public boolean getRemove() { 338 return this.remove; 339 } 340 341 public SubstringMatchType getSubstringMatchType() { 342 return substringMatchType == null ? SubstringMatchType.subinitial : substringMatchType; 343 } 344 345 public void setSubstringMatchType(SubstringMatchType substringMatchType) { 346 this.substringMatchType = substringMatchType; 347 } 348 349 public SQLStaticFilter[] getStaticFilters() { 350 if (staticFilters == null) { 351 return new SQLStaticFilter[0]; 352 } 353 return staticFilters; 354 } 355 356 /** 357 * Returns {@code true} if a multi tenant id should be computed for this directory, if the directory has support for 358 * multi tenancy, {@code false} otherwise. 359 * 360 * @since 5.6 361 */ 362 public boolean isComputeMultiTenantId() { 363 return computeMultiTenantId; 364 } 365 366 /** 367 * Merge re-written since 5.6 to comply to hot reload needs, omitting to merge properties initialized by xmap) 368 */ 369 public void merge(SQLDirectoryDescriptor other) { 370 merge(other, false); 371 } 372 373 public void merge(SQLDirectoryDescriptor other, boolean overwite) { 374 if (other.dataSourceName != null || overwite) { 375 dataSourceName = other.dataSourceName; 376 } 377 if (other.dbDriver != null || overwite) { 378 dbDriver = other.dbDriver; 379 } 380 if (other.dbUrl != null || overwite) { 381 dbUrl = other.dbUrl; 382 } 383 if (other.dbUser != null || overwite) { 384 dbUser = other.dbUser; 385 } 386 if (other.dbPassword != null || overwite) { 387 dbPassword = other.dbPassword; 388 } 389 if (other.tableName != null || overwite) { 390 tableName = other.tableName; 391 } 392 if (other.schemaName != null || overwite) { 393 schemaName = other.schemaName; 394 } 395 if (other.parentDirectory != null || overwite) { 396 parentDirectory = other.parentDirectory; 397 } 398 if ((other.initDependencies != null && other.initDependencies.size() != 0) || overwite) { 399 initDependencies = other.initDependencies; 400 } 401 if (other.idField != null || overwite) { 402 idField = other.idField; 403 } 404 if (other.dataFileName != null || overwite) { 405 dataFileName = other.dataFileName; 406 } 407 if (other.dataFileCharacterSeparator != null || overwite) { 408 dataFileCharacterSeparator = other.dataFileCharacterSeparator; 409 } 410 if (other.createTablePolicy != null || overwite) { 411 createTablePolicy = other.createTablePolicy; 412 } 413 if (other.substringMatchType != null || overwite) { 414 substringMatchType = other.substringMatchType; 415 } 416 if (other.autoincrementIdField != null || overwite) { 417 autoincrementIdField = other.autoincrementIdField; 418 } 419 if (other.readOnly != null || overwite) { 420 readOnly = other.readOnly; 421 } 422 if (other.passwordField != null || overwite) { 423 passwordField = other.passwordField; 424 } 425 if (other.passwordHashAlgorithm != null || overwite) { 426 passwordHashAlgorithm = other.passwordHashAlgorithm; 427 } 428 if (other.querySizeLimit != null || overwite) { 429 querySizeLimit = other.querySizeLimit; 430 } 431 432 if ((other.inverseReferences != null && other.inverseReferences.length != 0) || overwite) { 433 inverseReferences = other.inverseReferences; 434 } 435 if ((other.tableReferences != null && other.tableReferences.length != 0) || overwite) { 436 tableReferences = other.tableReferences; 437 } 438 if ((other.permissions != null && other.permissions.length != 0) || overwite) { 439 permissions = other.permissions; 440 } 441 442 remove = other.remove; 443 444 if (other.cacheEntryName != null || overwite) { 445 cacheEntryName = other.cacheEntryName; 446 } 447 if (other.cacheEntryWithoutReferencesName != null || overwite) { 448 cacheEntryWithoutReferencesName = other.cacheEntryWithoutReferencesName; 449 } 450 if ((other.staticFilters != null && other.staticFilters.length != 0) || overwite) { 451 staticFilters = other.staticFilters; 452 } 453 if (other.nativeCase != null || overwite) { 454 nativeCase = other.nativeCase; 455 } 456 457 computeMultiTenantId = other.computeMultiTenantId; 458 } 459 460 public SQLDirectoryDescriptor clone() { 461 SQLDirectoryDescriptor clone = new SQLDirectoryDescriptor(); 462 clone.name = name; 463 clone.schemaName = schemaName; 464 clone.parentDirectory = parentDirectory; 465 clone.dataSourceName = dataSourceName; 466 clone.dbDriver = dbDriver; 467 clone.dbUrl = dbUrl; 468 clone.dbUser = dbUser; 469 clone.dbPassword = dbPassword; 470 clone.tableName = tableName; 471 if (initDependencies != null) { 472 clone.initDependencies = new ArrayList<String>(initDependencies); 473 } 474 clone.idField = idField; 475 clone.dataFileName = dataFileName; 476 clone.dataFileCharacterSeparator = dataFileCharacterSeparator; 477 clone.createTablePolicy = createTablePolicy; 478 clone.substringMatchType = substringMatchType; 479 clone.autoincrementIdField = autoincrementIdField; 480 clone.readOnly = readOnly; 481 clone.passwordField = passwordField; 482 clone.passwordHashAlgorithm = passwordHashAlgorithm; 483 clone.querySizeLimit = querySizeLimit; 484 if (tableReferences != null) { 485 clone.tableReferences = new TableReference[tableReferences.length]; 486 for (int i = 0; i < tableReferences.length; i++) { 487 clone.tableReferences[i] = tableReferences[i].clone(); 488 } 489 } 490 if (inverseReferences != null) { 491 clone.inverseReferences = new InverseReference[inverseReferences.length]; 492 for (int i = 0; i < inverseReferences.length; i++) { 493 clone.inverseReferences[i] = inverseReferences[i].clone(); 494 } 495 } 496 if (permissions != null) { 497 clone.permissions = new PermissionDescriptor[permissions.length]; 498 for (int i = 0; i < permissions.length; i++) { 499 clone.permissions[i] = permissions[i].clone(); 500 } 501 } 502 clone.remove = remove; 503 clone.cacheEntryName = cacheEntryName; 504 clone.cacheEntryWithoutReferencesName = cacheEntryWithoutReferencesName; 505 if (staticFilters != null) { 506 clone.staticFilters = new SQLStaticFilter[staticFilters.length]; 507 for (int i = 0; i < staticFilters.length; i++) { 508 clone.staticFilters[i] = staticFilters[i].clone(); 509 } 510 } 511 clone.nativeCase = nativeCase; 512 clone.computeMultiTenantId = computeMultiTenantId; 513 return clone; 514 } 515}