/* * Copyright (c) 2006-2014 Nuxeo SA (http://nuxeo.com/) and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Florent Guillaume */ package org.nuxeo.ecm.core.storage.sql; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.ObjectUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.nuxeo.common.xmap.annotation.XNode; import org.nuxeo.common.xmap.annotation.XNodeList; import org.nuxeo.common.xmap.annotation.XNodeMap; import org.nuxeo.common.xmap.annotation.XObject; import org.nuxeo.ecm.core.repository.RepositoryFactory; import org.nuxeo.ecm.core.storage.binary.BinaryManager; import org.nuxeo.runtime.jtajca.NuxeoConnectionManagerConfiguration; /** * Low-level VCS Repository Descriptor. */ @XObject(value = "repository") public class RepositoryDescriptor { private static final Log log = LogFactory.getLog(RepositoryDescriptor.class); public static final int DEFAULT_READ_ACL_MAX_SIZE = 4096; public static final int DEFAULT_PATH_OPTIM_VERSION = 2; @XObject(value = "index") public static class FulltextIndexDescriptor { @XNode("@name") public String name; @XNode("@analyzer") public String analyzer; @XNode("@catalog") public String catalog; /** string or blob */ @XNode("fieldType") public String fieldType; @XNodeList(value = "field", type = HashSet.class, componentType = String.class) public Set<String> fields = new HashSet<String>(0); @XNodeList(value = "excludeField", type = HashSet.class, componentType = String.class) public Set<String> excludeFields = new HashSet<String>(0); public FulltextIndexDescriptor() { } /** Copy constructor. */ public FulltextIndexDescriptor(FulltextIndexDescriptor other) { name = other.name; analyzer = other.analyzer; catalog = other.catalog; fieldType = other.fieldType; fields = new HashSet<String>(other.fields); excludeFields = new HashSet<String>(other.excludeFields); } public static List<FulltextIndexDescriptor> copyList( List<FulltextIndexDescriptor> other) { List<FulltextIndexDescriptor> copy = new ArrayList<FulltextIndexDescriptor>(other.size()); for (FulltextIndexDescriptor fid : other) { copy.add(new FulltextIndexDescriptor(fid)); } return copy; } public void merge(FulltextIndexDescriptor other) { if (other.name != null) { name = other.name; } if (other.analyzer != null) { analyzer = other.analyzer; } if (other.catalog != null) { catalog = other.catalog; } if (other.fieldType != null) { fieldType = other.fieldType; } fields.addAll(other.fields); excludeFields.addAll(other.excludeFields); } } @XObject(value = "field") public static class FieldDescriptor { // empty constructor needed by XMap public FieldDescriptor() { } /** Copy constructor. */ public FieldDescriptor(FieldDescriptor other) { type = other.type; field = other.field; table = other.table; column = other.column; } public static List<FieldDescriptor> copyList( List<FieldDescriptor> other) { List<FieldDescriptor> copy = new ArrayList<FieldDescriptor>(other.size()); for (FieldDescriptor fd : other) { copy.add(new FieldDescriptor(fd)); } return copy; } public void merge(FieldDescriptor other) { if (other.field != null) { field = other.field; } if (other.type != null) { type = other.type; } if (other.table != null) { table = other.table; } if (other.column != null) { column = other.column; } } @XNode("@type") public String type; public String field; @XNode("@name") public void setName(String name) { if (!StringUtils.isBlank(name) && field == null) { field = name; } } // compat with older syntax @XNode public void setXNodeContent(String name) { setName(name); } @XNode("@table") public String table; @XNode("@column") public String column; @Override public String toString() { return this.getClass().getSimpleName() + '(' + field + ",type=" + type + ",table=" + table + ",column=" + column + ")"; } } /** False if the boolean is null or FALSE, true otherwise. */ private static boolean defaultFalse(Boolean bool) { return Boolean.TRUE.equals(bool); } /** True if the boolean is null or TRUE, false otherwise. */ private static boolean defaultTrue(Boolean bool) { return !Boolean.FALSE.equals(bool); } public String name; @XNode("@name") public void setName(String name) { this.name = name; pool.setName("repository/"+name); } @XNode("@label") public String label; @XNode("@isDefault") private Boolean isDefault; public Boolean isDefault() { return isDefault; } @XNode("@factory") private Class<? extends RepositoryFactory> repositoryFactoryClass; public Class<? extends RepositoryFactory> getRepositoryFactoryClass() { return repositoryFactoryClass; } public void setRepositoryFactoryClass(Class<? extends RepositoryFactory> klass) { repositoryFactoryClass = klass; } // compat, when used with old-style extension point syntax // and nested repository @XNode("repository") public RepositoryDescriptor repositoryDescriptor; public NuxeoConnectionManagerConfiguration pool = new NuxeoConnectionManagerConfiguration(); @XNode("pool") public void setPool(NuxeoConnectionManagerConfiguration pool) { pool.setName("repository/"+name); this.pool = pool; } @XNode("backendClass") public Class<? extends RepositoryBackend> backendClass; @XNode("cachingMapper@class") public Class<? extends CachingMapper> cachingMapperClass; @XNode("cachingMapper@enabled") private Boolean cachingMapperEnabled; public boolean getCachingMapperEnabled() { return defaultTrue(cachingMapperEnabled); } @XNodeMap(value = "cachingMapper/property", key = "@name", type = HashMap.class, componentType = String.class) public Map<String, String> cachingMapperProperties = new HashMap<String, String>(); @XNode("noDDL") private Boolean noDDL; public boolean getNoDDL() { return defaultFalse(noDDL); } @XNodeList(value = "sqlInitFile", type = ArrayList.class, componentType = String.class) public List<String> sqlInitFiles = new ArrayList<String>(0); @XNode("softDelete@enabled") private Boolean softDeleteEnabled; public boolean getSoftDeleteEnabled() { return defaultFalse(softDeleteEnabled); } protected void setSoftDeleteEnabled(boolean enabled) { softDeleteEnabled = Boolean.valueOf(enabled); } @XNode("proxies@enabled") private Boolean proxiesEnabled; public boolean getProxiesEnabled() { return defaultTrue(proxiesEnabled); } protected void setProxiesEnabled(boolean enabled) { proxiesEnabled = Boolean.valueOf(enabled); } @XNode("idType") public String idType; // "varchar", "uuid", "sequence" @XNode("clustering@enabled") private Boolean clusteringEnabled; public boolean getClusteringEnabled() { return defaultFalse(clusteringEnabled); } protected void setClusteringEnabled(boolean enabled) { clusteringEnabled = Boolean.valueOf(enabled); } @XNode("clustering@delay") private Long clusteringDelay; public long getClusteringDelay() { return clusteringDelay == null ? 0 : clusteringDelay.longValue(); } protected void setClusteringDelay(long delay) { clusteringDelay = Long.valueOf(delay); } @XNodeList(value = "schema/field", type = ArrayList.class, componentType = FieldDescriptor.class) public List<FieldDescriptor> schemaFields = new ArrayList<FieldDescriptor>(0); @XNode("schema/arrayColumns") private Boolean arrayColumns; public boolean getArrayColumns() { return defaultFalse(arrayColumns); } public void setArrayColumns(boolean enabled) { arrayColumns = Boolean.valueOf(enabled); } @XNode("indexing/fulltext@disabled") private Boolean fulltextDisabled; public boolean getFulltextDisabled() { return defaultFalse(fulltextDisabled); } public void setFulltextDisabled(boolean disabled) { fulltextDisabled = Boolean.valueOf(disabled); } @XNode("indexing/fulltext@analyzer") public String fulltextAnalyzer; @XNode("indexing/fulltext@parser") public String fulltextParser; @XNode("indexing/fulltext@catalog") public String fulltextCatalog; @XNode("indexing/queryMaker@class") public void setQueryMakerDeprecated(String klass) { log.warn("Setting queryMaker from repository configuration is now deprecated"); } @XNodeList(value = "indexing/fulltext/index", type = ArrayList.class, componentType = FulltextIndexDescriptor.class) public List<FulltextIndexDescriptor> fulltextIndexes = new ArrayList<FulltextIndexDescriptor>(0); @XNodeList(value = "indexing/excludedTypes/type", type = HashSet.class, componentType = String.class) public Set<String> fulltextExcludedTypes = new HashSet<String>(0); @XNodeList(value = "indexing/includedTypes/type", type = HashSet.class, componentType = String.class) public Set<String> fulltextIncludedTypes = new HashSet<String>(0); // compat @XNodeList(value = "indexing/neverPerDocumentFacets/facet", type = HashSet.class, componentType = String.class) public Set<String> neverPerInstanceMixins = new HashSet<String>(0); @XNode("pathOptimizations@enabled") private Boolean pathOptimizationsEnabled; public boolean getPathOptimizationsEnabled() { return defaultTrue(pathOptimizationsEnabled); } protected void setPathOptimizationsEnabled(boolean enabled) { pathOptimizationsEnabled = Boolean.valueOf(enabled); } /* @since 5.7 */ @XNode("pathOptimizations@version") private Integer pathOptimizationsVersion; public int getPathOptimizationsVersion() { return pathOptimizationsVersion == null ? DEFAULT_PATH_OPTIM_VERSION : pathOptimizationsVersion.intValue(); } @XNode("aclOptimizations@enabled") private Boolean aclOptimizationsEnabled; public boolean getAclOptimizationsEnabled() { return defaultTrue(aclOptimizationsEnabled); } protected void setAclOptimizationsEnabled(boolean enabled) { aclOptimizationsEnabled = Boolean.valueOf(enabled); } /* @since 5.4.2 */ @XNode("aclOptimizations@readAclMaxSize") private Integer readAclMaxSize; public int getReadAclMaxSize() { return readAclMaxSize == null ? DEFAULT_READ_ACL_MAX_SIZE : readAclMaxSize.intValue(); } @XNode("binaryManager@class") public Class<? extends BinaryManager> binaryManagerClass; @XNode("binaryManager@key") public String binaryManagerKey; @XNode("binaryStore@path") public String binaryStorePath; @XNode("@sendInvalidationEvents") public boolean sendInvalidationEvents; @XNode("usersSeparator@key") public String usersSeparatorKey; @XNode("xa-datasource") public String xaDataSourceName; @XNodeMap(value = "property", key = "@name", type = HashMap.class, componentType = String.class) public Map<String, String> properties = new HashMap<String, String>(); public RepositoryDescriptor() { } /** Copy constructor. */ public RepositoryDescriptor(RepositoryDescriptor other) { name = other.name; label = other.label; isDefault = other.isDefault; repositoryFactoryClass = other.repositoryFactoryClass; pool = other.pool == null ? null : new NuxeoConnectionManagerConfiguration(other.pool); backendClass = other.backendClass; cachingMapperClass = other.cachingMapperClass; cachingMapperEnabled = other.cachingMapperEnabled; cachingMapperProperties = new HashMap<String, String>(other.cachingMapperProperties); noDDL = other.noDDL; sqlInitFiles = new ArrayList<String>(other.sqlInitFiles); softDeleteEnabled = other.softDeleteEnabled; proxiesEnabled = other.proxiesEnabled; schemaFields = FieldDescriptor.copyList(other.schemaFields); arrayColumns = other.arrayColumns; idType = other.idType; clusteringEnabled = other.clusteringEnabled; clusteringDelay = other.clusteringDelay; fulltextDisabled = other.fulltextDisabled; fulltextAnalyzer = other.fulltextAnalyzer; fulltextParser = other.fulltextParser; fulltextCatalog = other.fulltextCatalog; fulltextIndexes = FulltextIndexDescriptor.copyList(other.fulltextIndexes); fulltextExcludedTypes = new HashSet<String>(other.fulltextExcludedTypes); fulltextIncludedTypes = new HashSet<String>(other.fulltextIncludedTypes); neverPerInstanceMixins = other.neverPerInstanceMixins; pathOptimizationsEnabled = other.pathOptimizationsEnabled; pathOptimizationsVersion = other.pathOptimizationsVersion; aclOptimizationsEnabled = other.aclOptimizationsEnabled; readAclMaxSize = other.readAclMaxSize; binaryManagerClass = other.binaryManagerClass; binaryManagerKey = other.binaryManagerKey; binaryStorePath = other.binaryStorePath; sendInvalidationEvents = other.sendInvalidationEvents; usersSeparatorKey = other.usersSeparatorKey; xaDataSourceName = other.xaDataSourceName; properties = new HashMap<String, String>(other.properties); } public void merge(RepositoryDescriptor other) { if (other.name != null) { name = other.name; } if (other.label != null) { label = other.label; } if (other.isDefault != null) { isDefault = other.isDefault; } if (other.repositoryFactoryClass != null) { repositoryFactoryClass = other.repositoryFactoryClass; } if (other.pool != null) { pool = new NuxeoConnectionManagerConfiguration(other.pool); } if (other.backendClass != null) { backendClass = other.backendClass; } if (other.cachingMapperClass != null) { cachingMapperClass = other.cachingMapperClass; } if (other.cachingMapperEnabled != null) { cachingMapperEnabled = other.cachingMapperEnabled; } cachingMapperProperties.putAll(other.cachingMapperProperties); if (other.noDDL != null) { noDDL = other.noDDL; } sqlInitFiles.addAll(other.sqlInitFiles); if (other.softDeleteEnabled != null) { softDeleteEnabled = other.softDeleteEnabled; } if (other.proxiesEnabled != null) { proxiesEnabled = other.proxiesEnabled; } if (other.idType != null) { idType = other.idType; } if (other.clusteringEnabled != null) { clusteringEnabled = other.clusteringEnabled; } if (other.clusteringDelay != null) { clusteringDelay = other.clusteringDelay; } for (FieldDescriptor of : other.schemaFields) { boolean append = true; for (FieldDescriptor f : schemaFields) { if (f.field.equals(of.field)) { f.merge(of); append = false; break; } } if (append) { schemaFields.add(of); } } if (other.arrayColumns != null) { arrayColumns = other.arrayColumns; } if (other.fulltextDisabled != null) { fulltextDisabled = other.fulltextDisabled; } if (other.fulltextAnalyzer != null) { fulltextAnalyzer = other.fulltextAnalyzer; } if (other.fulltextParser != null) { fulltextParser = other.fulltextParser; } if (other.fulltextCatalog != null) { fulltextCatalog = other.fulltextCatalog; } for (FulltextIndexDescriptor oi : other.fulltextIndexes) { boolean append = true; for (FulltextIndexDescriptor i : fulltextIndexes) { if (ObjectUtils.equals(i.name, oi.name)) { i.merge(oi); append = false; break; } } if (append) { fulltextIndexes.add(oi); } } fulltextExcludedTypes.addAll(other.fulltextExcludedTypes); fulltextIncludedTypes.addAll(other.fulltextIncludedTypes); neverPerInstanceMixins.addAll(other.neverPerInstanceMixins); if (other.pathOptimizationsEnabled != null) { pathOptimizationsEnabled = other.pathOptimizationsEnabled; } if (other.pathOptimizationsVersion != null) { pathOptimizationsVersion = other.pathOptimizationsVersion; } if (other.aclOptimizationsEnabled != null) { aclOptimizationsEnabled = other.aclOptimizationsEnabled; } if (other.readAclMaxSize != null) { readAclMaxSize = other.readAclMaxSize; } if (other.binaryManagerClass != null) { binaryManagerClass = other.binaryManagerClass; } if (other.binaryManagerKey != null) { binaryManagerKey = other.binaryManagerKey; } if (other.binaryStorePath != null) { binaryStorePath = other.binaryStorePath; } sendInvalidationEvents = other.sendInvalidationEvents; // no merge if (other.usersSeparatorKey != null) { usersSeparatorKey = other.usersSeparatorKey; } if (other.xaDataSourceName != null) { xaDataSourceName = other.xaDataSourceName; } properties.putAll(other.properties); } }