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 *     Nuxeo - initial API and implementation
018 *
019 */
020
021package org.nuxeo.ecm.directory;
022
023import java.util.ArrayList;
024import java.util.Collection;
025import java.util.Collections;
026import java.util.HashMap;
027import java.util.List;
028import java.util.Map;
029
030import org.apache.commons.lang.StringUtils;
031import org.nuxeo.ecm.core.api.DocumentModel;
032import org.nuxeo.ecm.core.api.DocumentModelComparator;
033import org.nuxeo.runtime.metrics.MetricsService;
034
035import com.codahale.metrics.Counter;
036import com.codahale.metrics.MetricRegistry;
037import com.codahale.metrics.SharedMetricRegistries;
038
039public abstract class AbstractDirectory implements Directory {
040
041    public final BaseDirectoryDescriptor descriptor;
042
043    protected DirectoryFieldMapper fieldMapper;
044
045    protected final Map<String, List<Reference>> references = new HashMap<>();
046
047    // simple cache system for entry lookups, disabled by default
048    protected final DirectoryCache cache;
049
050    // @since 5.7
051    protected final MetricRegistry registry = SharedMetricRegistries.getOrCreate(MetricsService.class.getName());
052
053    protected final Counter sessionCount;
054
055    protected final Counter sessionMaxCount;
056
057    protected AbstractDirectory(BaseDirectoryDescriptor descriptor) {
058        this.descriptor = descriptor;
059        if (!descriptor.template && doSanityChecks()) {
060            if (StringUtils.isEmpty(descriptor.idField)) {
061                throw new DirectoryException("idField configuration is missing for directory: " + getName());
062            }
063            if (StringUtils.isEmpty(descriptor.schemaName)) {
064                throw new DirectoryException("schema configuration is missing for directory " + getName());
065            }
066        }
067        cache = new DirectoryCache(getName());
068        sessionCount = registry.counter(MetricRegistry.name("nuxeo", "directories", getName(), "sessions", "active"));
069        sessionMaxCount = registry.counter(MetricRegistry.name("nuxeo", "directories", getName(), "sessions", "max"));
070    }
071
072    protected boolean doSanityChecks() {
073        return true;
074    }
075
076    /** To be implemented with a more specific return type. */
077    public abstract BaseDirectoryDescriptor getDescriptor();
078
079    @Override
080    public String getName() {
081        return descriptor.name;
082    }
083
084    @Override
085    public String getSchema() {
086        return descriptor.schemaName;
087    }
088
089    @Override
090    public String getParentDirectory() {
091        return descriptor.parentDirectory;
092    }
093
094    @Override
095    public String getIdField() {
096        return descriptor.idField;
097    }
098
099    @Override
100    public String getPasswordField() {
101        return descriptor.passwordField;
102    }
103
104    @Override
105    public boolean isReadOnly() {
106        return descriptor.isReadOnly();
107    }
108
109    public void setReadOnly(boolean readOnly) {
110        descriptor.setReadOnly(readOnly);
111    }
112
113    /**
114     * Invalidate my cache and the caches of linked directories by references.
115     */
116    public void invalidateCaches() throws DirectoryException {
117        cache.invalidateAll();
118        for (Reference ref : getReferences()) {
119            Directory targetDir = ref.getTargetDirectory();
120            if (targetDir != null) {
121                targetDir.invalidateDirectoryCache();
122            }
123        }
124    }
125
126    public DirectoryFieldMapper getFieldMapper() {
127        if (fieldMapper == null) {
128            fieldMapper = new DirectoryFieldMapper();
129        }
130        return fieldMapper;
131    }
132
133    @Deprecated
134    @Override
135    public Reference getReference(String referenceFieldName) {
136        List<Reference> refs = getReferences(referenceFieldName);
137        if (refs == null || refs.isEmpty()) {
138            return null;
139        } else if (refs.size() == 1) {
140            return refs.get(0);
141        } else {
142            throw new DirectoryException("Unexpected multiple references for " + referenceFieldName + " in directory "
143                    + getName());
144        }
145    }
146
147    @Override
148    public List<Reference> getReferences(String referenceFieldName) {
149        return references.get(referenceFieldName);
150    }
151
152    public boolean isReference(String referenceFieldName) {
153        return references.containsKey(referenceFieldName);
154    }
155
156    public void addReference(Reference reference) {
157        reference.setSourceDirectoryName(getName());
158        String fieldName = reference.getFieldName();
159        List<Reference> fieldRefs;
160        if (references.containsKey(fieldName)) {
161            fieldRefs = references.get(fieldName);
162        } else {
163            references.put(fieldName, fieldRefs = new ArrayList<>(1));
164        }
165        fieldRefs.add(reference);
166    }
167
168    public void addReferences(Reference[] refs) {
169        for (Reference reference : refs) {
170            addReference(reference);
171        }
172    }
173
174    @Override
175    public Collection<Reference> getReferences() {
176        List<Reference> allRefs = new ArrayList<>(2);
177        for (List<Reference> refs : references.values()) {
178            allRefs.addAll(refs);
179        }
180        return allRefs;
181    }
182
183    /**
184     * Helper method to order entries.
185     *
186     * @param entries the list of entries.
187     * @param orderBy an ordered map of field name -> "asc" or "desc".
188     */
189    public void orderEntries(List<DocumentModel> entries, Map<String, String> orderBy) throws DirectoryException {
190        Collections.sort(entries, new DocumentModelComparator(getSchema(), orderBy));
191    }
192
193    @Override
194    public DirectoryCache getCache() {
195        return cache;
196    }
197
198    public void removeSession(Session session) {
199        sessionCount.dec();
200    }
201
202    public void addSession(Session session) {
203        sessionCount.inc();
204        if (sessionCount.getCount() > sessionMaxCount.getCount()) {
205            sessionMaxCount.inc();
206        }
207    }
208
209    @Override
210    public void invalidateDirectoryCache() throws DirectoryException {
211        getCache().invalidateAll();
212    }
213
214    @Override
215    public boolean isMultiTenant() {
216        return false;
217    }
218
219    @Override
220    public void shutdown() {
221        sessionCount.dec(sessionCount.getCount());
222        sessionMaxCount.dec(sessionMaxCount.getCount());
223    }
224
225}