001/* 002 * (C) Copyright 2006-2015 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Florent Guillaume 018 */ 019package org.nuxeo.ecm.core.storage.sql; 020 021import java.util.ArrayList; 022import java.util.HashMap; 023import java.util.HashSet; 024import java.util.List; 025import java.util.Map; 026import java.util.Set; 027 028import org.apache.commons.lang.StringUtils; 029import org.apache.commons.logging.Log; 030import org.apache.commons.logging.LogFactory; 031import org.nuxeo.common.xmap.annotation.XNode; 032import org.nuxeo.common.xmap.annotation.XNodeList; 033import org.nuxeo.common.xmap.annotation.XNodeMap; 034import org.nuxeo.common.xmap.annotation.XObject; 035import org.nuxeo.ecm.core.storage.FulltextDescriptor; 036import org.nuxeo.ecm.core.storage.FulltextDescriptor.FulltextIndexDescriptor; 037import org.nuxeo.runtime.jtajca.NuxeoConnectionManagerConfiguration; 038 039/** 040 * Low-level VCS Repository Descriptor. 041 */ 042@XObject(value = "repository", order = { "@name" }) 043public class RepositoryDescriptor { 044 045 private static final Log log = LogFactory.getLog(RepositoryDescriptor.class); 046 047 public static final int DEFAULT_READ_ACL_MAX_SIZE = 4096; 048 049 public static final int DEFAULT_PATH_OPTIM_VERSION = 2; 050 051 /** At startup, DDL changes are not detected. */ 052 public static final String DDL_MODE_IGNORE = "ignore"; 053 054 /** At startup, DDL changes are detected and if not empty they are dumped. */ 055 public static final String DDL_MODE_DUMP = "dump"; 056 057 /** At startup, DDL changes are detected and executed. */ 058 public static final String DDL_MODE_EXECUTE = "execute"; 059 060 /** At startup, DDL changes are detected and if not empty Nuxeo startup is aborted. */ 061 public static final String DDL_MODE_ABORT = "abort"; 062 063 /** Specifies that stored procedure detection must be compatible with previous Nuxeo versions. */ 064 public static final String DDL_MODE_COMPAT = "compat"; 065 066 @XObject(value = "field") 067 public static class FieldDescriptor { 068 069 // empty constructor needed by XMap 070 public FieldDescriptor() { 071 } 072 073 /** Copy constructor. */ 074 public FieldDescriptor(FieldDescriptor other) { 075 type = other.type; 076 field = other.field; 077 table = other.table; 078 column = other.column; 079 } 080 081 public static List<FieldDescriptor> copyList(List<FieldDescriptor> other) { 082 List<FieldDescriptor> copy = new ArrayList<>(other.size()); 083 for (FieldDescriptor fd : other) { 084 copy.add(new FieldDescriptor(fd)); 085 } 086 return copy; 087 } 088 089 public void merge(FieldDescriptor other) { 090 if (other.field != null) { 091 field = other.field; 092 } 093 if (other.type != null) { 094 type = other.type; 095 } 096 if (other.table != null) { 097 table = other.table; 098 } 099 if (other.column != null) { 100 column = other.column; 101 } 102 } 103 104 @XNode("@type") 105 public String type; 106 107 public String field; 108 109 @XNode("@name") 110 public void setName(String name) { 111 if (!StringUtils.isBlank(name) && field == null) { 112 field = name; 113 } 114 } 115 116 // compat with older syntax 117 @XNode 118 public void setXNodeContent(String name) { 119 setName(name); 120 } 121 122 @XNode("@table") 123 public String table; 124 125 @XNode("@column") 126 public String column; 127 128 @Override 129 public String toString() { 130 return this.getClass().getSimpleName() + '(' + field + ",type=" + type + ",table=" + table + ",column=" 131 + column + ")"; 132 } 133 } 134 135 /** False if the boolean is null or FALSE, true otherwise. */ 136 private static boolean defaultFalse(Boolean bool) { 137 return Boolean.TRUE.equals(bool); 138 } 139 140 /** True if the boolean is null or TRUE, false otherwise. */ 141 private static boolean defaultTrue(Boolean bool) { 142 return !Boolean.FALSE.equals(bool); 143 } 144 145 public String name; 146 147 @XNode("@name") 148 public void setName(String name) { 149 this.name = name; 150 } 151 152 @XNode("@label") 153 public String label; 154 155 @XNode("@isDefault") 156 private Boolean isDefault; 157 158 public Boolean isDefault() { 159 return isDefault; 160 } 161 162 // compat, when used with old-style extension point syntax 163 // and nested repository 164 @XNode("repository") 165 public RepositoryDescriptor repositoryDescriptor; 166 167 public NuxeoConnectionManagerConfiguration pool; 168 169 @XNode("pool") 170 public void setPool(NuxeoConnectionManagerConfiguration pool) { 171 pool.setName("repository/" + name); 172 this.pool = pool; 173 } 174 175 @XNode("backendClass") 176 public Class<? extends RepositoryBackend> backendClass; 177 178 @XNode("clusterInvalidatorClass") 179 public Class<? extends ClusterInvalidator> clusterInvalidatorClass; 180 181 @XNode("cachingMapper@class") 182 public Class<? extends CachingMapper> cachingMapperClass; 183 184 @XNode("cachingMapper@enabled") 185 private Boolean cachingMapperEnabled; 186 187 public boolean getCachingMapperEnabled() { 188 return defaultTrue(cachingMapperEnabled); 189 } 190 191 @XNodeMap(value = "cachingMapper/property", key = "@name", type = HashMap.class, componentType = String.class) 192 public Map<String, String> cachingMapperProperties = new HashMap<>(); 193 194 @XNode("ddlMode") 195 private String ddlMode; 196 197 public String getDDLMode() { 198 return ddlMode; 199 } 200 201 @XNode("noDDL") 202 private Boolean noDDL; 203 204 public boolean getNoDDL() { 205 return defaultFalse(noDDL); 206 } 207 208 @XNodeList(value = "sqlInitFile", type = ArrayList.class, componentType = String.class) 209 public List<String> sqlInitFiles = new ArrayList<>(0); 210 211 @XNode("softDelete@enabled") 212 private Boolean softDeleteEnabled; 213 214 public boolean getSoftDeleteEnabled() { 215 return defaultFalse(softDeleteEnabled); 216 } 217 218 protected void setSoftDeleteEnabled(boolean enabled) { 219 softDeleteEnabled = Boolean.valueOf(enabled); 220 } 221 222 @XNode("proxies@enabled") 223 private Boolean proxiesEnabled; 224 225 public boolean getProxiesEnabled() { 226 return defaultTrue(proxiesEnabled); 227 } 228 229 protected void setProxiesEnabled(boolean enabled) { 230 proxiesEnabled = Boolean.valueOf(enabled); 231 } 232 233 @XNode("idType") 234 public String idType; // "varchar", "uuid", "sequence" 235 236 @XNode("clustering@id") 237 private String clusterNodeId; 238 239 public String getClusterNodeId() { 240 return clusterNodeId; 241 } 242 243 @XNode("clustering@enabled") 244 private Boolean clusteringEnabled; 245 246 public boolean getClusteringEnabled() { 247 return defaultFalse(clusteringEnabled); 248 } 249 250 protected void setClusteringEnabled(boolean enabled) { 251 clusteringEnabled = Boolean.valueOf(enabled); 252 } 253 254 @XNode("clustering@delay") 255 private Long clusteringDelay; 256 257 public long getClusteringDelay() { 258 return clusteringDelay == null ? 0 : clusteringDelay.longValue(); 259 } 260 261 protected void setClusteringDelay(long delay) { 262 clusteringDelay = Long.valueOf(delay); 263 } 264 265 @XNodeList(value = "schema/field", type = ArrayList.class, componentType = FieldDescriptor.class) 266 public List<FieldDescriptor> schemaFields = new ArrayList<>(0); 267 268 @XNode("schema/arrayColumns") 269 private Boolean arrayColumns; 270 271 public boolean getArrayColumns() { 272 return defaultFalse(arrayColumns); 273 } 274 275 public void setArrayColumns(boolean enabled) { 276 arrayColumns = Boolean.valueOf(enabled); 277 } 278 279 @XNode("childNameUniqueConstraintEnabled") 280 private Boolean childNameUniqueConstraintEnabled; 281 282 public boolean getChildNameUniqueConstraintEnabled() { 283 return defaultTrue(childNameUniqueConstraintEnabled); 284 } 285 286 @XNode("collectionUniqueConstraintEnabled") 287 private Boolean collectionUniqueConstraintEnabled; 288 289 public boolean getCollectionUniqueConstraintEnabled() { 290 return defaultTrue(collectionUniqueConstraintEnabled); 291 } 292 293 @XNode("indexing/queryMaker@class") 294 public void setQueryMakerDeprecated(String klass) { 295 log.warn("Setting queryMaker from repository configuration is now deprecated"); 296 } 297 298 // VCS-specific fulltext indexing options 299 private String fulltextAnalyzer; 300 301 public String getFulltextAnalyzer() { 302 return fulltextAnalyzer; 303 } 304 305 @XNode("indexing/fulltext@analyzer") 306 public void setFulltextAnalyzer(String fulltextAnalyzer) { 307 this.fulltextAnalyzer = fulltextAnalyzer; 308 } 309 310 private String fulltextCatalog; 311 312 public String getFulltextCatalog() { 313 return fulltextCatalog; 314 } 315 316 @XNode("indexing/fulltext@catalog") 317 public void setFulltextCatalog(String fulltextCatalog) { 318 this.fulltextCatalog = fulltextCatalog; 319 } 320 321 private FulltextDescriptor fulltextDescriptor = new FulltextDescriptor(); 322 323 public FulltextDescriptor getFulltextDescriptor() { 324 return fulltextDescriptor; 325 } 326 327 @XNode("indexing/fulltext@fieldSizeLimit") 328 public void setFulltextFieldSizeLimit(int fieldSizeLimit) { 329 fulltextDescriptor.setFulltextFieldSizeLimit(fieldSizeLimit); 330 } 331 332 @XNode("indexing/fulltext@disabled") 333 public void setFulltextDisabled(boolean disabled) { 334 fulltextDescriptor.setFulltextDisabled(disabled); 335 } 336 337 @XNode("indexing/fulltext@searchDisabled") 338 public void setFulltextSearchDisabled(boolean disabled) { 339 fulltextDescriptor.setFulltextSearchDisabled(disabled); 340 } 341 342 @XNode("indexing/fulltext@parser") 343 public void setFulltextParser(String fulltextParser) { 344 fulltextDescriptor.setFulltextParser(fulltextParser); 345 } 346 347 @XNodeList(value = "indexing/fulltext/index", type = ArrayList.class, componentType = FulltextIndexDescriptor.class) 348 public void setFulltextIndexes(List<FulltextIndexDescriptor> fulltextIndexes) { 349 fulltextDescriptor.setFulltextIndexes(fulltextIndexes); 350 } 351 352 @XNodeList(value = "indexing/excludedTypes/type", type = HashSet.class, componentType = String.class) 353 public void setFulltextExcludedTypes(Set<String> fulltextExcludedTypes) { 354 fulltextDescriptor.setFulltextExcludedTypes(fulltextExcludedTypes); 355 } 356 357 @XNodeList(value = "indexing/includedTypes/type", type = HashSet.class, componentType = String.class) 358 public void setFulltextIncludedTypes(Set<String> fulltextIncludedTypes) { 359 fulltextDescriptor.setFulltextIncludedTypes(fulltextIncludedTypes); 360 } 361 362 // compat 363 @XNodeList(value = "indexing/neverPerDocumentFacets/facet", type = HashSet.class, componentType = String.class) 364 public Set<String> neverPerInstanceMixins = new HashSet<>(0); 365 366 @XNode("pathOptimizations@enabled") 367 private Boolean pathOptimizationsEnabled; 368 369 public boolean getPathOptimizationsEnabled() { 370 return defaultTrue(pathOptimizationsEnabled); 371 } 372 373 protected void setPathOptimizationsEnabled(boolean enabled) { 374 pathOptimizationsEnabled = Boolean.valueOf(enabled); 375 } 376 377 /* @since 5.7 */ 378 @XNode("pathOptimizations@version") 379 private Integer pathOptimizationsVersion; 380 381 public int getPathOptimizationsVersion() { 382 return pathOptimizationsVersion == null ? DEFAULT_PATH_OPTIM_VERSION : pathOptimizationsVersion.intValue(); 383 } 384 385 @XNode("aclOptimizations@enabled") 386 private Boolean aclOptimizationsEnabled; 387 388 public boolean getAclOptimizationsEnabled() { 389 return defaultTrue(aclOptimizationsEnabled); 390 } 391 392 protected void setAclOptimizationsEnabled(boolean enabled) { 393 aclOptimizationsEnabled = Boolean.valueOf(enabled); 394 } 395 396 /* @since 5.4.2 */ 397 @XNode("aclOptimizations@readAclMaxSize") 398 private Integer readAclMaxSize; 399 400 public int getReadAclMaxSize() { 401 return readAclMaxSize == null ? DEFAULT_READ_ACL_MAX_SIZE : readAclMaxSize.intValue(); 402 } 403 404 @XNode("usersSeparator@key") 405 public String usersSeparatorKey; 406 407 /** @since 9.1 */ 408 @XNode("changeTokenEnabled") 409 private Boolean changeTokenEnabled; 410 411 /** @since 9.1 */ 412 public boolean isChangeTokenEnabled() { 413 return defaultFalse(changeTokenEnabled); 414 } 415 416 /** @since 9.1 */ 417 public void setChangeTokenEnabled(boolean enabled) { 418 this.changeTokenEnabled = Boolean.valueOf(enabled); 419 } 420 421 public RepositoryDescriptor() { 422 } 423 424 /** Copy constructor. */ 425 public RepositoryDescriptor(RepositoryDescriptor other) { 426 name = other.name; 427 label = other.label; 428 isDefault = other.isDefault; 429 pool = other.pool == null ? null : new NuxeoConnectionManagerConfiguration(other.pool); 430 backendClass = other.backendClass; 431 clusterInvalidatorClass = other.clusterInvalidatorClass; 432 cachingMapperClass = other.cachingMapperClass; 433 cachingMapperEnabled = other.cachingMapperEnabled; 434 cachingMapperProperties = new HashMap<>(other.cachingMapperProperties); 435 noDDL = other.noDDL; 436 ddlMode = other.ddlMode; 437 sqlInitFiles = new ArrayList<>(other.sqlInitFiles); 438 softDeleteEnabled = other.softDeleteEnabled; 439 proxiesEnabled = other.proxiesEnabled; 440 schemaFields = FieldDescriptor.copyList(other.schemaFields); 441 arrayColumns = other.arrayColumns; 442 childNameUniqueConstraintEnabled = other.childNameUniqueConstraintEnabled; 443 collectionUniqueConstraintEnabled = other.collectionUniqueConstraintEnabled; 444 idType = other.idType; 445 clusterNodeId = other.clusterNodeId; 446 clusteringEnabled = other.clusteringEnabled; 447 clusteringDelay = other.clusteringDelay; 448 fulltextAnalyzer = other.fulltextAnalyzer; 449 fulltextCatalog = other.fulltextCatalog; 450 fulltextDescriptor = new FulltextDescriptor(other.fulltextDescriptor); 451 neverPerInstanceMixins = other.neverPerInstanceMixins; 452 pathOptimizationsEnabled = other.pathOptimizationsEnabled; 453 pathOptimizationsVersion = other.pathOptimizationsVersion; 454 aclOptimizationsEnabled = other.aclOptimizationsEnabled; 455 readAclMaxSize = other.readAclMaxSize; 456 usersSeparatorKey = other.usersSeparatorKey; 457 changeTokenEnabled = other.changeTokenEnabled; 458 } 459 460 public void merge(RepositoryDescriptor other) { 461 if (other.name != null) { 462 name = other.name; 463 } 464 if (other.label != null) { 465 label = other.label; 466 } 467 if (other.isDefault != null) { 468 isDefault = other.isDefault; 469 } 470 if (other.pool != null) { 471 if (pool == null) { 472 pool = new NuxeoConnectionManagerConfiguration(other.pool); 473 } else { 474 pool.merge(other.pool); 475 } 476 } 477 if (other.backendClass != null) { 478 backendClass = other.backendClass; 479 } 480 if (other.clusterInvalidatorClass != null) { 481 clusterInvalidatorClass = other.clusterInvalidatorClass; 482 } 483 if (other.cachingMapperClass != null) { 484 cachingMapperClass = other.cachingMapperClass; 485 } 486 if (other.cachingMapperEnabled != null) { 487 cachingMapperEnabled = other.cachingMapperEnabled; 488 } 489 cachingMapperProperties.putAll(other.cachingMapperProperties); 490 if (other.noDDL != null) { 491 noDDL = other.noDDL; 492 } 493 if (other.ddlMode != null) { 494 ddlMode = other.ddlMode; 495 } 496 sqlInitFiles.addAll(other.sqlInitFiles); 497 if (other.softDeleteEnabled != null) { 498 softDeleteEnabled = other.softDeleteEnabled; 499 } 500 if (other.proxiesEnabled != null) { 501 proxiesEnabled = other.proxiesEnabled; 502 } 503 if (other.idType != null) { 504 idType = other.idType; 505 } 506 if (other.clusterNodeId != null) { 507 clusterNodeId = other.clusterNodeId; 508 } 509 if (other.clusteringEnabled != null) { 510 clusteringEnabled = other.clusteringEnabled; 511 } 512 if (other.clusteringDelay != null) { 513 clusteringDelay = other.clusteringDelay; 514 } 515 for (FieldDescriptor of : other.schemaFields) { 516 boolean append = true; 517 for (FieldDescriptor f : schemaFields) { 518 if (f.field.equals(of.field)) { 519 f.merge(of); 520 append = false; 521 break; 522 } 523 } 524 if (append) { 525 schemaFields.add(of); 526 } 527 } 528 if (other.arrayColumns != null) { 529 arrayColumns = other.arrayColumns; 530 } 531 if (other.childNameUniqueConstraintEnabled != null) { 532 childNameUniqueConstraintEnabled = other.childNameUniqueConstraintEnabled; 533 } 534 if (other.collectionUniqueConstraintEnabled != null) { 535 collectionUniqueConstraintEnabled = other.collectionUniqueConstraintEnabled; 536 } 537 if (other.fulltextAnalyzer != null) { 538 fulltextAnalyzer = other.fulltextAnalyzer; 539 } 540 if (other.fulltextCatalog != null) { 541 fulltextCatalog = other.fulltextCatalog; 542 } 543 fulltextDescriptor.merge(other.fulltextDescriptor); 544 neverPerInstanceMixins.addAll(other.neverPerInstanceMixins); 545 if (other.pathOptimizationsEnabled != null) { 546 pathOptimizationsEnabled = other.pathOptimizationsEnabled; 547 } 548 if (other.pathOptimizationsVersion != null) { 549 pathOptimizationsVersion = other.pathOptimizationsVersion; 550 } 551 if (other.aclOptimizationsEnabled != null) { 552 aclOptimizationsEnabled = other.aclOptimizationsEnabled; 553 } 554 if (other.readAclMaxSize != null) { 555 readAclMaxSize = other.readAclMaxSize; 556 } 557 if (other.usersSeparatorKey != null) { 558 usersSeparatorKey = other.usersSeparatorKey; 559 } 560 if (other.changeTokenEnabled != null) { 561 changeTokenEnabled = other.changeTokenEnabled; 562 } 563 } 564 565}