001/* 002 * (C) Copyright 2006-2018 Nuxeo (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.lang3.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.api.repository.PoolConfiguration; 036import org.nuxeo.ecm.core.storage.FulltextDescriptor; 037import org.nuxeo.ecm.core.storage.FulltextDescriptor.FulltextIndexDescriptor; 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 /** @deprecated since 11.1, was PostgreSQL-specific */ 048 @Deprecated 049 public static final int DEFAULT_READ_ACL_MAX_SIZE = 4096; 050 051 public static final int DEFAULT_PATH_OPTIM_VERSION = 2; 052 053 /** At startup, DDL changes are not detected. */ 054 public static final String DDL_MODE_IGNORE = "ignore"; 055 056 /** At startup, DDL changes are detected and if not empty they are dumped. */ 057 public static final String DDL_MODE_DUMP = "dump"; 058 059 /** At startup, DDL changes are detected and executed. */ 060 public static final String DDL_MODE_EXECUTE = "execute"; 061 062 /** At startup, DDL changes are detected and if not empty Nuxeo startup is aborted. */ 063 public static final String DDL_MODE_ABORT = "abort"; 064 065 /** Specifies that stored procedure detection must be compatible with previous Nuxeo versions. */ 066 public static final String DDL_MODE_COMPAT = "compat"; 067 068 @XObject(value = "field") 069 public static class FieldDescriptor { 070 071 // empty constructor needed by XMap 072 public FieldDescriptor() { 073 } 074 075 /** Copy constructor. */ 076 public FieldDescriptor(FieldDescriptor other) { 077 type = other.type; 078 field = other.field; 079 table = other.table; 080 column = other.column; 081 } 082 083 public static List<FieldDescriptor> copyList(List<FieldDescriptor> other) { 084 List<FieldDescriptor> copy = new ArrayList<>(other.size()); 085 for (FieldDescriptor fd : other) { 086 copy.add(new FieldDescriptor(fd)); 087 } 088 return copy; 089 } 090 091 public void merge(FieldDescriptor other) { 092 if (other.field != null) { 093 field = other.field; 094 } 095 if (other.type != null) { 096 type = other.type; 097 } 098 if (other.table != null) { 099 table = other.table; 100 } 101 if (other.column != null) { 102 column = other.column; 103 } 104 } 105 106 @XNode("@type") 107 public String type; 108 109 public String field; 110 111 @XNode("@name") 112 public void setName(String name) { 113 if (!StringUtils.isBlank(name) && field == null) { 114 field = name; 115 } 116 } 117 118 // compat with older syntax 119 @XNode 120 public void setXNodeContent(String name) { 121 setName(name); 122 } 123 124 @XNode("@table") 125 public String table; 126 127 @XNode("@column") 128 public String column; 129 130 @Override 131 public String toString() { 132 return this.getClass().getSimpleName() + '(' + field + ",type=" + type + ",table=" + table + ",column=" 133 + column + ")"; 134 } 135 } 136 137 /** False if the boolean is null or FALSE, true otherwise. */ 138 private static boolean defaultFalse(Boolean bool) { 139 return Boolean.TRUE.equals(bool); 140 } 141 142 /** True if the boolean is null or TRUE, false otherwise. */ 143 private static boolean defaultTrue(Boolean bool) { 144 return !Boolean.FALSE.equals(bool); 145 } 146 147 public String name; 148 149 @XNode("@name") 150 public void setName(String name) { 151 this.name = name; 152 } 153 154 @XNode("@label") 155 public String label; 156 157 @XNode("@isDefault") 158 private Boolean isDefault; 159 160 public Boolean isDefault() { 161 return isDefault; 162 } 163 164 @XNode("@headless") 165 private Boolean headless; 166 167 /** @since 11.2 */ 168 public Boolean isHeadless() { 169 return headless; 170 } 171 172 // compat, when used with old-style extension point syntax 173 // and nested repository 174 @XNode("repository") 175 public RepositoryDescriptor repositoryDescriptor; 176 177 @XNode("pool") 178 public PoolConfiguration pool; 179 180 @XNode("clusterInvalidatorClass") 181 public Class<? extends VCSClusterInvalidator> clusterInvalidatorClass; 182 183 @XNode("cachingMapper@class") 184 public Class<? extends CachingMapper> cachingMapperClass; 185 186 @XNode("cachingMapper@enabled") 187 private Boolean cachingMapperEnabled; 188 189 public boolean getCachingMapperEnabled() { 190 return defaultTrue(cachingMapperEnabled); 191 } 192 193 @XNodeMap(value = "cachingMapper/property", key = "@name", type = HashMap.class, componentType = String.class) 194 public Map<String, String> cachingMapperProperties = new HashMap<>(); 195 196 @XNode("ddlMode") 197 private String ddlMode; 198 199 public String getDDLMode() { 200 return ddlMode; 201 } 202 203 @XNode("noDDL") 204 private Boolean noDDL; 205 206 public boolean getNoDDL() { 207 return defaultFalse(noDDL); 208 } 209 210 @XNodeList(value = "sqlInitFile", type = ArrayList.class, componentType = String.class) 211 public List<String> sqlInitFiles = new ArrayList<>(0); 212 213 @XNode("softDelete@enabled") 214 private Boolean softDeleteEnabled; 215 216 public boolean getSoftDeleteEnabled() { 217 return defaultFalse(softDeleteEnabled); 218 } 219 220 protected void setSoftDeleteEnabled(boolean enabled) { 221 softDeleteEnabled = Boolean.valueOf(enabled); 222 } 223 224 @XNode("proxies@enabled") 225 private Boolean proxiesEnabled; 226 227 public boolean getProxiesEnabled() { 228 return defaultTrue(proxiesEnabled); 229 } 230 231 protected void setProxiesEnabled(boolean enabled) { 232 proxiesEnabled = Boolean.valueOf(enabled); 233 } 234 235 @XNode("idType") 236 public String idType; // "varchar", "uuid", "sequence" 237 238 @XNodeList(value = "schema/field", type = ArrayList.class, componentType = FieldDescriptor.class) 239 public List<FieldDescriptor> schemaFields = new ArrayList<>(0); 240 241 @XNode("schema/arrayColumns") 242 private Boolean arrayColumns; 243 244 public boolean getArrayColumns() { 245 return defaultFalse(arrayColumns); 246 } 247 248 public void setArrayColumns(boolean enabled) { 249 arrayColumns = Boolean.valueOf(enabled); 250 } 251 252 @XNode("childNameUniqueConstraintEnabled") 253 private Boolean childNameUniqueConstraintEnabled; 254 255 public boolean getChildNameUniqueConstraintEnabled() { 256 return defaultTrue(childNameUniqueConstraintEnabled); 257 } 258 259 @XNode("collectionUniqueConstraintEnabled") 260 private Boolean collectionUniqueConstraintEnabled; 261 262 public boolean getCollectionUniqueConstraintEnabled() { 263 return defaultTrue(collectionUniqueConstraintEnabled); 264 } 265 266 @XNode("indexing/queryMaker@class") 267 public void setQueryMakerDeprecated(String klass) { 268 log.warn("Setting queryMaker from repository configuration is now deprecated"); 269 } 270 271 // VCS-specific fulltext indexing options 272 private String fulltextAnalyzer; 273 274 public String getFulltextAnalyzer() { 275 return fulltextAnalyzer; 276 } 277 278 @XNode("indexing/fulltext@analyzer") 279 public void setFulltextAnalyzer(String fulltextAnalyzer) { 280 this.fulltextAnalyzer = fulltextAnalyzer; 281 } 282 283 private String fulltextCatalog; 284 285 public String getFulltextCatalog() { 286 return fulltextCatalog; 287 } 288 289 @XNode("indexing/fulltext@catalog") 290 public void setFulltextCatalog(String fulltextCatalog) { 291 this.fulltextCatalog = fulltextCatalog; 292 } 293 294 private FulltextDescriptor fulltextDescriptor = new FulltextDescriptor(); 295 296 public FulltextDescriptor getFulltextDescriptor() { 297 return fulltextDescriptor; 298 } 299 300 @XNode("indexing/fulltext@fieldSizeLimit") 301 public void setFulltextFieldSizeLimit(int fieldSizeLimit) { 302 fulltextDescriptor.setFulltextFieldSizeLimit(fieldSizeLimit); 303 } 304 305 @XNode("indexing/fulltext@disabled") 306 public void setFulltextDisabled(boolean disabled) { 307 fulltextDescriptor.setFulltextDisabled(disabled); 308 } 309 310 /** @since 11.1 */ 311 @XNode("indexing/fulltext@storedInBlob") 312 public void setFulltextStoredInBlob(boolean storedInBlob) { 313 fulltextDescriptor.setFulltextStoredInBlob(storedInBlob); 314 } 315 316 @XNode("indexing/fulltext@searchDisabled") 317 public void setFulltextSearchDisabled(boolean disabled) { 318 fulltextDescriptor.setFulltextSearchDisabled(disabled); 319 } 320 321 @XNodeList(value = "indexing/fulltext/index", type = ArrayList.class, componentType = FulltextIndexDescriptor.class) 322 public void setFulltextIndexes(List<FulltextIndexDescriptor> fulltextIndexes) { 323 fulltextDescriptor.setFulltextIndexes(fulltextIndexes); 324 } 325 326 @XNodeList(value = "indexing/excludedTypes/type", type = HashSet.class, componentType = String.class) 327 public void setFulltextExcludedTypes(Set<String> fulltextExcludedTypes) { 328 fulltextDescriptor.setFulltextExcludedTypes(fulltextExcludedTypes); 329 } 330 331 @XNodeList(value = "indexing/includedTypes/type", type = HashSet.class, componentType = String.class) 332 public void setFulltextIncludedTypes(Set<String> fulltextIncludedTypes) { 333 fulltextDescriptor.setFulltextIncludedTypes(fulltextIncludedTypes); 334 } 335 336 // compat 337 @XNodeList(value = "indexing/neverPerDocumentFacets/facet", type = HashSet.class, componentType = String.class) 338 public Set<String> neverPerInstanceMixins = new HashSet<>(0); 339 340 @XNode("pathOptimizations@enabled") 341 private Boolean pathOptimizationsEnabled; 342 343 public boolean getPathOptimizationsEnabled() { 344 return defaultTrue(pathOptimizationsEnabled); 345 } 346 347 protected void setPathOptimizationsEnabled(boolean enabled) { 348 pathOptimizationsEnabled = Boolean.valueOf(enabled); 349 } 350 351 /* @since 5.7 */ 352 @XNode("pathOptimizations@version") 353 private Integer pathOptimizationsVersion; 354 355 public int getPathOptimizationsVersion() { 356 return pathOptimizationsVersion == null ? DEFAULT_PATH_OPTIM_VERSION : pathOptimizationsVersion.intValue(); 357 } 358 359 @XNode("aclOptimizations@enabled") 360 private Boolean aclOptimizationsEnabled; 361 362 public boolean getAclOptimizationsEnabled() { 363 return defaultTrue(aclOptimizationsEnabled); 364 } 365 366 protected void setAclOptimizationsEnabled(boolean enabled) { 367 aclOptimizationsEnabled = Boolean.valueOf(enabled); 368 } 369 370 /* @since 5.4.2 */ 371 @XNode("aclOptimizations@readAclMaxSize") 372 private Integer readAclMaxSize; 373 374 public int getReadAclMaxSize() { 375 return readAclMaxSize == null ? 0 : readAclMaxSize.intValue(); 376 } 377 378 @XNode("usersSeparator@key") 379 public String usersSeparatorKey; 380 381 /** @since 9.1 */ 382 @XNode("changeTokenEnabled") 383 private Boolean changeTokenEnabled; 384 385 /** @since 9.1 */ 386 public boolean isChangeTokenEnabled() { 387 return defaultFalse(changeTokenEnabled); 388 } 389 390 /** @since 9.1 */ 391 public void setChangeTokenEnabled(boolean enabled) { 392 this.changeTokenEnabled = Boolean.valueOf(enabled); 393 } 394 395 public RepositoryDescriptor() { 396 } 397 398 /** Copy constructor. */ 399 public RepositoryDescriptor(RepositoryDescriptor other) { 400 name = other.name; 401 label = other.label; 402 isDefault = other.isDefault; 403 headless = other.headless; 404 pool = other.pool == null ? null : new PoolConfiguration(other.pool); 405 clusterInvalidatorClass = other.clusterInvalidatorClass; 406 cachingMapperClass = other.cachingMapperClass; 407 cachingMapperEnabled = other.cachingMapperEnabled; 408 cachingMapperProperties = new HashMap<>(other.cachingMapperProperties); 409 noDDL = other.noDDL; 410 ddlMode = other.ddlMode; 411 sqlInitFiles = new ArrayList<>(other.sqlInitFiles); 412 softDeleteEnabled = other.softDeleteEnabled; 413 proxiesEnabled = other.proxiesEnabled; 414 schemaFields = FieldDescriptor.copyList(other.schemaFields); 415 arrayColumns = other.arrayColumns; 416 childNameUniqueConstraintEnabled = other.childNameUniqueConstraintEnabled; 417 collectionUniqueConstraintEnabled = other.collectionUniqueConstraintEnabled; 418 idType = other.idType; 419 fulltextAnalyzer = other.fulltextAnalyzer; 420 fulltextCatalog = other.fulltextCatalog; 421 fulltextDescriptor = new FulltextDescriptor(other.fulltextDescriptor); 422 neverPerInstanceMixins = other.neverPerInstanceMixins; 423 pathOptimizationsEnabled = other.pathOptimizationsEnabled; 424 pathOptimizationsVersion = other.pathOptimizationsVersion; 425 aclOptimizationsEnabled = other.aclOptimizationsEnabled; 426 readAclMaxSize = other.readAclMaxSize; 427 usersSeparatorKey = other.usersSeparatorKey; 428 changeTokenEnabled = other.changeTokenEnabled; 429 } 430 431 public void merge(RepositoryDescriptor other) { 432 if (other.name != null) { 433 name = other.name; 434 } 435 if (other.label != null) { 436 label = other.label; 437 } 438 if (other.isDefault != null) { 439 isDefault = other.isDefault; 440 } 441 if (other.headless != null) { 442 headless = other.headless; 443 } 444 if (other.pool != null) { 445 if (pool == null) { 446 pool = new PoolConfiguration(other.pool); 447 } else { 448 pool.merge(other.pool); 449 } 450 } 451 if (other.clusterInvalidatorClass != null) { 452 clusterInvalidatorClass = other.clusterInvalidatorClass; 453 } 454 if (other.cachingMapperClass != null) { 455 cachingMapperClass = other.cachingMapperClass; 456 } 457 if (other.cachingMapperEnabled != null) { 458 cachingMapperEnabled = other.cachingMapperEnabled; 459 } 460 cachingMapperProperties.putAll(other.cachingMapperProperties); 461 if (other.noDDL != null) { 462 noDDL = other.noDDL; 463 } 464 if (other.ddlMode != null) { 465 ddlMode = other.ddlMode; 466 } 467 sqlInitFiles.addAll(other.sqlInitFiles); 468 if (other.softDeleteEnabled != null) { 469 softDeleteEnabled = other.softDeleteEnabled; 470 } 471 if (other.proxiesEnabled != null) { 472 proxiesEnabled = other.proxiesEnabled; 473 } 474 if (other.idType != null) { 475 idType = other.idType; 476 } 477 for (FieldDescriptor of : other.schemaFields) { 478 boolean append = true; 479 for (FieldDescriptor f : schemaFields) { 480 if (f.field.equals(of.field)) { 481 f.merge(of); 482 append = false; 483 break; 484 } 485 } 486 if (append) { 487 schemaFields.add(of); 488 } 489 } 490 if (other.arrayColumns != null) { 491 arrayColumns = other.arrayColumns; 492 } 493 if (other.childNameUniqueConstraintEnabled != null) { 494 childNameUniqueConstraintEnabled = other.childNameUniqueConstraintEnabled; 495 } 496 if (other.collectionUniqueConstraintEnabled != null) { 497 collectionUniqueConstraintEnabled = other.collectionUniqueConstraintEnabled; 498 } 499 if (other.fulltextAnalyzer != null) { 500 fulltextAnalyzer = other.fulltextAnalyzer; 501 } 502 if (other.fulltextCatalog != null) { 503 fulltextCatalog = other.fulltextCatalog; 504 } 505 fulltextDescriptor.merge(other.fulltextDescriptor); 506 neverPerInstanceMixins.addAll(other.neverPerInstanceMixins); 507 if (other.pathOptimizationsEnabled != null) { 508 pathOptimizationsEnabled = other.pathOptimizationsEnabled; 509 } 510 if (other.pathOptimizationsVersion != null) { 511 pathOptimizationsVersion = other.pathOptimizationsVersion; 512 } 513 if (other.aclOptimizationsEnabled != null) { 514 aclOptimizationsEnabled = other.aclOptimizationsEnabled; 515 } 516 if (other.readAclMaxSize != null) { 517 readAclMaxSize = other.readAclMaxSize; 518 } 519 if (other.usersSeparatorKey != null) { 520 usersSeparatorKey = other.usersSeparatorKey; 521 } 522 if (other.changeTokenEnabled != null) { 523 changeTokenEnabled = other.changeTokenEnabled; 524 } 525 } 526 527}