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    @XNodeList(value = "filters/staticFilter", type = SQLStaticFilter[].class, componentType = SQLStaticFilter.class)
133    private SQLStaticFilter[] staticFilters;
134
135    @XNode("nativeCase")
136    public Boolean nativeCase;
137
138    @XNode("computeMultiTenantId")
139    private boolean computeMultiTenantId = true;
140
141    public String getDataSourceName() {
142        return dataSourceName;
143    }
144
145    public void setDataSourceName(String dataSourceName) {
146        this.dataSourceName = dataSourceName;
147    }
148
149    public void setIdField(String idField) {
150        this.idField = idField;
151    }
152
153    public String getName() {
154        return name;
155    }
156
157    public void setName(String name) {
158        this.name = name;
159    }
160
161    public String getSchemaName() {
162        return schemaName;
163    }
164
165    public void setSchemaName(String schemaName) {
166        this.schemaName = schemaName;
167    }
168
169    // XXX never used: is it supposed to help determining an entry full id
170    // using
171    // the parent directory id?
172    public String getParentDirectory() {
173        return parentDirectory;
174    }
175
176    public void setParentDirectory(String parentDirectory) {
177        this.parentDirectory = parentDirectory;
178    }
179
180    public String getTableName() {
181        return tableName;
182    }
183
184    public void setTableName(String tableName) {
185        this.tableName = tableName;
186    }
187
188    public String getDbDriver() {
189        return dbDriver;
190    }
191
192    public String getDbPassword() {
193        return dbPassword;
194    }
195
196    public String getDbUrl() {
197        return dbUrl;
198    }
199
200    public String getDbUser() {
201        return dbUser;
202    }
203
204    public String getDataFileName() {
205        return dataFileName;
206    }
207
208    public char getDataFileCharacterSeparator() {
209        if (dataFileCharacterSeparator == null || dataFileCharacterSeparator.length() == 0) {
210            log.info("Character separator not well set will " + "take the default value, \""
211                    + DEFAULT_CHARACTER_SEPARATOR + "\"");
212            return DEFAULT_CHARACTER_SEPARATOR;
213        }
214
215        if (dataFileCharacterSeparator.length() > 1) {
216            log.warn("More than one character found for character separator, " + "will take the first one \""
217                    + dataFileCharacterSeparator.charAt(0) + "\"");
218        }
219
220        return dataFileCharacterSeparator.charAt(0);
221    }
222
223    public String getPasswordField() {
224        return passwordField;
225    }
226
227    public void setPasswordField(String passwordField) {
228        this.passwordField = passwordField;
229    }
230
231    public String getIdField() {
232        return idField;
233    }
234
235    public String getCreateTablePolicy() {
236        return createTablePolicy;
237    }
238
239    @XNode("createTablePolicy")
240    public void setCreateTablePolicy(String createTablePolicy) throws DirectoryException {
241        if (createTablePolicy == null) {
242            this.createTablePolicy = DEFAULT_POLICY;
243            return;
244        }
245        createTablePolicy = createTablePolicy.toLowerCase();
246        boolean validPolicy = false;
247        for (String policy : SCRIPT_POLICIES) {
248            if (createTablePolicy.equals(policy)) {
249                validPolicy = true;
250                break;
251            }
252        }
253        if (!validPolicy) {
254            throw new DirectoryException("invalid value for createTablePolicy: " + createTablePolicy
255                    + ". It should be one of 'never', " + "'on_missing_columns',  or 'always'.");
256        }
257        this.createTablePolicy = createTablePolicy;
258    }
259
260    @XNode("substringMatchType")
261    public void setSubstringMatchType(String substringMatchType) {
262        if (substringMatchType != null) {
263            try {
264                this.substringMatchType = Enum.valueOf(SubstringMatchType.class, substringMatchType);
265            } catch (IllegalArgumentException iae) {
266                log.error("Invalid substring match type: " + substringMatchType
267                        + ". Valid options: subinitial, subfinal, subany");
268                this.substringMatchType = SubstringMatchType.subinitial;
269            }
270        }
271    }
272
273    public Reference[] getInverseReferences() {
274        return inverseReferences;
275    }
276
277    public Reference[] getTableReferences() {
278        return tableReferences;
279    }
280
281    public Boolean getReadOnly() {
282        return readOnly == null ? Boolean.valueOf(READ_ONY_DEFAULT) : readOnly;
283    }
284
285    public void setReadOnly(Boolean readOnly) {
286        this.readOnly = readOnly;
287    }
288
289    public boolean isAutoincrementIdField() {
290        return autoincrementIdField == null ? AUTO_INCREMENT_ID_FIELD_DEFAULT : autoincrementIdField.booleanValue();
291    }
292
293    public void setAutoincrementIdField(boolean autoincrementIdField) {
294        this.autoincrementIdField = Boolean.valueOf(autoincrementIdField);
295    }
296
297    public void setDbDriver(String dbDriver) {
298        this.dbDriver = dbDriver;
299    }
300
301    public void setDbPassword(String dbPassword) {
302        this.dbPassword = dbPassword;
303    }
304
305    public void setDbUrl(String dbUrl) {
306        this.dbUrl = dbUrl;
307    }
308
309    public void setDbUser(String dbUser) {
310        this.dbUser = dbUser;
311    }
312
313    public void setInverseReferences(InverseReference[] inverseReferences) {
314        this.inverseReferences = inverseReferences;
315    }
316
317    public void setDataFileName(String dataFile) {
318        this.dataFileName = dataFile;
319    }
320
321    public void setTableReferences(TableReference[] tableReferences) {
322        this.tableReferences = tableReferences;
323    }
324
325    public int getQuerySizeLimit() {
326        return querySizeLimit == null ? QUERY_SIZE_LIMIT_DEFAULT : querySizeLimit.intValue();
327    }
328
329    public void setQuerySizeLimit(int querySizeLimit) {
330        this.querySizeLimit = Integer.valueOf(querySizeLimit);
331    }
332
333    public void setRemove(boolean delete) {
334        this.remove = delete;
335    }
336
337    public boolean getRemove() {
338        return this.remove;
339    }
340
341    public SubstringMatchType getSubstringMatchType() {
342        return substringMatchType == null ? SubstringMatchType.subinitial : substringMatchType;
343    }
344
345    public void setSubstringMatchType(SubstringMatchType substringMatchType) {
346        this.substringMatchType = substringMatchType;
347    }
348
349    public SQLStaticFilter[] getStaticFilters() {
350        if (staticFilters == null) {
351            return new SQLStaticFilter[0];
352        }
353        return staticFilters;
354    }
355
356    /**
357     * Returns {@code true} if a multi tenant id should be computed for this directory, if the directory has support for
358     * multi tenancy, {@code false} otherwise.
359     *
360     * @since 5.6
361     */
362    public boolean isComputeMultiTenantId() {
363        return computeMultiTenantId;
364    }
365
366    /**
367     * Merge re-written since 5.6 to comply to hot reload needs, omitting to merge properties initialized by xmap)
368     */
369    public void merge(SQLDirectoryDescriptor other) {
370        merge(other, false);
371    }
372
373    public void merge(SQLDirectoryDescriptor other, boolean overwite) {
374        if (other.dataSourceName != null || overwite) {
375            dataSourceName = other.dataSourceName;
376        }
377        if (other.dbDriver != null || overwite) {
378            dbDriver = other.dbDriver;
379        }
380        if (other.dbUrl != null || overwite) {
381            dbUrl = other.dbUrl;
382        }
383        if (other.dbUser != null || overwite) {
384            dbUser = other.dbUser;
385        }
386        if (other.dbPassword != null || overwite) {
387            dbPassword = other.dbPassword;
388        }
389        if (other.tableName != null || overwite) {
390            tableName = other.tableName;
391        }
392        if (other.schemaName != null || overwite) {
393            schemaName = other.schemaName;
394        }
395        if (other.parentDirectory != null || overwite) {
396            parentDirectory = other.parentDirectory;
397        }
398        if ((other.initDependencies != null && other.initDependencies.size() != 0) || overwite) {
399            initDependencies = other.initDependencies;
400        }
401        if (other.idField != null || overwite) {
402            idField = other.idField;
403        }
404        if (other.dataFileName != null || overwite) {
405            dataFileName = other.dataFileName;
406        }
407        if (other.dataFileCharacterSeparator != null || overwite) {
408            dataFileCharacterSeparator = other.dataFileCharacterSeparator;
409        }
410        if (other.createTablePolicy != null || overwite) {
411            createTablePolicy = other.createTablePolicy;
412        }
413        if (other.substringMatchType != null || overwite) {
414            substringMatchType = other.substringMatchType;
415        }
416        if (other.autoincrementIdField != null || overwite) {
417            autoincrementIdField = other.autoincrementIdField;
418        }
419        if (other.readOnly != null || overwite) {
420            readOnly = other.readOnly;
421        }
422        if (other.passwordField != null || overwite) {
423            passwordField = other.passwordField;
424        }
425        if (other.passwordHashAlgorithm != null || overwite) {
426            passwordHashAlgorithm = other.passwordHashAlgorithm;
427        }
428        if (other.querySizeLimit != null || overwite) {
429            querySizeLimit = other.querySizeLimit;
430        }
431
432        if ((other.inverseReferences != null && other.inverseReferences.length != 0) || overwite) {
433            inverseReferences = other.inverseReferences;
434        }
435        if ((other.tableReferences != null && other.tableReferences.length != 0) || overwite) {
436            tableReferences = other.tableReferences;
437        }
438        if ((other.permissions != null && other.permissions.length != 0) || overwite) {
439            permissions = other.permissions;
440        }
441
442        remove = other.remove;
443
444        if (other.cacheEntryName != null || overwite) {
445            cacheEntryName = other.cacheEntryName;
446        }
447        if (other.cacheEntryWithoutReferencesName != null || overwite) {
448            cacheEntryWithoutReferencesName = other.cacheEntryWithoutReferencesName;
449        }
450        if ((other.staticFilters != null && other.staticFilters.length != 0) || overwite) {
451            staticFilters = other.staticFilters;
452        }
453        if (other.nativeCase != null || overwite) {
454            nativeCase = other.nativeCase;
455        }
456
457        computeMultiTenantId = other.computeMultiTenantId;
458    }
459
460    public SQLDirectoryDescriptor clone() {
461        SQLDirectoryDescriptor clone = new SQLDirectoryDescriptor();
462        clone.name = name;
463        clone.schemaName = schemaName;
464        clone.parentDirectory = parentDirectory;
465        clone.dataSourceName = dataSourceName;
466        clone.dbDriver = dbDriver;
467        clone.dbUrl = dbUrl;
468        clone.dbUser = dbUser;
469        clone.dbPassword = dbPassword;
470        clone.tableName = tableName;
471        if (initDependencies != null) {
472            clone.initDependencies = new ArrayList<String>(initDependencies);
473        }
474        clone.idField = idField;
475        clone.dataFileName = dataFileName;
476        clone.dataFileCharacterSeparator = dataFileCharacterSeparator;
477        clone.createTablePolicy = createTablePolicy;
478        clone.substringMatchType = substringMatchType;
479        clone.autoincrementIdField = autoincrementIdField;
480        clone.readOnly = readOnly;
481        clone.passwordField = passwordField;
482        clone.passwordHashAlgorithm = passwordHashAlgorithm;
483        clone.querySizeLimit = querySizeLimit;
484        if (tableReferences != null) {
485            clone.tableReferences = new TableReference[tableReferences.length];
486            for (int i = 0; i < tableReferences.length; i++) {
487                clone.tableReferences[i] = tableReferences[i].clone();
488            }
489        }
490        if (inverseReferences != null) {
491            clone.inverseReferences = new InverseReference[inverseReferences.length];
492            for (int i = 0; i < inverseReferences.length; i++) {
493                clone.inverseReferences[i] = inverseReferences[i].clone();
494            }
495        }
496        if (permissions != null) {
497            clone.permissions = new PermissionDescriptor[permissions.length];
498            for (int i = 0; i < permissions.length; i++) {
499                clone.permissions[i] = permissions[i].clone();
500            }
501        }
502        clone.remove = remove;
503        clone.cacheEntryName = cacheEntryName;
504        clone.cacheEntryWithoutReferencesName = cacheEntryWithoutReferencesName;
505        if (staticFilters != null) {
506            clone.staticFilters = new SQLStaticFilter[staticFilters.length];
507            for (int i = 0; i < staticFilters.length; i++) {
508                clone.staticFilters[i] = staticFilters[i].clone();
509            }
510        }
511        clone.nativeCase = nativeCase;
512        clone.computeMultiTenantId = computeMultiTenantId;
513        return clone;
514    }
515}