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