/*
* (C) Copyright 2006-2015 Nuxeo SA (http://nuxeo.com/) and others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* 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.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.storage.FulltextDescriptor;
import org.nuxeo.ecm.core.storage.FulltextDescriptor.FulltextIndexDescriptor;
import org.nuxeo.runtime.jtajca.NuxeoConnectionManagerConfiguration;
/**
* Low-level VCS Repository Descriptor.
*/
@XObject(value = "repository", order = { "@name" })
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;
/** At startup, DDL changes are not detected. */
public static final String DDL_MODE_IGNORE = "ignore";
/** At startup, DDL changes are detected and if not empty they are dumped. */
public static final String DDL_MODE_DUMP = "dump";
/** At startup, DDL changes are detected and executed. */
public static final String DDL_MODE_EXECUTE = "execute";
/** At startup, DDL changes are detected and if not empty Nuxeo startup is aborted. */
public static final String DDL_MODE_ABORT = "abort";
/** Specifies that stored procedure detection must be compatible with previous Nuxeo versions. */
public static final String DDL_MODE_COMPAT = "compat";
@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<>(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;
}
@XNode("@label")
public String label;
@XNode("@isDefault")
private Boolean isDefault;
public Boolean isDefault() {
return isDefault;
}
// compat, when used with old-style extension point syntax
// and nested repository
@XNode("repository")
public RepositoryDescriptor repositoryDescriptor;
public NuxeoConnectionManagerConfiguration pool;
@XNode("pool")
public void setPool(NuxeoConnectionManagerConfiguration pool) {
pool.setName("repository/" + name);
this.pool = pool;
}
@XNode("backendClass")
public Class<? extends RepositoryBackend> backendClass;
@XNode("clusterInvalidatorClass")
public Class<? extends ClusterInvalidator> clusterInvalidatorClass;
@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<>();
@XNode("ddlMode")
private String ddlMode;
public String getDDLMode() {
return ddlMode;
}
@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<>(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@id")
private String clusterNodeId;
public String getClusterNodeId() {
return clusterNodeId;
}
@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<>(0);
@XNode("schema/arrayColumns")
private Boolean arrayColumns;
public boolean getArrayColumns() {
return defaultFalse(arrayColumns);
}
public void setArrayColumns(boolean enabled) {
arrayColumns = Boolean.valueOf(enabled);
}
@XNode("indexing/queryMaker@class")
public void setQueryMakerDeprecated(String klass) {
log.warn("Setting queryMaker from repository configuration is now deprecated");
}
// VCS-specific fulltext indexing options
private String fulltextAnalyzer;
public String getFulltextAnalyzer() {
return fulltextAnalyzer;
}
@XNode("indexing/fulltext@analyzer")
public void setFulltextAnalyzer(String fulltextAnalyzer) {
this.fulltextAnalyzer = fulltextAnalyzer;
}
private String fulltextCatalog;
public String getFulltextCatalog() {
return fulltextCatalog;
}
@XNode("indexing/fulltext@catalog")
public void setFulltextCatalog(String fulltextCatalog) {
this.fulltextCatalog = fulltextCatalog;
}
private FulltextDescriptor fulltextDescriptor = new FulltextDescriptor();
public FulltextDescriptor getFulltextDescriptor() {
return fulltextDescriptor;
}
@XNode("indexing/fulltext@fieldSizeLimit")
public void setFulltextFieldSizeLimit(int fieldSizeLimit) {
fulltextDescriptor.setFulltextFieldSizeLimit(fieldSizeLimit);
}
@XNode("indexing/fulltext@disabled")
public void setFulltextDisabled(boolean disabled) {
fulltextDescriptor.setFulltextDisabled(disabled);
}
@XNode("indexing/fulltext@searchDisabled")
public void setFulltextSearchDisabled(boolean disabled) {
fulltextDescriptor.setFulltextSearchDisabled(disabled);
}
@XNode("indexing/fulltext@parser")
public void setFulltextParser(String fulltextParser) {
fulltextDescriptor.setFulltextParser(fulltextParser);
}
@XNodeList(value = "indexing/fulltext/index", type = ArrayList.class, componentType = FulltextIndexDescriptor.class)
public void setFulltextIndexes(List<FulltextIndexDescriptor> fulltextIndexes) {
fulltextDescriptor.setFulltextIndexes(fulltextIndexes);
}
@XNodeList(value = "indexing/excludedTypes/type", type = HashSet.class, componentType = String.class)
public void setFulltextExcludedTypes(Set<String> fulltextExcludedTypes) {
fulltextDescriptor.setFulltextExcludedTypes(fulltextExcludedTypes);
}
@XNodeList(value = "indexing/includedTypes/type", type = HashSet.class, componentType = String.class)
public void setFulltextIncludedTypes(Set<String> fulltextIncludedTypes) {
fulltextDescriptor.setFulltextIncludedTypes(fulltextIncludedTypes);
}
// compat
@XNodeList(value = "indexing/neverPerDocumentFacets/facet", type = HashSet.class, componentType = String.class)
public Set<String> neverPerInstanceMixins = new HashSet<>(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("usersSeparator@key")
public String usersSeparatorKey;
/** @since 9.1 */
@XNode("changeTokenEnabled")
private Boolean changeTokenEnabled;
/** @since 9.1 */
public boolean isChangeTokenEnabled() {
return defaultFalse(changeTokenEnabled);
}
/** @since 9.1 */
public void setChangeTokenEnabled(boolean enabled) {
this.changeTokenEnabled = Boolean.valueOf(enabled);
}
public RepositoryDescriptor() {
}
/** Copy constructor. */
public RepositoryDescriptor(RepositoryDescriptor other) {
name = other.name;
label = other.label;
isDefault = other.isDefault;
pool = other.pool == null ? null : new NuxeoConnectionManagerConfiguration(other.pool);
backendClass = other.backendClass;
clusterInvalidatorClass = other.clusterInvalidatorClass;
cachingMapperClass = other.cachingMapperClass;
cachingMapperEnabled = other.cachingMapperEnabled;
cachingMapperProperties = new HashMap<>(other.cachingMapperProperties);
noDDL = other.noDDL;
ddlMode = other.ddlMode;
sqlInitFiles = new ArrayList<>(other.sqlInitFiles);
softDeleteEnabled = other.softDeleteEnabled;
proxiesEnabled = other.proxiesEnabled;
schemaFields = FieldDescriptor.copyList(other.schemaFields);
arrayColumns = other.arrayColumns;
idType = other.idType;
clusterNodeId = other.clusterNodeId;
clusteringEnabled = other.clusteringEnabled;
clusteringDelay = other.clusteringDelay;
fulltextAnalyzer = other.fulltextAnalyzer;
fulltextCatalog = other.fulltextCatalog;
fulltextDescriptor = new FulltextDescriptor(other.fulltextDescriptor);
neverPerInstanceMixins = other.neverPerInstanceMixins;
pathOptimizationsEnabled = other.pathOptimizationsEnabled;
pathOptimizationsVersion = other.pathOptimizationsVersion;
aclOptimizationsEnabled = other.aclOptimizationsEnabled;
readAclMaxSize = other.readAclMaxSize;
usersSeparatorKey = other.usersSeparatorKey;
changeTokenEnabled = other.changeTokenEnabled;
}
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.pool != null) {
if (pool == null) {
pool = new NuxeoConnectionManagerConfiguration(other.pool);
} else {
pool.merge(other.pool);
}
}
if (other.backendClass != null) {
backendClass = other.backendClass;
}
if (other.clusterInvalidatorClass != null) {
clusterInvalidatorClass = other.clusterInvalidatorClass;
}
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;
}
if (other.ddlMode != null) {
ddlMode = other.ddlMode;
}
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.clusterNodeId != null) {
clusterNodeId = other.clusterNodeId;
}
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.fulltextAnalyzer != null) {
fulltextAnalyzer = other.fulltextAnalyzer;
}
if (other.fulltextCatalog != null) {
fulltextCatalog = other.fulltextCatalog;
}
fulltextDescriptor.merge(other.fulltextDescriptor);
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.usersSeparatorKey != null) {
usersSeparatorKey = other.usersSeparatorKey;
}
if (other.changeTokenEnabled != null) {
changeTokenEnabled = other.changeTokenEnabled;
}
}
}