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", order = { "@name" })
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    }
151
152    @XNode("@label")
153    public String label;
154
155    @XNode("@isDefault")
156    private Boolean isDefault;
157
158    public Boolean isDefault() {
159        return isDefault;
160    }
161
162    // compat, when used with old-style extension point syntax
163    // and nested repository
164    @XNode("repository")
165    public RepositoryDescriptor repositoryDescriptor;
166
167    public NuxeoConnectionManagerConfiguration pool;
168
169    @XNode("pool")
170    public void setPool(NuxeoConnectionManagerConfiguration pool) {
171        pool.setName("repository/" + name);
172        this.pool = pool;
173    }
174
175    @XNode("backendClass")
176    public Class<? extends RepositoryBackend> backendClass;
177
178    @XNode("clusterInvalidatorClass")
179    public Class<? extends ClusterInvalidator> clusterInvalidatorClass;
180
181    @XNode("cachingMapper@class")
182    public Class<? extends CachingMapper> cachingMapperClass;
183
184    @XNode("cachingMapper@enabled")
185    private Boolean cachingMapperEnabled;
186
187    public boolean getCachingMapperEnabled() {
188        return defaultTrue(cachingMapperEnabled);
189    }
190
191    @XNodeMap(value = "cachingMapper/property", key = "@name", type = HashMap.class, componentType = String.class)
192    public Map<String, String> cachingMapperProperties = new HashMap<>();
193
194    @XNode("ddlMode")
195    private String ddlMode;
196
197    public String getDDLMode() {
198        return ddlMode;
199    }
200
201    @XNode("noDDL")
202    private Boolean noDDL;
203
204    public boolean getNoDDL() {
205        return defaultFalse(noDDL);
206    }
207
208    @XNodeList(value = "sqlInitFile", type = ArrayList.class, componentType = String.class)
209    public List<String> sqlInitFiles = new ArrayList<>(0);
210
211    @XNode("softDelete@enabled")
212    private Boolean softDeleteEnabled;
213
214    public boolean getSoftDeleteEnabled() {
215        return defaultFalse(softDeleteEnabled);
216    }
217
218    protected void setSoftDeleteEnabled(boolean enabled) {
219        softDeleteEnabled = Boolean.valueOf(enabled);
220    }
221
222    @XNode("proxies@enabled")
223    private Boolean proxiesEnabled;
224
225    public boolean getProxiesEnabled() {
226        return defaultTrue(proxiesEnabled);
227    }
228
229    protected void setProxiesEnabled(boolean enabled) {
230        proxiesEnabled = Boolean.valueOf(enabled);
231    }
232
233    @XNode("idType")
234    public String idType; // "varchar", "uuid", "sequence"
235
236    @XNode("clustering@id")
237    private String clusterNodeId;
238
239    public String getClusterNodeId() {
240        return clusterNodeId;
241    }
242
243    @XNode("clustering@enabled")
244    private Boolean clusteringEnabled;
245
246    public boolean getClusteringEnabled() {
247        return defaultFalse(clusteringEnabled);
248    }
249
250    protected void setClusteringEnabled(boolean enabled) {
251        clusteringEnabled = Boolean.valueOf(enabled);
252    }
253
254    @XNode("clustering@delay")
255    private Long clusteringDelay;
256
257    public long getClusteringDelay() {
258        return clusteringDelay == null ? 0 : clusteringDelay.longValue();
259    }
260
261    protected void setClusteringDelay(long delay) {
262        clusteringDelay = Long.valueOf(delay);
263    }
264
265    @XNodeList(value = "schema/field", type = ArrayList.class, componentType = FieldDescriptor.class)
266    public List<FieldDescriptor> schemaFields = new ArrayList<>(0);
267
268    @XNode("schema/arrayColumns")
269    private Boolean arrayColumns;
270
271    public boolean getArrayColumns() {
272        return defaultFalse(arrayColumns);
273    }
274
275    public void setArrayColumns(boolean enabled) {
276        arrayColumns = Boolean.valueOf(enabled);
277    }
278
279    @XNode("indexing/queryMaker@class")
280    public void setQueryMakerDeprecated(String klass) {
281        log.warn("Setting queryMaker from repository configuration is now deprecated");
282    }
283
284    // VCS-specific fulltext indexing options
285    private String fulltextAnalyzer;
286
287    public String getFulltextAnalyzer() {
288        return fulltextAnalyzer;
289    }
290
291    @XNode("indexing/fulltext@analyzer")
292    public void setFulltextAnalyzer(String fulltextAnalyzer) {
293        this.fulltextAnalyzer = fulltextAnalyzer;
294    }
295
296    private String fulltextCatalog;
297
298    public String getFulltextCatalog() {
299        return fulltextCatalog;
300    }
301
302    @XNode("indexing/fulltext@catalog")
303    public void setFulltextCatalog(String fulltextCatalog) {
304        this.fulltextCatalog = fulltextCatalog;
305    }
306
307    private FulltextDescriptor fulltextDescriptor = new FulltextDescriptor();
308
309    public FulltextDescriptor getFulltextDescriptor() {
310        return fulltextDescriptor;
311    }
312
313    @XNode("indexing/fulltext@fieldSizeLimit")
314    public void setFulltextFieldSizeLimit(int fieldSizeLimit) {
315        fulltextDescriptor.setFulltextFieldSizeLimit(fieldSizeLimit);
316    }
317
318    @XNode("indexing/fulltext@disabled")
319    public void setFulltextDisabled(boolean disabled) {
320        fulltextDescriptor.setFulltextDisabled(disabled);
321    }
322
323    @XNode("indexing/fulltext@searchDisabled")
324    public void setFulltextSearchDisabled(boolean disabled) {
325        fulltextDescriptor.setFulltextSearchDisabled(disabled);
326    }
327
328    @XNode("indexing/fulltext@parser")
329    public void setFulltextParser(String fulltextParser) {
330        fulltextDescriptor.setFulltextParser(fulltextParser);
331    }
332
333    @XNodeList(value = "indexing/fulltext/index", type = ArrayList.class, componentType = FulltextIndexDescriptor.class)
334    public void setFulltextIndexes(List<FulltextIndexDescriptor> fulltextIndexes) {
335        fulltextDescriptor.setFulltextIndexes(fulltextIndexes);
336    }
337
338    @XNodeList(value = "indexing/excludedTypes/type", type = HashSet.class, componentType = String.class)
339    public void setFulltextExcludedTypes(Set<String> fulltextExcludedTypes) {
340        fulltextDescriptor.setFulltextExcludedTypes(fulltextExcludedTypes);
341    }
342
343    @XNodeList(value = "indexing/includedTypes/type", type = HashSet.class, componentType = String.class)
344    public void setFulltextIncludedTypes(Set<String> fulltextIncludedTypes) {
345        fulltextDescriptor.setFulltextIncludedTypes(fulltextIncludedTypes);
346    }
347
348    // compat
349    @XNodeList(value = "indexing/neverPerDocumentFacets/facet", type = HashSet.class, componentType = String.class)
350    public Set<String> neverPerInstanceMixins = new HashSet<>(0);
351
352    @XNode("pathOptimizations@enabled")
353    private Boolean pathOptimizationsEnabled;
354
355    public boolean getPathOptimizationsEnabled() {
356        return defaultTrue(pathOptimizationsEnabled);
357    }
358
359    protected void setPathOptimizationsEnabled(boolean enabled) {
360        pathOptimizationsEnabled = Boolean.valueOf(enabled);
361    }
362
363    /* @since 5.7 */
364    @XNode("pathOptimizations@version")
365    private Integer pathOptimizationsVersion;
366
367    public int getPathOptimizationsVersion() {
368        return pathOptimizationsVersion == null ? DEFAULT_PATH_OPTIM_VERSION : pathOptimizationsVersion.intValue();
369    }
370
371    @XNode("aclOptimizations@enabled")
372    private Boolean aclOptimizationsEnabled;
373
374    public boolean getAclOptimizationsEnabled() {
375        return defaultTrue(aclOptimizationsEnabled);
376    }
377
378    protected void setAclOptimizationsEnabled(boolean enabled) {
379        aclOptimizationsEnabled = Boolean.valueOf(enabled);
380    }
381
382    /* @since 5.4.2 */
383    @XNode("aclOptimizations@readAclMaxSize")
384    private Integer readAclMaxSize;
385
386    public int getReadAclMaxSize() {
387        return readAclMaxSize == null ? DEFAULT_READ_ACL_MAX_SIZE : readAclMaxSize.intValue();
388    }
389
390    @XNode("usersSeparator@key")
391    public String usersSeparatorKey;
392
393    public RepositoryDescriptor() {
394    }
395
396    /** Copy constructor. */
397    public RepositoryDescriptor(RepositoryDescriptor other) {
398        name = other.name;
399        label = other.label;
400        isDefault = other.isDefault;
401        pool = other.pool == null ? null : new NuxeoConnectionManagerConfiguration(other.pool);
402        backendClass = other.backendClass;
403        clusterInvalidatorClass = other.clusterInvalidatorClass;
404        cachingMapperClass = other.cachingMapperClass;
405        cachingMapperEnabled = other.cachingMapperEnabled;
406        cachingMapperProperties = new HashMap<>(other.cachingMapperProperties);
407        noDDL = other.noDDL;
408        ddlMode = other.ddlMode;
409        sqlInitFiles = new ArrayList<>(other.sqlInitFiles);
410        softDeleteEnabled = other.softDeleteEnabled;
411        proxiesEnabled = other.proxiesEnabled;
412        schemaFields = FieldDescriptor.copyList(other.schemaFields);
413        arrayColumns = other.arrayColumns;
414        idType = other.idType;
415        clusterNodeId = other.clusterNodeId;
416        clusteringEnabled = other.clusteringEnabled;
417        clusteringDelay = other.clusteringDelay;
418        fulltextAnalyzer = other.fulltextAnalyzer;
419        fulltextCatalog = other.fulltextCatalog;
420        fulltextDescriptor = new FulltextDescriptor(other.fulltextDescriptor);
421        neverPerInstanceMixins = other.neverPerInstanceMixins;
422        pathOptimizationsEnabled = other.pathOptimizationsEnabled;
423        pathOptimizationsVersion = other.pathOptimizationsVersion;
424        aclOptimizationsEnabled = other.aclOptimizationsEnabled;
425        readAclMaxSize = other.readAclMaxSize;
426        usersSeparatorKey = other.usersSeparatorKey;
427    }
428
429    public void merge(RepositoryDescriptor other) {
430        if (other.name != null) {
431            name = other.name;
432        }
433        if (other.label != null) {
434            label = other.label;
435        }
436        if (other.isDefault != null) {
437            isDefault = other.isDefault;
438        }
439        if (other.pool != null) {
440            if (pool == null) {
441                pool = new NuxeoConnectionManagerConfiguration(other.pool);
442            } else {
443                pool.merge(other.pool);
444            }
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    }
524
525}