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