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.Arrays; 025import java.util.Collection; 026import java.util.Collections; 027import java.util.HashMap; 028import java.util.List; 029import java.util.Map; 030 031import org.apache.commons.lang.StringUtils; 032import org.nuxeo.ecm.core.api.DocumentModel; 033import org.nuxeo.ecm.core.api.DocumentModelComparator; 034import org.nuxeo.ecm.core.schema.types.Field; 035import org.nuxeo.ecm.directory.api.DirectoryDeleteConstraint; 036import org.nuxeo.runtime.metrics.MetricsService; 037 038import com.codahale.metrics.Counter; 039import com.codahale.metrics.MetricRegistry; 040import com.codahale.metrics.SharedMetricRegistries; 041 042public abstract class AbstractDirectory implements Directory { 043 044 public final BaseDirectoryDescriptor descriptor; 045 046 protected DirectoryFieldMapper fieldMapper; 047 048 protected final Map<String, List<Reference>> references = new HashMap<>(); 049 050 // simple cache system for entry lookups, disabled by default 051 protected final DirectoryCache cache; 052 053 // @since 5.7 054 protected final MetricRegistry registry = SharedMetricRegistries.getOrCreate(MetricsService.class.getName()); 055 056 protected final Counter sessionCount; 057 058 protected final Counter sessionMaxCount; 059 060 protected Map<String, Field> schemaFieldMap; 061 062 protected List<String> types = new ArrayList<>(); 063 064 protected Class<? extends Reference> referenceClass; 065 066 protected AbstractDirectory(BaseDirectoryDescriptor descriptor, Class<? extends Reference> referenceClass) { 067 this.referenceClass = referenceClass; 068 this.descriptor = descriptor; 069 // is the directory visible in the ui 070 if (descriptor.types != null) { 071 this.types = Arrays.asList(descriptor.types); 072 } 073 if (!descriptor.template && doSanityChecks()) { 074 if (StringUtils.isEmpty(descriptor.idField)) { 075 throw new DirectoryException("idField configuration is missing for directory: " + getName()); 076 } 077 if (StringUtils.isEmpty(descriptor.schemaName)) { 078 throw new DirectoryException("schema configuration is missing for directory " + getName()); 079 } 080 } 081 082 sessionCount = registry.counter(MetricRegistry.name("nuxeo", "directories", getName(), "sessions", "active")); 083 sessionMaxCount = registry.counter(MetricRegistry.name("nuxeo", "directories", getName(), "sessions", "max")); 084 085 // add references 086 addInverseReferences(descriptor.getInverseReferences()); 087 addReferences(descriptor.getReferences()); 088 089 // cache parameterization 090 cache = new DirectoryCache(getName()); 091 cache.setEntryCacheName(descriptor.cacheEntryName); 092 cache.setEntryCacheWithoutReferencesName(descriptor.cacheEntryWithoutReferencesName); 093 cache.setNegativeCaching(descriptor.negativeCaching); 094 095 } 096 097 protected boolean doSanityChecks() { 098 return true; 099 } 100 101 @Override 102 public String getName() { 103 return descriptor.name; 104 } 105 106 @Override 107 public String getSchema() { 108 return descriptor.schemaName; 109 } 110 111 @Override 112 public String getParentDirectory() { 113 return descriptor.parentDirectory; 114 } 115 116 @Override 117 public String getIdField() { 118 return descriptor.idField; 119 } 120 121 @Override 122 public String getPasswordField() { 123 return descriptor.passwordField; 124 } 125 126 @Override 127 public boolean isReadOnly() { 128 return descriptor.isReadOnly(); 129 } 130 131 public void setReadOnly(boolean readOnly) { 132 descriptor.setReadOnly(readOnly); 133 } 134 135 @Override 136 public void invalidateCaches() throws DirectoryException { 137 cache.invalidateAll(); 138 for (Reference ref : getReferences()) { 139 Directory targetDir = ref.getTargetDirectory(); 140 if (targetDir != null) { 141 targetDir.invalidateDirectoryCache(); 142 } 143 } 144 } 145 146 public DirectoryFieldMapper getFieldMapper() { 147 if (fieldMapper == null) { 148 fieldMapper = new DirectoryFieldMapper(); 149 } 150 return fieldMapper; 151 } 152 153 @Deprecated 154 @Override 155 public Reference getReference(String referenceFieldName) { 156 List<Reference> refs = getReferences(referenceFieldName); 157 if (refs == null || refs.isEmpty()) { 158 return null; 159 } else if (refs.size() == 1) { 160 return refs.get(0); 161 } else { 162 throw new DirectoryException("Unexpected multiple references for " + referenceFieldName + " in directory " 163 + getName()); 164 } 165 } 166 167 @Override 168 public List<Reference> getReferences(String referenceFieldName) { 169 return references.get(referenceFieldName); 170 } 171 172 public boolean isReference(String referenceFieldName) { 173 return references.containsKey(referenceFieldName); 174 } 175 176 public void addReference(Reference reference) { 177 reference.setSourceDirectoryName(getName()); 178 String fieldName = reference.getFieldName(); 179 List<Reference> fieldRefs; 180 if (references.containsKey(fieldName)) { 181 fieldRefs = references.get(fieldName); 182 } else { 183 references.put(fieldName, fieldRefs = new ArrayList<>(1)); 184 } 185 fieldRefs.add(reference); 186 } 187 188 public void addReferences(Reference[] refs) { 189 for (Reference reference : refs) { 190 addReference(reference); 191 } 192 } 193 194 protected void addInverseReferences(InverseReferenceDescriptor[] references) { 195 if (references != null) { 196 Arrays.stream(references).map(InverseReference::new).forEach(this::addReference); 197 } 198 } 199 200 protected void addReferences(ReferenceDescriptor[] references) { 201 if (references != null) { 202 for (ReferenceDescriptor desc : references) { 203 try { 204 addReference(referenceClass.getDeclaredConstructor(ReferenceDescriptor.class).newInstance(desc)); 205 } catch (ReflectiveOperationException e) { 206 throw new DirectoryException( 207 "An error occurred while instantiating reference class " + referenceClass.getName(), e); 208 } 209 } 210 } 211 } 212 213 @Override 214 public Collection<Reference> getReferences() { 215 List<Reference> allRefs = new ArrayList<>(2); 216 for (List<Reference> refs : references.values()) { 217 allRefs.addAll(refs); 218 } 219 return allRefs; 220 } 221 222 /** 223 * Helper method to order entries. 224 * 225 * @param entries the list of entries. 226 * @param orderBy an ordered map of field name -> "asc" or "desc". 227 */ 228 public void orderEntries(List<DocumentModel> entries, Map<String, String> orderBy) throws DirectoryException { 229 Collections.sort(entries, new DocumentModelComparator(getSchema(), orderBy)); 230 } 231 232 @Override 233 public DirectoryCache getCache() { 234 return cache; 235 } 236 237 public void removeSession(Session session) { 238 sessionCount.dec(); 239 } 240 241 public void addSession(Session session) { 242 sessionCount.inc(); 243 if (sessionCount.getCount() > sessionMaxCount.getCount()) { 244 sessionMaxCount.inc(); 245 } 246 } 247 248 @Override 249 public void invalidateDirectoryCache() throws DirectoryException { 250 getCache().invalidateAll(); 251 } 252 253 @Override 254 public boolean isMultiTenant() { 255 return false; 256 } 257 258 @Override 259 public void shutdown() { 260 sessionCount.dec(sessionCount.getCount()); 261 sessionMaxCount.dec(sessionMaxCount.getCount()); 262 } 263 264 /** 265 * since @8.4 266 */ 267 @Override 268 public List<String> getTypes() { 269 return types; 270 } 271 272 /** 273 * @since 8.4 274 */ 275 @Override 276 public List<DirectoryDeleteConstraint> getDirectoryDeleteConstraints() { 277 return descriptor.getDeleteConstraints(); 278 } 279 280 @Override 281 public Map<String, Field> getSchemaFieldMap() { 282 return schemaFieldMap; 283 } 284}