001/*
002 * (C) Copyright 2006-2007 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 *     Nuxeo - initial API and implementation
018 *
019 * $Id$
020 */
021
022package org.nuxeo.ecm.directory.sql;
023
024import java.util.ArrayList;
025import java.util.List;
026
027import org.apache.commons.logging.Log;
028import org.apache.commons.logging.LogFactory;
029import org.nuxeo.common.xmap.annotation.XNode;
030import org.nuxeo.common.xmap.annotation.XNodeList;
031import org.nuxeo.common.xmap.annotation.XObject;
032import org.nuxeo.ecm.directory.DirectoryException;
033import org.nuxeo.ecm.directory.InverseReference;
034import org.nuxeo.ecm.directory.PermissionDescriptor;
035import org.nuxeo.ecm.directory.Reference;
036
037@XObject(value = "directory")
038public class SQLDirectoryDescriptor {
039
040    private static final Log log = LogFactory.getLog(SQLDirectoryDescriptor.class);
041
042    public enum SubstringMatchType {
043        subinitial, subfinal, subany
044    }
045
046    public static final int QUERY_SIZE_LIMIT_DEFAULT = 0;
047
048    public static final boolean AUTO_INCREMENT_ID_FIELD_DEFAULT = false;
049
050    public static final boolean READ_ONY_DEFAULT = false;
051
052    protected static final char DEFAULT_CHARACTER_SEPARATOR = ',';
053
054    private static final String[] SCRIPT_POLICIES = { "never", "on_missing_columns", "always", };
055
056    private static final String DEFAULT_POLICY = "never";
057
058    @XNode("@name")
059    public String name;
060
061    @XNode("schema")
062    public String schemaName;
063
064    @XNode("parentDirectory")
065    public String parentDirectory;
066
067    @XNode("dataSource")
068    public String dataSourceName;
069
070    @XNode("dbDriver")
071    public String dbDriver;
072
073    @XNode("dbUrl")
074    public String dbUrl;
075
076    @XNode("dbUser")
077    public String dbUser;
078
079    @XNode("dbPassword")
080    public String dbPassword;
081
082    @XNode("table")
083    public String tableName;
084
085    @XNodeList(value = "init-dependencies/dependency", type = ArrayList.class, componentType = String.class)
086    public List<String> initDependencies;
087
088    @XNode("idField")
089    public String idField;
090
091    @XNode("dataFile")
092    public String dataFileName;
093
094    @XNode(value = "dataFileCharacterSeparator", trim = false)
095    public String dataFileCharacterSeparator = ",";
096
097    public String createTablePolicy;
098
099    public SubstringMatchType substringMatchType;
100
101    @XNode("autoincrementIdField")
102    public Boolean autoincrementIdField;
103
104    @XNode("readOnly")
105    public Boolean readOnly;
106
107    @XNode("passwordField")
108    private String passwordField;
109
110    @XNode("passwordHashAlgorithm")
111    public String passwordHashAlgorithm;
112
113    @XNode("querySizeLimit")
114    private Integer querySizeLimit;
115
116    @XNodeList(value = "references/tableReference", type = TableReference[].class, componentType = TableReference.class)
117    private TableReference[] tableReferences;
118
119    @XNodeList(value = "references/inverseReference", type = InverseReference[].class, componentType = InverseReference.class)
120    private InverseReference[] inverseReferences;
121
122    @XNodeList(value = "permissions/permission", type = PermissionDescriptor[].class, componentType = PermissionDescriptor.class)
123    public PermissionDescriptor[] permissions = null;
124
125    @XNode("@remove")
126    private boolean remove = false;
127
128    @XNode("cacheEntryName")
129    public String cacheEntryName = null;
130
131    @XNode("cacheEntryWithoutReferencesName")
132    public String cacheEntryWithoutReferencesName = null;
133
134    @XNode("negativeCaching")
135    public Boolean negativeCaching;
136
137    @XNodeList(value = "filters/staticFilter", type = SQLStaticFilter[].class, componentType = SQLStaticFilter.class)
138    private SQLStaticFilter[] staticFilters;
139
140    @XNode("nativeCase")
141    public Boolean nativeCase;
142
143    @XNode("computeMultiTenantId")
144    private boolean computeMultiTenantId = true;
145
146    public String getDataSourceName() {
147        return dataSourceName;
148    }
149
150    public void setDataSourceName(String dataSourceName) {
151        this.dataSourceName = dataSourceName;
152    }
153
154    public void setIdField(String idField) {
155        this.idField = idField;
156    }
157
158    public String getName() {
159        return name;
160    }
161
162    public void setName(String name) {
163        this.name = name;
164    }
165
166    public String getSchemaName() {
167        return schemaName;
168    }
169
170    public void setSchemaName(String schemaName) {
171        this.schemaName = schemaName;
172    }
173
174    // XXX never used: is it supposed to help determining an entry full id
175    // using
176    // the parent directory id?
177    public String getParentDirectory() {
178        return parentDirectory;
179    }
180
181    public void setParentDirectory(String parentDirectory) {
182        this.parentDirectory = parentDirectory;
183    }
184
185    public String getTableName() {
186        return tableName;
187    }
188
189    public void setTableName(String tableName) {
190        this.tableName = tableName;
191    }
192
193    public String getDbDriver() {
194        return dbDriver;
195    }
196
197    public String getDbPassword() {
198        return dbPassword;
199    }
200
201    public String getDbUrl() {
202        return dbUrl;
203    }
204
205    public String getDbUser() {
206        return dbUser;
207    }
208
209    public String getDataFileName() {
210        return dataFileName;
211    }
212
213    public char getDataFileCharacterSeparator() {
214        if (dataFileCharacterSeparator == null || dataFileCharacterSeparator.length() == 0) {
215            log.info("Character separator not well set will " + "take the default value, \""
216                    + DEFAULT_CHARACTER_SEPARATOR + "\"");
217            return DEFAULT_CHARACTER_SEPARATOR;
218        }
219
220        if (dataFileCharacterSeparator.length() > 1) {
221            log.warn("More than one character found for character separator, " + "will take the first one \""
222                    + dataFileCharacterSeparator.charAt(0) + "\"");
223        }
224
225        return dataFileCharacterSeparator.charAt(0);
226    }
227
228    public String getPasswordField() {
229        return passwordField;
230    }
231
232    public void setPasswordField(String passwordField) {
233        this.passwordField = passwordField;
234    }
235
236    public String getIdField() {
237        return idField;
238    }
239
240    public String getCreateTablePolicy() {
241        return createTablePolicy;
242    }
243
244    @XNode("createTablePolicy")
245    public void setCreateTablePolicy(String createTablePolicy) throws DirectoryException {
246        if (createTablePolicy == null) {
247            this.createTablePolicy = DEFAULT_POLICY;
248            return;
249        }
250        createTablePolicy = createTablePolicy.toLowerCase();
251        boolean validPolicy = false;
252        for (String policy : SCRIPT_POLICIES) {
253            if (createTablePolicy.equals(policy)) {
254                validPolicy = true;
255                break;
256            }
257        }
258        if (!validPolicy) {
259            throw new DirectoryException("invalid value for createTablePolicy: " + createTablePolicy
260                    + ". It should be one of 'never', " + "'on_missing_columns',  or 'always'.");
261        }
262        this.createTablePolicy = createTablePolicy;
263    }
264
265    @XNode("substringMatchType")
266    public void setSubstringMatchType(String substringMatchType) {
267        if (substringMatchType != null) {
268            try {
269                this.substringMatchType = Enum.valueOf(SubstringMatchType.class, substringMatchType);
270            } catch (IllegalArgumentException iae) {
271                log.error("Invalid substring match type: " + substringMatchType
272                        + ". Valid options: subinitial, subfinal, subany");
273                this.substringMatchType = SubstringMatchType.subinitial;
274            }
275        }
276    }
277
278    public Reference[] getInverseReferences() {
279        return inverseReferences;
280    }
281
282    public Reference[] getTableReferences() {
283        return tableReferences;
284    }
285
286    public Boolean getReadOnly() {
287        return readOnly == null ? Boolean.valueOf(READ_ONY_DEFAULT) : readOnly;
288    }
289
290    public void setReadOnly(Boolean readOnly) {
291        this.readOnly = readOnly;
292    }
293
294    public boolean isAutoincrementIdField() {
295        return autoincrementIdField == null ? AUTO_INCREMENT_ID_FIELD_DEFAULT : autoincrementIdField.booleanValue();
296    }
297
298    public void setAutoincrementIdField(boolean autoincrementIdField) {
299        this.autoincrementIdField = Boolean.valueOf(autoincrementIdField);
300    }
301
302    public void setDbDriver(String dbDriver) {
303        this.dbDriver = dbDriver;
304    }
305
306    public void setDbPassword(String dbPassword) {
307        this.dbPassword = dbPassword;
308    }
309
310    public void setDbUrl(String dbUrl) {
311        this.dbUrl = dbUrl;
312    }
313
314    public void setDbUser(String dbUser) {
315        this.dbUser = dbUser;
316    }
317
318    public void setInverseReferences(InverseReference[] inverseReferences) {
319        this.inverseReferences = inverseReferences;
320    }
321
322    public void setDataFileName(String dataFile) {
323        this.dataFileName = dataFile;
324    }
325
326    public void setTableReferences(TableReference[] tableReferences) {
327        this.tableReferences = tableReferences;
328    }
329
330    public int getQuerySizeLimit() {
331        return querySizeLimit == null ? QUERY_SIZE_LIMIT_DEFAULT : querySizeLimit.intValue();
332    }
333
334    public void setQuerySizeLimit(int querySizeLimit) {
335        this.querySizeLimit = Integer.valueOf(querySizeLimit);
336    }
337
338    public void setRemove(boolean delete) {
339        this.remove = delete;
340    }
341
342    public boolean getRemove() {
343        return this.remove;
344    }
345
346    public SubstringMatchType getSubstringMatchType() {
347        return substringMatchType == null ? SubstringMatchType.subinitial : substringMatchType;
348    }
349
350    public void setSubstringMatchType(SubstringMatchType substringMatchType) {
351        this.substringMatchType = substringMatchType;
352    }
353
354    public SQLStaticFilter[] getStaticFilters() {
355        if (staticFilters == null) {
356            return new SQLStaticFilter[0];
357        }
358        return staticFilters;
359    }
360
361    /**
362     * Returns {@code true} if a multi tenant id should be computed for this directory, if the directory has support for
363     * multi tenancy, {@code false} otherwise.
364     *
365     * @since 5.6
366     */
367    public boolean isComputeMultiTenantId() {
368        return computeMultiTenantId;
369    }
370
371    /**
372     * Merge re-written since 5.6 to comply to hot reload needs, omitting to merge properties initialized by xmap)
373     */
374    public void merge(SQLDirectoryDescriptor other) {
375        merge(other, false);
376    }
377
378    public void merge(SQLDirectoryDescriptor other, boolean overwite) {
379        if (other.dataSourceName != null || overwite) {
380            dataSourceName = other.dataSourceName;
381        }
382        if (other.dbDriver != null || overwite) {
383            dbDriver = other.dbDriver;
384        }
385        if (other.dbUrl != null || overwite) {
386            dbUrl = other.dbUrl;
387        }
388        if (other.dbUser != null || overwite) {
389            dbUser = other.dbUser;
390        }
391        if (other.dbPassword != null || overwite) {
392            dbPassword = other.dbPassword;
393        }
394        if (other.tableName != null || overwite) {
395            tableName = other.tableName;
396        }
397        if (other.schemaName != null || overwite) {
398            schemaName = other.schemaName;
399        }
400        if (other.parentDirectory != null || overwite) {
401            parentDirectory = other.parentDirectory;
402        }
403        if ((other.initDependencies != null && other.initDependencies.size() != 0) || overwite) {
404            initDependencies = other.initDependencies;
405        }
406        if (other.idField != null || overwite) {
407            idField = other.idField;
408        }
409        if (other.dataFileName != null || overwite) {
410            dataFileName = other.dataFileName;
411        }
412        if (other.dataFileCharacterSeparator != null || overwite) {
413            dataFileCharacterSeparator = other.dataFileCharacterSeparator;
414        }
415        if (other.createTablePolicy != null || overwite) {
416            createTablePolicy = other.createTablePolicy;
417        }
418        if (other.substringMatchType != null || overwite) {
419            substringMatchType = other.substringMatchType;
420        }
421        if (other.autoincrementIdField != null || overwite) {
422            autoincrementIdField = other.autoincrementIdField;
423        }
424        if (other.readOnly != null || overwite) {
425            readOnly = other.readOnly;
426        }
427        if (other.passwordField != null || overwite) {
428            passwordField = other.passwordField;
429        }
430        if (other.passwordHashAlgorithm != null || overwite) {
431            passwordHashAlgorithm = other.passwordHashAlgorithm;
432        }
433        if (other.querySizeLimit != null || overwite) {
434            querySizeLimit = other.querySizeLimit;
435        }
436
437        if ((other.inverseReferences != null && other.inverseReferences.length != 0) || overwite) {
438            inverseReferences = other.inverseReferences;
439        }
440        if ((other.tableReferences != null && other.tableReferences.length != 0) || overwite) {
441            tableReferences = other.tableReferences;
442        }
443        if ((other.permissions != null && other.permissions.length != 0) || overwite) {
444            permissions = other.permissions;
445        }
446
447        remove = other.remove;
448
449        if (other.cacheEntryName != null || overwite) {
450            cacheEntryName = other.cacheEntryName;
451        }
452        if (other.cacheEntryWithoutReferencesName != null || overwite) {
453            cacheEntryWithoutReferencesName = other.cacheEntryWithoutReferencesName;
454        }
455        if (other.negativeCaching != null || overwite) {
456            negativeCaching = other.negativeCaching;
457        }
458        if ((other.staticFilters != null && other.staticFilters.length != 0) || overwite) {
459            staticFilters = other.staticFilters;
460        }
461        if (other.nativeCase != null || overwite) {
462            nativeCase = other.nativeCase;
463        }
464
465        computeMultiTenantId = other.computeMultiTenantId;
466    }
467
468    public SQLDirectoryDescriptor clone() {
469        SQLDirectoryDescriptor clone = new SQLDirectoryDescriptor();
470        clone.name = name;
471        clone.schemaName = schemaName;
472        clone.parentDirectory = parentDirectory;
473        clone.dataSourceName = dataSourceName;
474        clone.dbDriver = dbDriver;
475        clone.dbUrl = dbUrl;
476        clone.dbUser = dbUser;
477        clone.dbPassword = dbPassword;
478        clone.tableName = tableName;
479        if (initDependencies != null) {
480            clone.initDependencies = new ArrayList<String>(initDependencies);
481        }
482        clone.idField = idField;
483        clone.dataFileName = dataFileName;
484        clone.dataFileCharacterSeparator = dataFileCharacterSeparator;
485        clone.createTablePolicy = createTablePolicy;
486        clone.substringMatchType = substringMatchType;
487        clone.autoincrementIdField = autoincrementIdField;
488        clone.readOnly = readOnly;
489        clone.passwordField = passwordField;
490        clone.passwordHashAlgorithm = passwordHashAlgorithm;
491        clone.querySizeLimit = querySizeLimit;
492        if (tableReferences != null) {
493            clone.tableReferences = new TableReference[tableReferences.length];
494            for (int i = 0; i < tableReferences.length; i++) {
495                clone.tableReferences[i] = tableReferences[i].clone();
496            }
497        }
498        if (inverseReferences != null) {
499            clone.inverseReferences = new InverseReference[inverseReferences.length];
500            for (int i = 0; i < inverseReferences.length; i++) {
501                clone.inverseReferences[i] = inverseReferences[i].clone();
502            }
503        }
504        if (permissions != null) {
505            clone.permissions = new PermissionDescriptor[permissions.length];
506            for (int i = 0; i < permissions.length; i++) {
507                clone.permissions[i] = permissions[i].clone();
508            }
509        }
510        clone.remove = remove;
511        clone.cacheEntryName = cacheEntryName;
512        clone.cacheEntryWithoutReferencesName = cacheEntryWithoutReferencesName;
513        clone.negativeCaching = negativeCaching;
514        if (staticFilters != null) {
515            clone.staticFilters = new SQLStaticFilter[staticFilters.length];
516            for (int i = 0; i < staticFilters.length; i++) {
517                clone.staticFilters[i] = staticFilters[i].clone();
518            }
519        }
520        clone.nativeCase = nativeCase;
521        clone.computeMultiTenantId = computeMultiTenantId;
522        return clone;
523    }
524}