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