@Test public void testUpdateReadonlyMultidir() throws Exception { MultiDirectory readonlyMultidir = (MultiDirectory) directoryService.getDirectory("readonlymulti"); MultiDirectorySession readonlyDir = (MultiDirectorySession) readonlyMultidir.getSession(); Session dir1 = memdir1.getSession(); Session dir2 = memdir2.getSession(); Session dir3 = memdir3.getSession(); // multi-subdirs update DocumentModel e = readonlyDir.getEntry("1"); assertEquals("foo1", e.getProperty("schema3", "thefoo")); assertEquals("bar1", e.getProperty("schema3", "thebar")); e.setProperty("schema3", "thefoo", "fffooo1"); e.setProperty("schema3", "thebar", "babar1"); readonlyDir.updateEntry(e); e = readonlyDir.getEntry("1"); // the multidirectory entry was not updated assertEquals("foo1", e.getProperty("schema3", "thefoo")); assertEquals("bar1", e.getProperty("schema3", "thebar")); // neither the underlying directories assertEquals("foo1", dir1.getEntry("1").getProperty("schema1", "foo")); assertEquals("bar1", dir2.getEntry("1").getProperty("schema2", "bar")); assertNull(dir3.getEntry("1")); }
@Test public void testReadOnlyEntryFromMultidirectory() throws Exception { MultiDirectory readonlyMultidir = (MultiDirectory) directoryService.getDirectory("readonlymulti"); MultiDirectorySession readonlyDir = (MultiDirectorySession) readonlyMultidir.getSession(); // all should be readonly assertTrue(BaseSession.isReadOnlyEntry(readonlyDir.getEntry("1"))); assertTrue(BaseSession.isReadOnlyEntry(readonlyDir.getEntry("2"))); assertTrue(BaseSession.isReadOnlyEntry(readonlyDir.getEntry("3"))); assertTrue(BaseSession.isReadOnlyEntry(readonlyDir.getEntry("4"))); }
@Override public DocumentModel createEntry(Map<String, Object> fieldMap) { if (!isCurrentUserAllowed(SecurityConstants.WRITE)) { return null; } init(); final Object rawid = fieldMap.get(schemaIdField); if (rawid == null) { throw new DirectoryException(String.format("Entry is missing id field '%s'", schemaIdField)); } final String id = String.valueOf(rawid); // XXX allow longs too for (SourceInfo sourceInfo : sourceInfos) { if (!sourceInfo.source.creation) { continue; } for (SubDirectoryInfo dirInfo : sourceInfo.subDirectoryInfos) { Map<String, Object> map = new HashMap<String, Object>(); map.put(dirInfo.idField, id); for (Entry<String, String> e : dirInfo.fromSource.entrySet()) { map.put(e.getValue(), fieldMap.get(e.getKey())); } dirInfo.getSession().createEntry(map); } return getEntry(id); } throw new DirectoryException( String.format("Directory '%s' has no source allowing creation", directory.getName())); }
@Override public void close() throws DirectoryException { try { if (sourceInfos == null) { return; } DirectoryException exc = null; for (SourceInfo sourceInfo : sourceInfos) { for (SubDirectoryInfo subDirectoryInfo : sourceInfo.subDirectoryInfos) { Session session = subDirectoryInfo.session; subDirectoryInfo.session = null; if (session != null) { try { session.close(); } catch (DirectoryException e) { // remember exception, we want to close all session // first if (exc == null) { exc = e; } else { // we can't reraise both, log this one log.error("Error closing directory " + subDirectoryInfo.dirName, e); } } } } if (exc != null) { throw exc; } } } finally { directory.removeSession(this); } }
public MultiDirectorySession(MultiDirectory directory) { directoryService = Framework.getService(DirectoryService.class); schemaManager = Framework.getLocalService(SchemaManager.class); this.directory = directory; descriptor = directory.getDescriptor(); schemaName = descriptor.schemaName; schemaIdField = descriptor.idField; schemaPasswordField = descriptor.passwordField; permissions = descriptor.permissions; }
@Override @SuppressWarnings("boxing") public DocumentModelList query( Map<String, Serializable> filter, Set<String> fulltext, Map<String, String> orderBy, boolean fetchReferences) { // list of entries final DocumentModelList results = new DocumentModelListImpl(); if (!isCurrentUserAllowed(SecurityConstants.READ)) { return results; } init(); // entry ids already seen (mapped to the source name) final Map<String, String> seen = new HashMap<String, String>(); if (fulltext == null) { fulltext = Collections.emptySet(); } Set<String> readOnlyEntries = new HashSet<String>(); for (SourceInfo sourceInfo : sourceInfos) { // accumulated map for each entry final Map<String, Map<String, Object>> maps = new HashMap<String, Map<String, Object>>(); // number of dirs seen for each entry final Map<String, Integer> counts = new HashMap<String, Integer>(); // list of optional dirs where filter matches default values List<SubDirectoryInfo> optionalDirsMatching = new ArrayList<SubDirectoryInfo>(); for (SubDirectoryInfo dirInfo : sourceInfo.subDirectoryInfos) { // compute filter final Map<String, Serializable> dirFilter = new HashMap<String, Serializable>(); for (Entry<String, Serializable> e : filter.entrySet()) { final String fieldName = dirInfo.fromSource.get(e.getKey()); if (fieldName == null) { continue; } dirFilter.put(fieldName, e.getValue()); } if (dirInfo.isOptional) { // check if filter matches directory default values boolean matches = true; for (Map.Entry<String, Serializable> dirFilterEntry : dirFilter.entrySet()) { Object defaultValue = dirInfo.defaultEntry.get(dirFilterEntry.getKey()); Object filterValue = dirFilterEntry.getValue(); if (defaultValue == null && filterValue != null) { matches = false; } else if (defaultValue != null && !defaultValue.equals(filterValue)) { matches = false; } } if (matches) { optionalDirsMatching.add(dirInfo); } } // compute fulltext Set<String> dirFulltext = new HashSet<String>(); for (String sourceFieldName : fulltext) { final String fieldName = dirInfo.fromSource.get(sourceFieldName); if (fieldName != null) { dirFulltext.add(fieldName); } } // make query to subdirectory DocumentModelList l = dirInfo.getSession().query(dirFilter, dirFulltext, null, fetchReferences); for (DocumentModel entry : l) { final String id = entry.getId(); Map<String, Object> map = maps.get(id); if (map == null) { map = new HashMap<String, Object>(); maps.put(id, map); counts.put(id, 1); } else { counts.put(id, counts.get(id) + 1); } for (Entry<String, String> e : dirInfo.toSource.entrySet()) { map.put(e.getValue(), entry.getProperty(dirInfo.dirSchemaName, e.getKey())); } if (BaseSession.isReadOnlyEntry(entry)) { readOnlyEntries.add(id); } } } // add default entry values for optional dirs for (SubDirectoryInfo dirInfo : optionalDirsMatching) { // add entry for every data found in other dirs Set<String> existingIds = new HashSet<String>( dirInfo .getSession() .getProjection(Collections.<String, Serializable>emptyMap(), dirInfo.idField)); for (Entry<String, Map<String, Object>> result : maps.entrySet()) { final String id = result.getKey(); if (!existingIds.contains(id)) { counts.put(id, counts.get(id) + 1); final Map<String, Object> map = result.getValue(); for (Entry<String, String> e : dirInfo.toSource.entrySet()) { String value = e.getValue(); if (!map.containsKey(value)) { map.put(value, dirInfo.defaultEntry.get(e.getKey())); } } } } } // intersection, ignore entries not in all subdirectories final int numdirs = sourceInfo.subDirectoryInfos.size(); for (Iterator<String> it = maps.keySet().iterator(); it.hasNext(); ) { final String id = it.next(); if (counts.get(id) != numdirs) { it.remove(); } } // now create entries ((ArrayList<?>) results).ensureCapacity(results.size() + maps.size()); for (Entry<String, Map<String, Object>> e : maps.entrySet()) { final String id = e.getKey(); if (seen.containsKey(id)) { log.warn( String.format( "Entry '%s' is present in source '%s' but also in source '%s'. " + "The second one will be ignored.", id, seen.get(id), sourceInfo.source.name)); continue; } final Map<String, Object> map = e.getValue(); seen.put(id, sourceInfo.source.name); final DocumentModel entry = BaseSession.createEntryModel(null, schemaName, id, map, readOnlyEntries.contains(id)); results.add(entry); } } if (orderBy != null && !orderBy.isEmpty()) { directory.orderEntries(results, orderBy); } return results; }
/** Recomputes all the info needed for efficient access. */ private void recomputeSourceInfos() throws DirectoryException { final Schema schema = schemaManager.getSchema(schemaName); if (schema == null) { throw new DirectoryException( String.format("Directory '%s' has unknown schema '%s'", directory.getName(), schemaName)); } final Set<String> sourceFields = new HashSet<String>(); for (Field f : schema.getFields()) { sourceFields.add(f.getName().getLocalName()); } if (!sourceFields.contains(schemaIdField)) { throw new DirectoryException( String.format( "Directory '%s' schema '%s' has no id field '%s'", directory.getName(), schemaName, schemaIdField)); } List<SourceInfo> newSourceInfos = new ArrayList<SourceInfo>(2); for (SourceDescriptor source : descriptor.sources) { int ndirs = source.subDirectories.length; if (ndirs == 0) { throw new DirectoryException( String.format( "Directory '%s' source '%s' has no subdirectories", directory.getName(), source.name)); } final List<SubDirectoryInfo> subDirectoryInfos = new ArrayList<SubDirectoryInfo>(ndirs); SubDirectoryInfo authDirectoryInfo = null; boolean hasRequiredDir = false; for (SubDirectoryDescriptor subDir : source.subDirectories) { final String dirName = subDir.name; final String dirSchemaName = directoryService.getDirectorySchema(dirName); final String dirIdField = directoryService.getDirectoryIdField(dirName); final boolean dirIsAuth = directoryService.getDirectoryPasswordField(dirName) != null; final Map<String, String> fromSource = new HashMap<String, String>(); final Map<String, String> toSource = new HashMap<String, String>(); final Map<String, Serializable> defaultEntry = new HashMap<String, Serializable>(); final boolean dirIsOptional = subDir.isOptional; // XXX check authenticating final Schema dirSchema = schemaManager.getSchema(dirSchemaName); if (dirSchema == null) { throw new DirectoryException( String.format( "Directory '%s' source '%s' subdirectory '%s' " + "has unknown schema '%s'", directory.getName(), source.name, dirName, dirSchemaName)); } // record default field mappings if same name and record default // values final Set<String> dirSchemaFields = new HashSet<String>(); for (Field f : dirSchema.getFields()) { final String fieldName = f.getName().getLocalName(); dirSchemaFields.add(fieldName); if (sourceFields.contains(fieldName)) { // XXX check no duplicates! fromSource.put(fieldName, fieldName); toSource.put(fieldName, fieldName); } // XXX cast to Serializable defaultEntry.put(fieldName, (Serializable) f.getDefaultValue()); } // treat renamings // XXX id field ? for (FieldDescriptor field : subDir.fields) { final String sourceFieldName = field.forField; final String fieldName = field.name; if (!sourceFields.contains(sourceFieldName)) { throw new DirectoryException( String.format( "Directory '%s' source '%s' subdirectory '%s' " + "has mapping for unknown field '%s'", directory.getName(), source.name, dirName, sourceFieldName)); } if (!dirSchemaFields.contains(fieldName)) { throw new DirectoryException( String.format( "Directory '%s' source '%s' subdirectory '%s' " + "has mapping of unknown field' '%s'", directory.getName(), source.name, dirName, fieldName)); } fromSource.put(sourceFieldName, fieldName); toSource.put(fieldName, sourceFieldName); } SubDirectoryInfo subDirectoryInfo = new SubDirectoryInfo( dirName, dirSchemaName, dirIdField, dirIsAuth, fromSource, toSource, defaultEntry, dirIsOptional); subDirectoryInfos.add(subDirectoryInfo); if (dirIsAuth) { if (authDirectoryInfo != null) { throw new DirectoryException( String.format( "Directory '%s' source '%s' has two subdirectories " + "with a password field, '%s' and '%s'", directory.getName(), source.name, authDirectoryInfo.dirName, dirName)); } authDirectoryInfo = subDirectoryInfo; } if (!dirIsOptional) { hasRequiredDir = true; } } if (isAuthenticating() && authDirectoryInfo == null) { throw new DirectoryException( String.format( "Directory '%s' source '%s' has no subdirectory " + "with a password field", directory.getName(), source.name)); } if (!hasRequiredDir) { throw new DirectoryException( String.format( "Directory '%s' source '%s' only has optional subdirectories: " + "no directory can be used has a reference.", directory.getName(), source.name)); } newSourceInfos.add(new SourceInfo(source, subDirectoryInfos, authDirectoryInfo)); } sourceInfos = newSourceInfos; }
@Before public void setUp() throws Exception { // mem dir factory directoryService = Framework.getLocalService(DirectoryService.class); memoryDirectoryFactory = new MemoryDirectoryFactory(); directoryService.registerDirectory("memdirs", memoryDirectoryFactory); // create and register mem directories Map<String, Object> e; // dir 1 Set<String> schema1Set = new HashSet<String>(Arrays.asList("uid", "foo")); memdir1 = new MemoryDirectory("dir1", "schema1", schema1Set, "uid", "foo"); memoryDirectoryFactory.registerDirectory(memdir1); Session dir1 = memdir1.getSession(); e = new HashMap<String, Object>(); e.put("uid", "1"); e.put("foo", "foo1"); dir1.createEntry(e); e = new HashMap<String, Object>(); e.put("uid", "2"); e.put("foo", "foo2"); dir1.createEntry(e); // dir 2 Set<String> schema2Set = new HashSet<String>(Arrays.asList("id", "bar")); memdir2 = new MemoryDirectory("dir2", "schema2", schema2Set, "id", null); memoryDirectoryFactory.registerDirectory(memdir2); Session dir2 = memdir2.getSession(); e = new HashMap<String, Object>(); e.put("id", "1"); e.put("bar", "bar1"); dir2.createEntry(e); e = new HashMap<String, Object>(); e.put("id", "2"); e.put("bar", "bar2"); dir2.createEntry(e); // dir 3 Set<String> schema3Set = new HashSet<String>(Arrays.asList("uid", "thefoo", "thebar")); memdir3 = new MemoryDirectory("dir3", "schema3", schema3Set, "uid", "thefoo"); memoryDirectoryFactory.registerDirectory(memdir3); Session dir3 = memdir3.getSession(); e = new HashMap<String, Object>(); e.put("uid", "3"); e.put("thefoo", "foo3"); e.put("thebar", "bar3"); dir3.createEntry(e); e = new HashMap<String, Object>(); e.put("uid", "4"); e.put("thefoo", "foo4"); e.put("thebar", "bar4"); dir3.createEntry(e); // the multi directory multiDir = (MultiDirectory) directoryService.getDirectory("multi"); dir = (MultiDirectorySession) multiDir.getSession(); }