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") 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 pool.setName("repository/" + name); 151 } 152 153 @XNode("@label") 154 public String label; 155 156 @XNode("@isDefault") 157 private Boolean isDefault; 158 159 public Boolean isDefault() { 160 return isDefault; 161 } 162 163 // compat, when used with old-style extension point syntax 164 // and nested repository 165 @XNode("repository") 166 public RepositoryDescriptor repositoryDescriptor; 167 168 public NuxeoConnectionManagerConfiguration pool = new NuxeoConnectionManagerConfiguration(); 169 170 @XNode("pool") 171 public void setPool(NuxeoConnectionManagerConfiguration pool) { 172 pool.setName("repository/" + name); 173 this.pool = pool; 174 } 175 176 @XNode("backendClass") 177 public Class<? extends RepositoryBackend> backendClass; 178 179 @XNode("clusterInvalidatorClass") 180 public Class<? extends ClusterInvalidator> clusterInvalidatorClass; 181 182 @XNode("cachingMapper@class") 183 public Class<? extends CachingMapper> cachingMapperClass; 184 185 @XNode("cachingMapper@enabled") 186 private Boolean cachingMapperEnabled; 187 188 public boolean getCachingMapperEnabled() { 189 return defaultTrue(cachingMapperEnabled); 190 } 191 192 @XNodeMap(value = "cachingMapper/property", key = "@name", type = HashMap.class, componentType = String.class) 193 public Map<String, String> cachingMapperProperties = new HashMap<>(); 194 195 @XNode("ddlMode") 196 private String ddlMode; 197 198 public String getDDLMode() { 199 return ddlMode; 200 } 201 202 @XNode("noDDL") 203 private Boolean noDDL; 204 205 public boolean getNoDDL() { 206 return defaultFalse(noDDL); 207 } 208 209 @XNodeList(value = "sqlInitFile", type = ArrayList.class, componentType = String.class) 210 public List<String> sqlInitFiles = new ArrayList<>(0); 211 212 @XNode("softDelete@enabled") 213 private Boolean softDeleteEnabled; 214 215 public boolean getSoftDeleteEnabled() { 216 return defaultFalse(softDeleteEnabled); 217 } 218 219 protected void setSoftDeleteEnabled(boolean enabled) { 220 softDeleteEnabled = Boolean.valueOf(enabled); 221 } 222 223 @XNode("proxies@enabled") 224 private Boolean proxiesEnabled; 225 226 public boolean getProxiesEnabled() { 227 return defaultTrue(proxiesEnabled); 228 } 229 230 protected void setProxiesEnabled(boolean enabled) { 231 proxiesEnabled = Boolean.valueOf(enabled); 232 } 233 234 @XNode("idType") 235 public String idType; // "varchar", "uuid", "sequence" 236 237 @XNode("clustering@id") 238 private String clusterNodeId; 239 240 public String getClusterNodeId() { 241 return clusterNodeId; 242 } 243 244 @XNode("clustering@enabled") 245 private Boolean clusteringEnabled; 246 247 public boolean getClusteringEnabled() { 248 return defaultFalse(clusteringEnabled); 249 } 250 251 protected void setClusteringEnabled(boolean enabled) { 252 clusteringEnabled = Boolean.valueOf(enabled); 253 } 254 255 @XNode("clustering@delay") 256 private Long clusteringDelay; 257 258 public long getClusteringDelay() { 259 return clusteringDelay == null ? 0 : clusteringDelay.longValue(); 260 } 261 262 protected void setClusteringDelay(long delay) { 263 clusteringDelay = Long.valueOf(delay); 264 } 265 266 @XNodeList(value = "schema/field", type = ArrayList.class, componentType = FieldDescriptor.class) 267 public List<FieldDescriptor> schemaFields = new ArrayList<>(0); 268 269 @XNode("schema/arrayColumns") 270 private Boolean arrayColumns; 271 272 public boolean getArrayColumns() { 273 return defaultFalse(arrayColumns); 274 } 275 276 public void setArrayColumns(boolean enabled) { 277 arrayColumns = Boolean.valueOf(enabled); 278 } 279 280 @XNode("indexing/queryMaker@class") 281 public void setQueryMakerDeprecated(String klass) { 282 log.warn("Setting queryMaker from repository configuration is now deprecated"); 283 } 284 285 // VCS-specific fulltext indexing options 286 private String fulltextAnalyzer; 287 288 public String getFulltextAnalyzer() { 289 return fulltextAnalyzer; 290 } 291 292 @XNode("indexing/fulltext@analyzer") 293 public void setFulltextAnalyzer(String fulltextAnalyzer) { 294 this.fulltextAnalyzer = fulltextAnalyzer; 295 } 296 297 private String fulltextCatalog; 298 299 public String getFulltextCatalog() { 300 return fulltextCatalog; 301 } 302 303 @XNode("indexing/fulltext@catalog") 304 public void setFulltextCatalog(String fulltextCatalog) { 305 this.fulltextCatalog = fulltextCatalog; 306 } 307 308 private FulltextDescriptor fulltextDescriptor = new FulltextDescriptor(); 309 310 public FulltextDescriptor getFulltextDescriptor() { 311 return fulltextDescriptor; 312 } 313 314 @XNode("indexing/fulltext@disabled") 315 public void setFulltextDisabled(boolean disabled) { 316 fulltextDescriptor.setFulltextDisabled(disabled); 317 } 318 319 @XNode("indexing/fulltext@searchDisabled") 320 public void setFulltextSearchDisabled(boolean disabled) { 321 fulltextDescriptor.setFulltextSearchDisabled(disabled); 322 } 323 324 @XNode("indexing/fulltext@parser") 325 public void setFulltextParser(String fulltextParser) { 326 fulltextDescriptor.setFulltextParser(fulltextParser); 327 } 328 329 @XNodeList(value = "indexing/fulltext/index", type = ArrayList.class, componentType = FulltextIndexDescriptor.class) 330 public void setFulltextIndexes(List<FulltextIndexDescriptor> fulltextIndexes) { 331 fulltextDescriptor.setFulltextIndexes(fulltextIndexes); 332 } 333 334 @XNodeList(value = "indexing/excludedTypes/type", type = HashSet.class, componentType = String.class) 335 public void setFulltextExcludedTypes(Set<String> fulltextExcludedTypes) { 336 fulltextDescriptor.setFulltextExcludedTypes(fulltextExcludedTypes); 337 } 338 339 @XNodeList(value = "indexing/includedTypes/type", type = HashSet.class, componentType = String.class) 340 public void setFulltextIncludedTypes(Set<String> fulltextIncludedTypes) { 341 fulltextDescriptor.setFulltextIncludedTypes(fulltextIncludedTypes); 342 } 343 344 // compat 345 @XNodeList(value = "indexing/neverPerDocumentFacets/facet", type = HashSet.class, componentType = String.class) 346 public Set<String> neverPerInstanceMixins = new HashSet<>(0); 347 348 @XNode("pathOptimizations@enabled") 349 private Boolean pathOptimizationsEnabled; 350 351 public boolean getPathOptimizationsEnabled() { 352 return defaultTrue(pathOptimizationsEnabled); 353 } 354 355 protected void setPathOptimizationsEnabled(boolean enabled) { 356 pathOptimizationsEnabled = Boolean.valueOf(enabled); 357 } 358 359 /* @since 5.7 */ 360 @XNode("pathOptimizations@version") 361 private Integer pathOptimizationsVersion; 362 363 public int getPathOptimizationsVersion() { 364 return pathOptimizationsVersion == null ? DEFAULT_PATH_OPTIM_VERSION : pathOptimizationsVersion.intValue(); 365 } 366 367 @XNode("aclOptimizations@enabled") 368 private Boolean aclOptimizationsEnabled; 369 370 public boolean getAclOptimizationsEnabled() { 371 return defaultTrue(aclOptimizationsEnabled); 372 } 373 374 protected void setAclOptimizationsEnabled(boolean enabled) { 375 aclOptimizationsEnabled = Boolean.valueOf(enabled); 376 } 377 378 /* @since 5.4.2 */ 379 @XNode("aclOptimizations@readAclMaxSize") 380 private Integer readAclMaxSize; 381 382 public int getReadAclMaxSize() { 383 return readAclMaxSize == null ? DEFAULT_READ_ACL_MAX_SIZE : readAclMaxSize.intValue(); 384 } 385 386 @XNode("usersSeparator@key") 387 public String usersSeparatorKey; 388 389 @XNode("xa-datasource") 390 public String xaDataSourceName; 391 392 @XNodeMap(value = "property", key = "@name", type = HashMap.class, componentType = String.class) 393 public Map<String, String> properties = new HashMap<>(); 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 pool = other.pool == null ? null : new NuxeoConnectionManagerConfiguration(other.pool); 404 backendClass = other.backendClass; 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 idType = other.idType; 417 clusterNodeId = other.clusterNodeId; 418 clusteringEnabled = other.clusteringEnabled; 419 clusteringDelay = other.clusteringDelay; 420 fulltextAnalyzer = other.fulltextAnalyzer; 421 fulltextCatalog = other.fulltextCatalog; 422 fulltextDescriptor = new FulltextDescriptor(other.fulltextDescriptor); 423 neverPerInstanceMixins = other.neverPerInstanceMixins; 424 pathOptimizationsEnabled = other.pathOptimizationsEnabled; 425 pathOptimizationsVersion = other.pathOptimizationsVersion; 426 aclOptimizationsEnabled = other.aclOptimizationsEnabled; 427 readAclMaxSize = other.readAclMaxSize; 428 usersSeparatorKey = other.usersSeparatorKey; 429 xaDataSourceName = other.xaDataSourceName; 430 properties = new HashMap<>(other.properties); 431 } 432 433 public void merge(RepositoryDescriptor other) { 434 if (other.name != null) { 435 name = other.name; 436 } 437 if (other.label != null) { 438 label = other.label; 439 } 440 if (other.isDefault != null) { 441 isDefault = other.isDefault; 442 } 443 if (other.pool != null) { 444 pool = new NuxeoConnectionManagerConfiguration(other.pool); 445 } 446 if (other.backendClass != null) { 447 backendClass = other.backendClass; 448 } 449 if (other.clusterInvalidatorClass != null) { 450 clusterInvalidatorClass = other.clusterInvalidatorClass; 451 } 452 if (other.cachingMapperClass != null) { 453 cachingMapperClass = other.cachingMapperClass; 454 } 455 if (other.cachingMapperEnabled != null) { 456 cachingMapperEnabled = other.cachingMapperEnabled; 457 } 458 cachingMapperProperties.putAll(other.cachingMapperProperties); 459 if (other.noDDL != null) { 460 noDDL = other.noDDL; 461 } 462 if (other.ddlMode != null) { 463 ddlMode = other.ddlMode; 464 } 465 sqlInitFiles.addAll(other.sqlInitFiles); 466 if (other.softDeleteEnabled != null) { 467 softDeleteEnabled = other.softDeleteEnabled; 468 } 469 if (other.proxiesEnabled != null) { 470 proxiesEnabled = other.proxiesEnabled; 471 } 472 if (other.idType != null) { 473 idType = other.idType; 474 } 475 if (other.clusterNodeId != null) { 476 clusterNodeId = other.clusterNodeId; 477 } 478 if (other.clusteringEnabled != null) { 479 clusteringEnabled = other.clusteringEnabled; 480 } 481 if (other.clusteringDelay != null) { 482 clusteringDelay = other.clusteringDelay; 483 } 484 for (FieldDescriptor of : other.schemaFields) { 485 boolean append = true; 486 for (FieldDescriptor f : schemaFields) { 487 if (f.field.equals(of.field)) { 488 f.merge(of); 489 append = false; 490 break; 491 } 492 } 493 if (append) { 494 schemaFields.add(of); 495 } 496 } 497 if (other.arrayColumns != null) { 498 arrayColumns = other.arrayColumns; 499 } 500 if (other.fulltextAnalyzer != null) { 501 fulltextAnalyzer = other.fulltextAnalyzer; 502 } 503 if (other.fulltextCatalog != null) { 504 fulltextCatalog = other.fulltextCatalog; 505 } 506 fulltextDescriptor.merge(other.fulltextDescriptor); 507 neverPerInstanceMixins.addAll(other.neverPerInstanceMixins); 508 if (other.pathOptimizationsEnabled != null) { 509 pathOptimizationsEnabled = other.pathOptimizationsEnabled; 510 } 511 if (other.pathOptimizationsVersion != null) { 512 pathOptimizationsVersion = other.pathOptimizationsVersion; 513 } 514 if (other.aclOptimizationsEnabled != null) { 515 aclOptimizationsEnabled = other.aclOptimizationsEnabled; 516 } 517 if (other.readAclMaxSize != null) { 518 readAclMaxSize = other.readAclMaxSize; 519 } 520 if (other.usersSeparatorKey != null) { 521 usersSeparatorKey = other.usersSeparatorKey; 522 } 523 if (other.xaDataSourceName != null) { 524 xaDataSourceName = other.xaDataSourceName; 525 } 526 properties.putAll(other.properties); 527 } 528 529}