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}