You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by he...@apache.org on 2007/12/21 17:03:49 UTC
svn commit: r606228 [3/4] - in /directory/sandbox/hennejg/odm/trunk/src: ./
main/ main/java/ main/java/org/ main/java/org/apache/
main/java/org/apache/directory/ main/java/org/apache/directory/odm/
main/java/org/apache/directory/odm/auth/ main/resource...
Added: directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/TypeMapping.java
URL: http://svn.apache.org/viewvc/directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/TypeMapping.java?rev=606228&view=auto
==============================================================================
--- directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/TypeMapping.java (added)
+++ directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/TypeMapping.java Fri Dec 21 08:03:46 2007
@@ -0,0 +1,1169 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *******************************************************************************/
+package org.apache.directory.odm;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import javax.naming.InvalidNameException;
+import javax.naming.Name;
+import javax.naming.NameAlreadyBoundException;
+import javax.naming.NameClassPair;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.BasicAttributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.ModificationItem;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The TypeMapping handles all mapping related tasks for one mapped class.
+ *
+ * @author levigo
+ */
+public class TypeMapping implements Cloneable {
+ private static final Logger logger = LoggerFactory
+ .getLogger(TypeMapping.class);
+
+ public enum SearchScope {
+ OBJECT(SearchControls.OBJECT_SCOPE), ONELEVEL(SearchControls.ONELEVEL_SCOPE), SUBTREE(
+ SearchControls.SUBTREE_SCOPE);
+
+ private final int sc;
+
+ SearchScope(int sc) {
+ this.sc = sc;
+ }
+
+ public int getScope() {
+ return sc;
+ }
+ }
+
+ /**
+ * The type's attributes.
+ */
+ protected List<AttributeMapping> attributes = new ArrayList<AttributeMapping>();
+
+ /**
+ * Other mapped attributes pointing back to this type. Not currently used.
+ */
+ private List<AttributeMapping> referrers = new ArrayList<AttributeMapping>();
+
+ /**
+ * The base RDN where objects of the type get stored by default. May be
+ * overridden by specifying a base RDN as an argument to the load/list/save
+ * methods.
+ */
+ private final String baseRDN;
+
+ /**
+ * This marker indicates an unchanged attribute, whatever the current value
+ * may be. Used by AttributeMappings to communicate an unchanged attribute
+ * during dehydration without having to know the current value.
+ */
+ protected static final Object ATTRIBUTE_UNCHANGED_MARKER = "�$%&/()==UNCHANGED";
+
+ /**
+ * The cached constructor for the type. Every mapped type must implement a
+ * public default constructor.
+ */
+ private Constructor constructor;
+
+ /**
+ * The parent Mapping.
+ */
+ private Mapping mapping;
+
+ /**
+ * The class of instances mapped by this mapping.
+ */
+ private final Class modelClass;
+
+ /**
+ * The key class of this type.
+ */
+ private final String keyClass;
+
+ /**
+ * The LDAP objectClasses used by elements of the type.
+ */
+ // private final String objectClasses[];
+ private String objectClasses[];
+
+ /**
+ * The (Java-) attribute which holds the DN.
+ */
+ private AttributeMapping dnAttribute;
+
+ /**
+ * The (Java-) attribute which holds the RDN.
+ */
+ private AttributeMapping rdnAttribute;
+
+ /**
+ * The search filter used to search for objects of the mapped type below the
+ * base dn. May be overridden by specifying a filter as an argument to the
+ * load method.
+ */
+ private final String searchFilter;
+ /**
+ * The scope to use when listing objects of the mapped type.
+ *
+ * @see SearchControls#setSearchScope(int)
+ */
+ // private SearchScope defaultScope = SearchScope.ONELEVEL;
+ private SearchScope defaultScope = SearchScope.SUBTREE;
+
+ /**
+ * The connection descriptor to use for this mapping.
+ */
+ private DirectoryFacade directoryFacade;
+
+ private Name defaultBaseName;
+
+ public TypeMapping(String className, String baseRDN, String searchFilter,
+ String objectClasses, String keyClass) throws Exception {
+ try {
+ this.modelClass = Class.forName(className);
+ this.baseRDN = baseRDN;
+ this.searchFilter = searchFilter;
+ this.objectClasses = null != objectClasses ? objectClasses
+ .split("\\s*,\\s*") : new String[]{};
+ this.keyClass = keyClass;
+ } catch (Exception e) {
+ logger.error("Can't construct TypeMapping for class " + className, e);
+ throw e;
+ }
+ }
+
+ /**
+ * @param mapping
+ * @throws NoSuchMethodException
+ */
+ public void add(AttributeMapping attributeMapping) {
+ attributeMapping.setTypeMapping(this);
+ attributes.add(attributeMapping);
+ }
+
+ /**
+ * @return
+ * @throws DirectoryException
+ * @throws InvocationTargetException
+ * @throws IllegalAccessException
+ * @throws InstantiationException
+ * @throws NoSuchMethodException
+ * @throws IllegalArgumentException
+ * @throws SecurityException
+ */
+ public Object create() throws DirectoryException {
+ try {
+ final Object instance = createInstance();
+
+ rdnAttribute.initNewInstance(instance);
+ for (final AttributeMapping am : attributes)
+ am.initNewInstance(instance);
+
+ return instance;
+ } catch (final Exception e) {
+ throw new DirectoryException("Can't create instance of " + modelClass);
+ }
+ }
+
+ /**
+ * Create an empty object instance for this mapped type.
+ *
+ * @return new, unhydrated instance
+ * @throws Exception
+ */
+ private Object createInstance() throws Exception {
+ final Constructor c = getConstructor();
+ final Object newInstance = c.newInstance(new Object[]{});
+
+ return newInstance;
+ }
+
+ /**
+ * Create and hydrate an object instance from a set of attributes.
+ *
+ * @param dn object's DN
+ * @param a attributes used to hydrate the object
+ * @param tx current transaction
+ * @return
+ * @throws Exception
+ */
+ Object createInstanceFromAttributes(String dn, Attributes a, Transaction tx)
+ throws Exception {
+ final Object o = createInstance();
+ setDN(dn, o);
+ hydrateInstance(a, o, tx);
+
+ return o;
+ }
+
+ /**
+ * Hydrate an object instance from a set of attributes.
+ *
+ * @param a attributes used to hydrate the object
+ * @param o object to hydrate
+ * @param tx current transaction
+ * @throws DirectoryException
+ */
+ private void hydrateInstance(Attributes a, Object o, Transaction tx)
+ throws DirectoryException {
+ // map RDN
+ rdnAttribute.hydrate(o, a, tx);
+
+ // map all other attributes
+ for (final Object element : attributes) {
+ final AttributeMapping am = (AttributeMapping) element;
+ am.hydrate(o, a, tx);
+ }
+ }
+
+ /**
+ * @param dn
+ * @param o
+ * @return
+ * @throws DirectoryException
+ */
+ private Object setDN(String dn, Object o) throws DirectoryException {
+ return dnAttribute.setValue(o, dn);
+ }
+
+ /**
+ * @return
+ */
+ public String getBaseRDN() {
+ return baseRDN;
+ }
+
+ /**
+ * @return
+ * @throws NoSuchMethodException
+ * @throws SecurityException
+ */
+ private Constructor getConstructor() throws SecurityException,
+ NoSuchMethodException {
+ if (null == constructor)
+ constructor = modelClass.getConstructor(new Class[]{});
+ return constructor;
+ }
+
+ public Mapping getMapping() {
+ return mapping;
+ }
+
+ /**
+ * @return
+ */
+ public Class getMappedType() {
+ return modelClass;
+ }
+
+ /**
+ * List object of the mapped type for the given base DN, search filter and
+ * scope.
+ *
+ * @param filter the search filter
+ * @param searchBase the search base DN
+ * @param scope the search scope
+ * @param tx the current transaction
+ * @return
+ * @throws DirectoryException
+ */
+ public Set list(Filter filter, String searchBase, SearchScope scope,
+ Transaction tx) throws DirectoryException {
+ try {
+ final DirContext ctx = tx.getContext(directoryFacade);
+
+ // construct filter. if filter is set, join this type's filter with
+ // the supplied one.
+ String applicableFilter = searchFilter;
+ Object args[] = null;
+
+ if (null != filter) {
+ applicableFilter = "(&" + searchFilter + filter.getExpression(0) + ")";
+ args = filter.getArgs();
+
+ // FIXME: use Apache DS filter parser to properly upper-case the search
+ // expression
+
+ // if (directoryFacade.guessDirectoryType()
+ // .requiresUpperCaseRDNAttributeNames())
+ // // ...
+ }
+
+ // the dn will frequently be a descendant of the ctx's name. If this
+ // is the case, the prefix is removed, because search() expects
+ // a relative name.
+ if (null == searchBase)
+ searchBase = null != baseRDN ? baseRDN : "";
+
+ final Name searchBaseName = directoryFacade.makeRelativeName(searchBase);
+
+ // we want or results to carry absolute names. This is where
+ // they are rooted.
+ final Name resultBaseName = directoryFacade.makeAbsoluteName(searchBase);
+
+ if (logger.isDebugEnabled())
+ logger.debug("listing objects of " + modelClass + " for base="
+ + searchBaseName + ", filter=" + filter);
+
+ final SearchControls sc = new SearchControls();
+ sc.setSearchScope(null != scope ? scope.getScope() : defaultScope
+ .getScope());
+
+ final Set results = new HashSet();
+ try {
+ NamingEnumeration<SearchResult> ne;
+
+ DiropLogger.LOG.logSearch(searchBase, applicableFilter, args, sc,
+ "list objects");
+
+ ne = ctx.search(searchBaseName, applicableFilter, args, sc);
+
+ try {
+ while (ne.hasMore()) {
+ final SearchResult result = ne.next();
+
+ // we want an absolute element name. Unfortunately,
+ // result.getNameInNamespace() is 1.5+ only, so we've
+ // got to work this out ourselves.
+ Name elementName = directoryFacade.getNameParser().parse(
+ result.getName());
+
+ // FIX for A-DS bug: name isn't relative but should be.
+ if (result.isRelative() && !elementName.startsWith(resultBaseName))
+ elementName = elementName.addAll(0, resultBaseName);
+
+ // got it in the tx cache?
+ Object instance = tx.getCacheEntry(elementName);
+ if (null == instance) {
+ final Attributes a = result.getAttributes();
+ instance = createInstanceFromAttributes(elementName.toString(),
+ a, tx);
+
+ // cache the object
+ tx.putCacheEntry(this, elementName, instance, a);
+ }
+
+ results.add(instance);
+ }
+ } finally {
+ // close the enumeration before cascading the load.
+ ne.close();
+ }
+
+ for (final Object o : results)
+ for (final AttributeMapping am : attributes)
+ // for (AttributeMapping am : attrs) {
+ am.cascadePostLoad(o, tx);
+
+ } catch (final NameNotFoundException e) {
+ logger.warn("NameNotFoundException listing objects of " + modelClass
+ + " for base=" + searchBaseName + ". Returning empty set instead.");
+ }
+ return results;
+
+ } catch (final Exception e) {
+ throw new DirectoryException("Can't list objects for type " + modelClass,
+ e);
+ }
+ }
+
+ /**
+ * Load an object of the mapped type from the given DN.
+ *
+ * @param tx the current transaction
+ * @param dn the object's DN
+ * @throws NamingException
+ * @throws DirectoryException
+ */
+ public Object load(String dn, Transaction tx) throws DirectoryException {
+ try {
+ if (null == dn)
+ dn = baseRDN;
+
+ // make the dn absolute, even if it was relative.
+ final DirContext ctx = tx.getContext(directoryFacade);
+
+ final Name targetName = directoryFacade.makeAbsoluteName(dn);
+
+ // got it in the tx cache?
+ final Object cached = tx.getCacheEntry(targetName);
+ if (null != cached)
+ return cached;
+
+ // seems like we've got to load it.
+ if (logger.isDebugEnabled())
+ logger.debug("loading object of " + modelClass + " for dn: "
+ + targetName);
+
+ // FIXME: use lookup() instead of search
+ final SearchControls sc = new SearchControls();
+ sc.setSearchScope(SearchControls.OBJECT_SCOPE);
+
+ Object o = null;
+
+ // search() expects a base name relative to the ctx.
+ final Name searchName = directoryFacade.makeRelativeName(dn);
+
+ DiropLogger.LOG.logSearch(dn, searchFilter, null, sc,
+ "loading single object");
+
+ final NamingEnumeration<SearchResult> ne = ctx.search(searchName,
+ searchFilter, null, sc);
+
+ try {
+ if (!ne.hasMore())
+ throw new NameNotFoundException("No object for the given dn found.");
+
+ final SearchResult result = ne.nextElement();
+
+ if (ne.hasMore())
+ // scope=OBJECT_SCOPE!
+ throw new DirectoryException("More than one result return for query");
+
+ final Attributes a = result.getAttributes();
+ o = createInstanceFromAttributes(targetName.toString(), a, tx);
+
+ tx.putCacheEntry(this, targetName, o, a);
+ } finally {
+ // close the enumeration before cascading the load.
+ ne.close();
+ }
+
+ for (final AttributeMapping am : attributes)
+ am.cascadePostLoad(o, tx);
+
+ return o;
+ } catch (final Exception e) {
+ throw new DirectoryException("Can't load object", e);
+ }
+ }
+
+ /**
+ * @param o
+ * @param transaction
+ * @param baseDN2
+ * @throws DirectoryException
+ */
+ public void save(Object o, String baseDN, Transaction tx)
+ throws DirectoryException {
+ assert o.getClass().equals(modelClass);
+
+ if (getDirectoryFacade().isReadOnly())
+ throw new DirectoryException("Directory for " + o + " is read only");
+
+ // break cycles
+ if (tx.didAlreadyProcessEntity(o))
+ return;
+ tx.addEntity(o);
+
+ try {
+ final DirContext ctx = tx.getContext(directoryFacade);
+
+ // if the object has already got a DN set, we update it. Otherwise
+ // we save a new one.
+ final String dn = getDN(o);
+ Name name = null;
+ if (null == dn)
+ try {
+ saveNewObject(o, ctx, baseDN, tx);
+
+ return;
+ } catch (final NameAlreadyBoundException e) {
+ // The object's dn wasn't set. However, its
+ // RDN may have pointed to an existing object.
+ // Fall through to update mode.
+ name = fillEmptyDN(o, ctx, baseDN);
+ if (logger.isDebugEnabled())
+ logger
+ .debug("Caught NameAlreadyBoundException on saveNewObject for "
+ + name + ". trying update instead.");
+ }
+
+ // if the target name wasn't provided by the fall-through above,
+ // build
+ // it based on the object's dn attribute.
+ // the dn will frequently be a descendant of the ctx's name. If this
+ // is the case, the prefix is removed.
+ if (null == name)
+ name = directoryFacade.makeRelativeName(dn);
+
+ try {
+ DiropLogger.LOG.logGetAttributes(name, null, "save object");
+
+ final Attributes currentAttributes = ctx.getAttributes(name);
+ updateObject(o, ctx, name, currentAttributes, tx);
+ return;
+ } catch (final NameNotFoundException e) {
+ throw new DirectoryException("Object to be updated no longer exists");
+ }
+ } catch (final DirectoryException e) {
+ throw e;
+ } catch (final Exception e) {
+ throw new DirectoryException("Can't save object", e);
+ }
+ }
+
+ /**
+ * @param o
+ * @param ctx
+ * @param baseDN
+ * @param tx
+ * @throws NamingException
+ * @throws DirectoryException
+ * @throws InvalidNameException
+ * @throws NamingException
+ * @throws DirectoryException
+ */
+ private void saveNewObject(Object o, DirContext ctx, String baseDN,
+ Transaction tx) throws InvalidNameException, DirectoryException,
+ NamingException {
+ final Name targetName = fillEmptyDN(o, ctx, baseDN);
+
+ // perform cascading of stuff which has to be done before the new object
+ // can been saved.
+ for (final AttributeMapping attributeMapping : attributes)
+ attributeMapping.cascadePreSave(o, tx);
+
+ final BasicAttributes a = new BasicAttributes();
+ rdnAttribute.dehydrate(o, a);
+
+ fillAttributes(o, a);
+
+ DiropLogger.LOG.logAdd(getDN(o), a, "save new object");
+ ctx.bind(targetName, null, a);
+
+ // cache new object
+ tx.putCacheEntry(this, getDirectoryFacade().makeAbsoluteName(targetName),
+ o, a);
+
+ // perform cascading of stuff which has to be done after the new object
+ // has been saved.
+ try {
+ for (final AttributeMapping attributeMapping : attributes)
+ attributeMapping.cascadePostSave(o, tx, ctx);
+ } catch (final DirectoryException t) {
+ // rollback. FIXME: use transaction to do the dirty work
+ try {
+ DiropLogger.LOG.logDelete(targetName, "delete due to rollback");
+ ctx.destroySubcontext(targetName);
+ } catch (final Throwable u) {
+ // ignore
+ }
+ throw t;
+ }
+ }
+
+ /**
+ * @param o
+ * @param ctx
+ * @param baseDN
+ * @return
+ * @throws DirectoryException
+ * @throws NamingException
+ * @throws InvalidNameException
+ */
+ private Name fillEmptyDN(Object o, DirContext ctx, String baseDN)
+ throws DirectoryException, NamingException, InvalidNameException {
+ // the dn will frequently be a descendant of the ctx's name. If this
+ // is the case, the prefix is removed.
+
+ if (null == baseDN)
+ baseDN = this.baseRDN;
+
+ if (null == baseDN && getDirectoryFacade().isReadOnly())
+ baseDN = "";
+
+ if (null == baseDN)
+ throw new DirectoryException(
+ "Can't save object: don't know where to save it to");
+
+ final Name name = directoryFacade.makeRelativeName(baseDN);
+
+ final Object rdnValue = rdnAttribute.getValue(o);
+ if (null == rdnValue)
+ throw new DirectoryException(
+ "Can't save new instance: attribute for RDN (" + rdnAttribute
+ + ") not set.");
+
+ // add rdn
+ name.addAll(directoryFacade.getNameParser().parse(
+ rdnAttribute.fieldName + "=" + rdnValue));
+
+ setDN(directoryFacade.makeAbsoluteName(name).toString(), o);
+
+ return name;
+ }
+
+ /**
+ * @param mapping
+ */
+ void setMapping(Mapping mapping) {
+ if (null != this.mapping)
+ this.mapping.remove(this);
+
+ this.mapping = mapping;
+ }
+
+ /**
+ * @param mapping
+ * @throws NoSuchMethodException
+ */
+ public void setRDNAttribute(AttributeMapping rdnAttribute) {
+ if (!dnAttribute.getFieldType().equals(String.class))
+ throw new IllegalArgumentException(
+ "The RDN Attribute must be of type string");
+
+ rdnAttribute.setTypeMapping(this);
+ this.rdnAttribute = rdnAttribute;
+ }
+
+ public AttributeMapping getRDNAttribute() {
+ return this.rdnAttribute;
+ }
+
+ /*
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "[TypeMapping class=" + modelClass + ", baseDN=" + baseRDN
+ + ", filter=" + searchFilter + "]";
+ }
+
+ /**
+ * @param o
+ * @param ctx
+ * @param targetName
+ * @param currentAttributes
+ * @param tx
+ * @throws DirectoryException
+ */
+ private void updateObject(Object o, DirContext ctx, Name targetName,
+ Attributes currentAttributes, Transaction tx) throws DirectoryException,
+ NamingException {
+ if (getDirectoryFacade().isReadOnly())
+ throw new DirectoryException("Directory for " + o + " is read only");
+
+ Name targetDN = getDirectoryFacade().makeAbsoluteName(targetName);
+
+ tx.purgeCacheEntry(targetDN);
+
+ try {
+ final BasicAttributes newAttributes = new BasicAttributes();
+
+ final Object rdn = rdnAttribute.dehydrate(o, newAttributes);
+ if (null == rdn)
+ throw new DirectoryException("Can't save new instance: "
+ + "attribute for RDN (" + rdnAttribute + ") not set.");
+
+ if (!rdn.equals(currentAttributes.get(rdnAttribute.fieldName).get())) {
+ // ok, go for a rename!
+ targetName = renameObject(targetName, ctx, rdn, o, tx, newAttributes);
+
+ // dn has changed as well...
+ targetDN = getDirectoryFacade().makeAbsoluteName(targetName);
+ }
+
+ fillAttributes(o, newAttributes);
+
+ final List<ModificationItem> mods = new LinkedList<ModificationItem>();
+
+ // remove cleared Attributes
+ if (currentAttributes.size() > 0) {
+ final Attributes clearedAttributes = getClearedAttributes(
+ (BasicAttributes) newAttributes.clone(),
+ (Attributes) currentAttributes.clone());
+
+ if (clearedAttributes.size() > 0) {
+ final NamingEnumeration<Attribute> enmAttribute = (NamingEnumeration<Attribute>) clearedAttributes
+ .getAll();
+
+ while (enmAttribute.hasMore()) {
+ final Attribute clearedAttribute = enmAttribute.next();
+ mods.add(new ModificationItem(DirContext.REMOVE_ATTRIBUTE,
+ clearedAttribute));
+
+ if (logger.isDebugEnabled())
+ logger.debug("The value of following Attribute will be cleared: "
+ + clearedAttribute);
+ }
+ }
+ }
+
+ // updates, adds
+ final NamingEnumeration<Attribute> ne = newAttributes.getAll();
+ try {
+ while (ne.hasMore()) {
+ final Attribute newValues = ne.next();
+
+ final String id = newValues.getID();
+ if (id.equalsIgnoreCase("objectclass")) {
+ currentAttributes.remove(id);
+ continue;
+ }
+
+ updateAttributes(currentAttributes, currentAttributes.get(id),
+ newValues, mods, o);
+ }
+ } finally {
+ ne.close();
+ }
+
+ // execute the modifications
+ if (mods.size() > 0) {
+ final ModificationItem mi[] = mods.toArray(new ModificationItem[mods
+ .size()]);
+ DiropLogger.LOG.logModify(targetName, mi, "update object");
+ ctx.modifyAttributes(targetName, mi);
+ }
+
+ tx.putCacheEntry(this, targetDN, o, newAttributes);
+
+ // perform cascading of stuff which has to be done after the new
+ // object has been saved.
+ for (final AttributeMapping attributeMapping : attributes)
+ attributeMapping.cascadePostSave(o, tx, ctx);
+ } catch (final DirectoryException e) {
+ throw e;
+ } catch (final Throwable e) {
+ throw new DirectoryException("Can't marshal instance of " + modelClass, e);
+ }
+ }
+
+ private Name renameObject(Name oldName, DirContext ctx, Object rdn, Object o,
+ Transaction tx, BasicAttributes attrib) throws NamingException,
+ DirectoryException {
+ final Name newName = oldName.getPrefix(oldName.size() - 1).add(
+ rdnAttribute.fieldName + "=" + rdn);
+
+ final String oldDN = getDN(o);
+ final String newDN = ((Name) getDirectoryFacade().getBaseDNName().clone())
+ .addAll(newName).toString();
+
+ DiropLogger.LOG.logModRDN(oldName, newName, "rename object");
+
+ ctx.rename(oldName, newName);
+ getMapping().updateReferences(tx, oldDN, newDN);
+
+ // and tell the object about the new dn
+ setDN(newDN, o);
+
+ // perform cascading of stuff which has to be done after the
+ // new object has been saved.
+ for (final AttributeMapping attributeMapping : attributes)
+ attributeMapping.cascadeRDNChange(o, oldDN, newDN, tx);
+
+ // let the rdn attribute alone!
+ attrib.remove(rdnAttribute.fieldName);
+
+ return oldName;
+ }
+
+ protected void updateAttributes(Attributes currentAttributes,
+ Attribute currentValues, Attribute newValues,
+ List<ModificationItem> mods, Object o) throws NamingException,
+ DirectoryException {
+ final String id = newValues.getID();
+ if (currentValues != null) {
+ // use identity comparison for unchanged marker!
+ if (newValues.size() == 1
+ && newValues.get(0) == ATTRIBUTE_UNCHANGED_MARKER)
+ currentAttributes.remove(id);
+ else {
+ if (!areAttributesEqual(newValues, currentValues))
+ mods
+ .add(new ModificationItem(DirContext.REPLACE_ATTRIBUTE, newValues));
+ currentAttributes.remove(id);
+ }
+ } else if (currentValues == null && newValues != null)
+ mods.add(new ModificationItem(DirContext.ADD_ATTRIBUTE, newValues));
+ }
+
+ private Attributes getClearedAttributes(BasicAttributes nowAttributes,
+ Attributes ldapAttributes) throws NamingException {
+
+ final Attributes cleared = new BasicAttributes();
+
+ // ignore objectClasses
+ nowAttributes.remove("objectClass");
+ ldapAttributes.remove("objectClass");
+
+ final NamingEnumeration<String> nowIDs = nowAttributes.getIDs();
+
+ while (nowIDs.hasMore()) {
+ final String id = nowIDs.next();
+ ldapAttributes.remove(id);
+ }
+
+ final Set<String> attr = new HashSet<String>();
+
+ for (final AttributeMapping am : attributes) {
+ if (am instanceof ManyToManyMapping)
+ continue;
+ attr.add(am.fieldName);
+ }
+
+ final NamingEnumeration<String> ldapIDs = ldapAttributes.getIDs();
+
+ while (ldapIDs.hasMore()) {
+ final String id = ldapIDs.next();
+ for (final String rightID : attr)
+ if (rightID.equalsIgnoreCase(id))
+ cleared.put(ldapAttributes.get(id));
+ }
+
+ return cleared;
+ }
+
+ /**
+ * @param a1
+ * @param a2
+ * @return
+ * @throws NamingException
+ */
+ private boolean areAttributesEqual(Attribute a1, Attribute a2)
+ throws NamingException {
+ if (!a1.getID().equalsIgnoreCase(a2.getID()))
+ return false;
+
+ if (a1.get() == null && a2.get() == null)
+ return true;
+
+ if (a1.get() == null || a2.get() == null)
+ return false;
+
+ if (a1.size() != a2.size())
+ return false;
+
+ for (int i = 0; i < a1.size(); i++)
+ if (a1.get() instanceof byte[])
+ return Arrays.equals((byte[]) a1.get(), (byte[]) a2.get());
+ else
+ return a1.get(i).equals(a2.get(i));
+ return true;
+ }
+
+ /**
+ * @param o
+ * @param a
+ * @throws DirectoryException
+ * @throws NamingException
+ */
+ private void fillAttributes(Object o, BasicAttributes a)
+ throws DirectoryException, NamingException {
+
+ // map all other attributes
+ for (final AttributeMapping attributeMapping : attributes)
+ attributeMapping.dehydrate(o, a);
+
+ // add object classes
+ final Attribute objectClassesAttribute = new BasicAttribute("objectClass");
+ for (final String oc : objectClasses)
+ objectClassesAttribute.add(oc);
+ a.put(objectClassesAttribute);
+ }
+
+ /**
+ * @param o
+ * @param transaction
+ * @return
+ * @throws DirectoryException
+ * @throws NamingException
+ */
+ public boolean delete(Object o, Transaction tx) throws DirectoryException {
+ if (getDirectoryFacade().isReadOnly())
+ throw new DirectoryException("Directory for " + o + " is read only");
+
+ // break cycles
+ if (tx.didAlreadyProcessEntity(o))
+ return true;
+ tx.addEntity(o);
+
+ final String dn = getDN(o);
+ if (null == dn)
+ throw new DirectoryException(
+ "Can't delete this object: no DN (mayby it wasn't saved before?)");
+ try {
+ final DirContext ctx = tx.getContext(directoryFacade);
+ // checkMutable(ctx);
+ // the dn will frequently be a descendant of the ctx's name. If
+ // this
+ // is the case, the prefix is removed.
+ final Name targetName = directoryFacade.makeRelativeName(dn);
+
+ // remove from cache
+ tx.purgeCacheEntry(targetName);
+
+ deleteRecursively(ctx, targetName, tx, "delete object");
+
+ getMapping().updateReferences(tx, dn, null);
+
+ try {
+ // perform cascading of stuff which has to be done after the
+ // object has been deleted.
+ for (final AttributeMapping attributeMapping : attributes)
+ attributeMapping.cascadeDelete(targetName, tx);
+ } catch (final DirectoryException e) {
+ logger.error("Exception during cascade post RDN change", e);
+ }
+ return true;
+
+ } catch (final NameNotFoundException e) {
+ logger.warn("Object to be deleted was not actually found.");
+ return false;
+ } catch (final Exception e) {
+ throw new DirectoryException("Can't load object", e);
+ }
+ }
+
+ /**
+ * @param ctx
+ * @param targetName
+ * @param tx
+ * @param comment TODO
+ * @throws NamingException
+ */
+ static void deleteRecursively(DirContext ctx, Name targetName,
+ Transaction tx, String comment) throws NamingException {
+
+ final NamingEnumeration<NameClassPair> children = ctx.list(targetName);
+ try {
+ while (children.hasMore()) {
+ final NameClassPair child = children.next();
+ targetName.add(child.getName());
+ deleteRecursively(ctx, targetName, tx, "delete recursively");
+ targetName.remove(targetName.size() - 1);
+ }
+ } finally {
+ children.close();
+ }
+
+ DiropLogger.LOG.logDelete(targetName, comment);
+ try {
+ ctx.destroySubcontext(targetName);
+ } catch (final Exception e) {
+ }
+
+ }
+
+ /**
+ *
+ */
+ protected void initPostLoad() {
+ for (final AttributeMapping am : attributes)
+ am.initPostLoad();
+ }
+
+ /**
+ * @param mapping
+ */
+ void addReferrer(AttributeMapping mapping) {
+ referrers.add(mapping);
+ }
+
+ public AttributeMapping getDNAttribute() {
+ return dnAttribute;
+ }
+
+ public void setDNAttribute(AttributeMapping dnAttribute) {
+ if (!dnAttribute.getFieldType().equals(String.class))
+ throw new IllegalArgumentException(
+ "The DN Attribute must be of type string");
+
+ this.dnAttribute = dnAttribute;
+ dnAttribute.setTypeMapping(this);
+ }
+
+ /**
+ * @param o
+ * @return
+ * @throws DirectoryException
+ */
+ String getDN(Object o) throws DirectoryException {
+ return (String) dnAttribute.getValue(o);
+ }
+
+ public String[] getObjectClasses() {
+ return objectClasses;
+ }
+
+ /**
+ * Refresh the given object's state from the directory.
+ *
+ * @param o the object to refresh.
+ * @param tx the current transaction
+ * @throws DirectoryException
+ */
+ public void refresh(Object o, Transaction tx) throws DirectoryException {
+ try {
+ final DirContext ctx = tx.getContext(directoryFacade);
+ final String dn = getDN(o);
+ try {
+ final Name targetName = directoryFacade.makeRelativeName(dn);
+
+ if (logger.isDebugEnabled())
+ logger.debug("refreshing object of " + modelClass + " for dn: " + dn);
+
+ DiropLogger.LOG.logGetAttributes(dn, null, "refresh object");
+
+ final Attributes a = ctx.getAttributes(targetName);
+ hydrateInstance(a, o, tx);
+
+ for (final AttributeMapping am : attributes)
+ am.cascadePostLoad(o, tx);
+
+ // update object in cache, no matter what
+ final Name absoluteName = directoryFacade.makeAbsoluteName(dn);
+ tx.putCacheEntry(this, absoluteName, o, a);
+
+ } catch (final NameNotFoundException n) {
+ throw new DirectoryException("Can't refresh " + dn
+ + ": object doesn't exist (any longer?)");
+ }
+ } catch (final DirectoryException e) {
+ throw e;
+ } catch (final Exception e) {
+ throw new DirectoryException("Can't refresh object", e);
+ }
+ }
+
+ public void setScope(String scope) {
+ this.defaultScope = SearchScope.valueOf(scope);
+ }
+
+ public void setScope(SearchScope scope) {
+ this.defaultScope = scope;
+ }
+
+ /*
+ * @see java.lang.Object#clone()
+ */
+ @Override
+ protected TypeMapping clone() throws CloneNotSupportedException {
+ final TypeMapping clone = (TypeMapping) super.clone();
+
+ clone.referrers = new ArrayList<AttributeMapping>();
+ clone.attributes = new ArrayList<AttributeMapping>();
+
+ for (final AttributeMapping am : attributes) {
+ final AttributeMapping clonedAM = am.clone();
+ clone.add(clonedAM);
+ }
+
+ return clone;
+ }
+
+ public String getSearchFilter() {
+ return searchFilter;
+ }
+
+ public void setObjectClasses(String[] objectClasses) {
+ this.objectClasses = objectClasses;
+ }
+
+ public String getKeyClass() {
+ return keyClass;
+ }
+
+ void setDirectoryFacade(DirectoryFacade lcd) {
+ this.directoryFacade = lcd;
+ }
+
+ DirectoryFacade getDirectoryFacade() {
+ return directoryFacade;
+ }
+
+ public boolean matchesKeyClasses(Attribute objectClasses)
+ throws NamingException {
+ if (null == keyClass)
+ return false;
+
+ for (final NamingEnumeration<String> ne = (NamingEnumeration<String>) objectClasses
+ .getAll(); ne.hasMore();)
+ if (ne.next().equalsIgnoreCase(keyClass))
+ return true;
+
+ return false;
+ }
+
+ public Name getDefaultBaseName() throws InvalidNameException, NamingException {
+ if (null == defaultBaseName) {
+ final Name baseDNName = (Name) directoryFacade.getBaseDNName().clone();
+ defaultBaseName = baseDNName.add(getBaseRDN());
+ }
+
+ return defaultBaseName;
+ }
+
+ protected void collectRefererAttributes(
+ Set<ReferenceAttributeMapping> refererAttributes) {
+ for (final AttributeMapping am : attributes)
+ if (am instanceof ReferenceAttributeMapping)
+ refererAttributes.add((ReferenceAttributeMapping) am);
+ }
+
+ /**
+ * Handle cases where the object's parent DN changed somewhere up the tree.
+ *
+ * @param o the persistent object of which we need to up-date the name
+ * @param oldName the old name prefix
+ * @param newName the new name prefix
+ * @param tx the current transaction
+ * @throws DirectoryException
+ * @throws NamingException
+ */
+ void handleParentNameChange(Object o, String oldDN, String newDN,
+ Transaction tx) throws DirectoryException, NamingException {
+ final String dn = getDN(o);
+
+ // if the dn is null, the object is still transient and we don't have to
+ // worry
+ if (null != dn)
+ if (dn.endsWith(oldDN))
+ // update DN
+ setDN(dn.substring(0, dn.length() - oldDN.length()) + newDN, o);
+ else
+ logger.warn("Unexpected state during parent DN change: object's dn "
+ + dn + " doesn't start with " + oldDN);
+
+ // perform cascading of stuff which has to be done after the
+ // new object has been saved.
+ for (final AttributeMapping attributeMapping : attributes)
+ attributeMapping.cascadeRDNChange(o, oldDN, newDN, tx);
+ }
+}
Propchange: directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/TypeMapping.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/UnmodifiableHashtable.java
URL: http://svn.apache.org/viewvc/directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/UnmodifiableHashtable.java?rev=606228&view=auto
==============================================================================
--- directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/UnmodifiableHashtable.java (added)
+++ directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/UnmodifiableHashtable.java Fri Dec 21 08:03:46 2007
@@ -0,0 +1,148 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *******************************************************************************/
+/**
+ *
+ */
+package org.apache.directory.odm;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+/**
+ * An unmodifiable view of a {@link Hashtable}, implemented by delegating all
+ * read-only methods to the table backing the view, but throwing an
+ * {@link UnsupportedOperationException} on all mutator methods.
+ *
+ * This class sucks inherently, since {@link Hashtable} is a class instead of an
+ * interface. However, JNDI requires Hashtables for its environment properties
+ * instead of {@link Map}s. <em>sigh</em>.
+ *
+ * @author levigo
+ *
+ * @param <K> Key type
+ * @param <V> Value type
+ */
+class UnmodifiableHashtable<K, V> extends Hashtable<K, V> {
+ private static final long serialVersionUID = 1L;
+ private final Hashtable<K, V> delegate;
+
+ public UnmodifiableHashtable(Hashtable<K, V> m) {
+ this.delegate = m;
+ }
+
+ @Override
+ public synchronized void clear() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public synchronized V put(K key, V value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public synchronized void putAll(Map<? extends K, ? extends V> t) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public synchronized V remove(Object key) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Set<Entry<K, V>> entrySet() {
+ return Collections.unmodifiableSet(delegate.entrySet());
+ }
+
+ @Override
+ public Set<K> keySet() {
+ return Collections.unmodifiableSet(delegate.keySet());
+ }
+
+ @Override
+ public Collection<V> values() {
+ return Collections.unmodifiableCollection(delegate.values());
+ }
+
+ @Override
+ public Object clone() {
+ return delegate.clone();
+ }
+
+ @Override
+ public boolean contains(Object value) {
+ return delegate.contains(value);
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ return delegate.containsKey(key);
+ }
+
+ @Override
+ public boolean containsValue(Object value) {
+ return delegate.containsValue(value);
+ }
+
+ @Override
+ public Enumeration<V> elements() {
+ return delegate.elements();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return delegate.equals(o);
+ }
+
+ @Override
+ public V get(Object key) {
+ return delegate.get(key);
+ }
+
+ @Override
+ public int hashCode() {
+ return delegate.hashCode();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return delegate.isEmpty();
+ }
+
+ @Override
+ public Enumeration<K> keys() {
+ return delegate.keys();
+ }
+
+ @Override
+ public int size() {
+ return delegate.size();
+ }
+
+ @Override
+ public String toString() {
+ return delegate.toString();
+ }
+}
\ No newline at end of file
Propchange: directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/UnmodifiableHashtable.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/Util.java
URL: http://svn.apache.org/viewvc/directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/Util.java?rev=606228&view=auto
==============================================================================
--- directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/Util.java (added)
+++ directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/Util.java Fri Dec 21 08:03:46 2007
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *******************************************************************************/
+package org.apache.directory.odm;
+
+import javax.naming.Name;
+import javax.naming.NameClassPair;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A bunch of static utility methods.
+ *
+ * @author levigo
+ */
+public class Util {
+ private static final Logger logger = LoggerFactory.getLogger(Util.class);
+
+ /**
+ * Recursively delete a tree at/below a given {@link Name}.
+ *
+ * @param ctx
+ * @param targetName
+ * @param tx
+ * @throws NamingException
+ */
+ public static void deleteRecursively(DirContext ctx, Name targetName)
+ throws NamingException {
+
+ final NamingEnumeration<NameClassPair> children = ctx.list(targetName);
+ try {
+ while (children.hasMore()) {
+ final NameClassPair child = children.next();
+ targetName.add(child.getName());
+ deleteRecursively(ctx, targetName);
+ targetName.remove(targetName.size() - 1);
+ }
+ } finally {
+ children.close();
+ }
+
+ if (logger.isDebugEnabled())
+ logger.debug("destroySubcontext: " + targetName);
+ try {
+ ctx.destroySubcontext(targetName);
+ } catch (final Exception e) {
+ }
+ }
+
+ /**
+ * @param schema
+ * @param sc
+ * @throws NamingException
+ */
+ public static boolean hasObjectClass(DirContext schema, String className)
+ throws NamingException {
+ final SearchControls sc = new SearchControls();
+ sc.setSearchScope(SearchControls.OBJECT_SCOPE);
+ try {
+ schema.list("ClassDefinition/" + className);
+ return true;
+ } catch (final NameNotFoundException e) {
+ return false;
+ }
+ }
+}
Propchange: directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/Util.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/auth/CachingCallbackHandler.java
URL: http://svn.apache.org/viewvc/directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/auth/CachingCallbackHandler.java?rev=606228&view=auto
==============================================================================
--- directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/auth/CachingCallbackHandler.java (added)
+++ directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/auth/CachingCallbackHandler.java Fri Dec 21 08:03:46 2007
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *******************************************************************************/
+package org.apache.directory.odm.auth;
+
+import java.io.IOException;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+/**
+ * @author levigo
+ */
+public interface CachingCallbackHandler extends CallbackHandler {
+ void purgeCache() throws IOException, UnsupportedCallbackException;
+}
Propchange: directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/auth/CachingCallbackHandler.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/auth/UsernamePasswordHandler.java
URL: http://svn.apache.org/viewvc/directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/auth/UsernamePasswordHandler.java?rev=606228&view=auto
==============================================================================
--- directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/auth/UsernamePasswordHandler.java (added)
+++ directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/auth/UsernamePasswordHandler.java Fri Dec 21 08:03:46 2007
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *******************************************************************************/
+/**
+ *
+ */
+package org.apache.directory.odm.auth;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+public class UsernamePasswordHandler implements CallbackHandler {
+
+ private transient final String username;
+ private transient char[] password;
+ private transient final Object credential;
+
+ public UsernamePasswordHandler(String username, char[] password) {
+ super();
+ this.username = username;
+ this.password = password;
+ this.credential = password;
+ }
+
+ public UsernamePasswordHandler(String username, Object credential) {
+ super();
+ this.username = username;
+ this.credential = credential;
+ }
+
+ public void handle(Callback[] callbacks) throws UnsupportedCallbackException {
+
+ for (int i = 0; i < callbacks.length; i++) {
+ Callback c = callbacks[i];
+ if (c instanceof NameCallback) {
+ NameCallback nc = (NameCallback) c;
+ nc.setName(username);
+ } else if (c instanceof PasswordCallback) {
+ PasswordCallback pc = (PasswordCallback) c;
+ if (password == null) {
+ if (credential != null) {
+ String tmp = credential.toString();
+ password = tmp.toCharArray();
+ }
+ }
+
+ pc.setPassword(password);
+ } else {
+ throw new UnsupportedCallbackException(callbacks[i],
+ "Unrecognized Callback");
+ }
+ }
+ }
+}
Propchange: directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/auth/UsernamePasswordHandler.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: directory/sandbox/hennejg/odm/trunk/src/main/resources/org/apache/directory/odm/ldap-mapping.xml
URL: http://svn.apache.org/viewvc/directory/sandbox/hennejg/odm/trunk/src/main/resources/org/apache/directory/odm/ldap-mapping.xml?rev=606228&view=auto
==============================================================================
--- directory/sandbox/hennejg/odm/trunk/src/main/resources/org/apache/directory/odm/ldap-mapping.xml (added)
+++ directory/sandbox/hennejg/odm/trunk/src/main/resources/org/apache/directory/odm/ldap-mapping.xml Fri Dec 21 08:03:46 2007
@@ -0,0 +1,202 @@
+<?xml version="1.0"?>
+<!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Object Mapping DTD Version 1.0//EN"
+ "http://castor.exolab.org/mapping.dtd">
+<mapping>
+ <class name="org.apache.directory.odm.Mapping" verify-constructable="false">
+ <map-to xml="mapping" />
+ <field name="name" set-method="setName" get-method="getName">
+ <bind-xml name="name" node="attribute" />
+ </field>
+ <field name="classes" type="org.apache.directory.odm.TypeMapping" collection="arraylist" set-method="add">
+ <bind-xml auto-naming="deriveByClass" node="element" />
+ </field>
+ </class>
+ <class name="org.apache.directory.odm.TypeMapping" verify-constructable="false">
+ <map-to xml="class" />
+ <field name="name" required="true" set-method="%1" type="string">
+ <bind-xml name="name" node="attribute" />
+ </field>
+ <field name="baseDN" required="true" set-method="%2" type="string">
+ <bind-xml name="base-rdn" node="attribute" />
+ </field>
+ <field name="searchFilter" required="true" set-method="%3" type="string">
+ <bind-xml name="filter" node="attribute" />
+ </field>
+ <field name="scope" required="true" type="string" set-method="setScope">
+ <bind-xml name="scope" node="attribute" />
+ </field>
+ <field name="objectClasses" required="true" set-method="%4" type="string">
+ <bind-xml name="object-classes" node="attribute" />
+ </field>
+ <field name="keyClass" required="true" set-method="%5" type="string">
+ <bind-xml name="key-class" node="attribute" />
+ </field>
+ <field name="DNAttribute" required="true" type="org.apache.directory.odm.AttributeMapping"
+ set-method="setDNAttribute">
+ <bind-xml name="dn-attribute" node="element" />
+ </field>
+ <field name="RDNAttribute" required="true" type="org.apache.directory.odm.RDNAttributeMapping"
+ set-method="setRDNAttribute">
+ <bind-xml name="rdn-attribute" node="element" />
+ </field>
+ <field name="attributes" type="org.apache.directory.odm.AttributeMapping" collection="arraylist" set-method="add">
+ <bind-xml name="attribute" node="element" />
+ </field>
+ <field name="manyToMany" type="org.apache.directory.odm.ManyToManyMapping" collection="arraylist" set-method="add">
+ <bind-xml name="many-to-many" node="element" />
+ </field>
+ <field name="manyToOne" type="org.apache.directory.odm.ManyToOneMapping" collection="arraylist" set-method="add">
+ <bind-xml name="many-to-one" node="element" />
+ </field>
+ <field name="children" type="org.apache.directory.odm.ChildMapping" collection="arraylist" set-method="add">
+ <bind-xml name="child" node="element" />
+ </field>
+ </class>
+ <class name="org.apache.directory.odm.GroupMapping" verify-constructable="false">
+ <map-to xml="group" />
+ <field name="name" required="true" set-method="%1" type="string">
+ <bind-xml name="name" node="attribute" />
+ </field>
+ <field name="baseRDN" required="true" set-method="%2" type="string">
+ <bind-xml name="base-rdn" node="attribute" />
+ </field>
+ <field name="searchFilter" required="true" set-method="%3" type="string">
+ <bind-xml name="filter" node="attribute" />
+ </field>
+ <field name="objectClasses" required="true" set-method="%4" type="string">
+ <bind-xml name="object-classes" node="attribute" />
+ </field>
+ <field name="keyClass" required="true" set-method="%5" type="string">
+ <bind-xml name="key-class" node="attribute" />
+ </field>
+ <field name="memberAttribute" required="true" set-method="%6" type="string">
+ <bind-xml name="member-attribute" node="attribute" />
+ </field>
+ <field name="DNAttribute" required="true" type="org.apache.directory.odm.AttributeMapping"
+ set-method="setDNAttribute">
+ <bind-xml name="dn-attribute" node="element" />
+ </field>
+ <field name="RDNAttribute" required="true" type="org.apache.directory.odm.RDNAttributeMapping"
+ set-method="setRDNAttribute">
+ <bind-xml name="rdn-attribute" node="element" />
+ </field>
+ <field name="members" type="org.apache.directory.odm.OneToManyMapping" collection="arraylist"
+ set-method="addMembers">
+ <bind-xml name="one-to-many" node="element" />
+ </field>
+ <field name="attributes" type="org.apache.directory.odm.AttributeMapping" collection="arraylist" set-method="add">
+ <bind-xml name="attribute" node="element" />
+ </field>
+ <field name="manyToMany" type="org.apache.directory.odm.ManyToManyMapping" collection="arraylist" set-method="add">
+ <bind-xml name="many-to-many" node="element" />
+ </field>
+ <field name="manyToOne" type="org.apache.directory.odm.ManyToOneMapping" collection="arraylist" set-method="add">
+ <bind-xml name="many-to-one" node="element" />
+ </field>
+ <field name="children" type="org.apache.directory.odm.ChildMapping" collection="arraylist" set-method="add">
+ <bind-xml name="child" node="element" />
+ </field>
+ </class>
+ <class name="org.apache.directory.odm.RDNAttributeMapping" verify-constructable="false">
+ <map-to xml="rdn-attribute" />
+ <field name="name" required="true" set-method="%1" type="string">
+ <bind-xml name="name" node="attribute" />
+ </field>
+ <field name="getMethod" required="false" set-method="setGetMethod" type="string">
+ <bind-xml name="get-method" node="attribute" />
+ </field>
+ <field name="setMethod" required="true" set-method="setSetMethod" type="string">
+ <bind-xml name="set-method" node="attribute" />
+ </field>
+ </class>
+ <class name="org.apache.directory.odm.AttributeMapping" verify-constructable="false">
+ <map-to xml="attribute" />
+ <field name="name" required="true" set-method="%1" type="string">
+ <bind-xml name="name" node="attribute" />
+ </field>
+ <field name="type" required="true" set-method="%2" type="string">
+ <bind-xml name="type" node="attribute" />
+ </field>
+ <field name="getMethod" required="false" set-method="setGetMethod" type="string">
+ <bind-xml name="get-method" node="attribute" />
+ </field>
+ <field name="setMethod" required="true" set-method="setSetMethod" type="string">
+ <bind-xml name="set-method" node="attribute" />
+ </field>
+ <field name="cardinality" required="false" set-method="setCardinality" type="string">
+ <bind-xml name="cardinality" node="attribute" />
+ </field>
+ </class>
+ <class name="org.apache.directory.odm.ManyToOneMapping" verify-constructable="false">
+ <map-to xml="many-to-one" />
+ <field name="name" required="true" set-method="%1" type="string">
+ <bind-xml name="name" node="attribute" />
+ </field>
+ <field name="type" required="true" set-method="%2" type="string">
+ <bind-xml name="type" node="attribute" />
+ </field>
+ <field name="getMethod" required="true" set-method="setGetMethod" type="string">
+ <bind-xml name="get-method" node="attribute" />
+ </field>
+ <field name="setMethod" required="true" set-method="setSetMethod" type="string">
+ <bind-xml name="set-method" node="attribute" />
+ </field>
+ </class>
+ <class name="org.apache.directory.odm.OneToManyMapping" verify-constructable="false">
+ <map-to xml="one-to-many" />
+ <field name="name" required="true" set-method="%1" type="string">
+ <bind-xml name="name" node="attribute" />
+ </field>
+ <field name="type" required="true" set-method="%2" type="string">
+ <bind-xml name="type" node="attribute" />
+ </field>
+ <field name="getMethod" required="true" set-method="setGetMethod" type="string">
+ <bind-xml name="get-method" node="attribute" />
+ </field>
+ <field name="setMethod" required="true" set-method="setSetMethod" type="string">
+ <bind-xml name="set-method" node="attribute" />
+ </field>
+ </class>
+ <class name="org.apache.directory.odm.ManyToManyMapping" verify-constructable="false">
+ <map-to xml="many-to-many" />
+ <field name="name" required="true" set-method="%1" type="string">
+ <bind-xml name="name" node="attribute" />
+ </field>
+ <field name="type" required="true" set-method="%2" type="string">
+ <bind-xml name="type" node="attribute" />
+ </field>
+ <field name="getMethod" required="true" set-method="setGetMethod" type="string">
+ <bind-xml name="get-method" node="attribute" />
+ </field>
+ <field name="setMethod" required="true" set-method="setSetMethod" type="string">
+ <bind-xml name="set-method" node="attribute" />
+ </field>
+ <field name="memberField" required="true" set-method="setMemberField" type="string">
+ <bind-xml name="member-field" node="attribute" />
+ </field>
+ <field name="filter" required="false" set-method="setFilter" type="string">
+ <bind-xml name="filter" node="attribute" />
+ </field>
+ </class>
+ <class name="org.apache.directory.odm.ChildMapping" verify-constructable="false">
+ <map-to xml="child" />
+ <field name="name" required="true" set-method="%1" type="string">
+ <bind-xml name="name" node="attribute" />
+ </field>
+ <field name="type" required="true" set-method="%2" type="string">
+ <bind-xml name="type" node="attribute" />
+ </field>
+ <field name="getMethod" required="false" set-method="setGetMethod" type="string">
+ <bind-xml name="get-method" node="attribute" />
+ </field>
+ <field name="setMethod" required="false" set-method="setSetMethod" type="string">
+ <bind-xml name="set-method" node="attribute" />
+ </field>
+ <field name="filter" required="false" set-method="setFilter" type="string">
+ <bind-xml name="filter" node="attribute" />
+ </field>
+ <field name="cardinality" required="false" set-method="setCardinality" type="string">
+ <bind-xml name="cardinality" node="attribute" />
+ </field>
+ </class>
+</mapping>
\ No newline at end of file
Propchange: directory/sandbox/hennejg/odm/trunk/src/main/resources/org/apache/directory/odm/ldap-mapping.xml
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: directory/sandbox/hennejg/odm/trunk/src/test/java/org/apache/directory/odm/test/AbstractEmbeddedDirectoryTest.java
URL: http://svn.apache.org/viewvc/directory/sandbox/hennejg/odm/trunk/src/test/java/org/apache/directory/odm/test/AbstractEmbeddedDirectoryTest.java?rev=606228&view=auto
==============================================================================
--- directory/sandbox/hennejg/odm/trunk/src/test/java/org/apache/directory/odm/test/AbstractEmbeddedDirectoryTest.java (added)
+++ directory/sandbox/hennejg/odm/trunk/src/test/java/org/apache/directory/odm/test/AbstractEmbeddedDirectoryTest.java Fri Dec 21 08:03:46 2007
@@ -0,0 +1,268 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *******************************************************************************/
+package org.apache.directory.odm.test;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.Random;
+import java.util.Set;
+
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.BasicAttributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+
+import org.apache.directory.odm.DirectoryException;
+import org.apache.directory.odm.DirectoryFacade;
+import org.apache.directory.odm.DiropLogger;
+import org.apache.directory.odm.LDAPConnectionDescriptor;
+import org.apache.directory.odm.Mapping;
+import org.apache.directory.odm.Util;
+import org.apache.directory.odm.LDAPConnectionDescriptor.ProviderType;
+import org.apache.directory.odm.test.model.OrganizationalUnit;
+import org.apache.directory.server.configuration.MutableServerStartupConfiguration;
+import org.apache.directory.server.core.configuration.Configuration;
+import org.apache.directory.server.core.configuration.MutablePartitionConfiguration;
+import org.apache.directory.server.core.configuration.PartitionConfiguration;
+import org.apache.directory.server.core.configuration.ShutdownConfiguration;
+import org.apache.directory.server.core.configuration.SyncConfiguration;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+
+public class AbstractEmbeddedDirectoryTest {
+
+ private static short ldapPort;
+ protected static String baseDN = "dc=test,dc=test";
+ protected static String envDN = "ou=test," + baseDN;
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ DiropLogger.LOG.enable(true, true);
+
+ ldapPort = getRandomNumber();
+
+ startService();
+ }
+
+ private static Hashtable createContextEnv() {
+ Hashtable env = new Properties();
+ env.put("java.naming.provider.url", "uid=admin,ou=system");
+ env.put("java.naming.factory.initial",
+ "org.apache.directory.server.jndi.ServerContextFactory");
+ env.put("java.naming.security.authentication", "simple");
+ env.put("java.naming.security.principal", "uid=admin,ou=system");
+ env.put("java.naming.security.credentials", "secret");
+ env.put(Configuration.JNDI_KEY, new SyncConfiguration());
+ return env;
+ }
+
+ private static void startService() throws Exception {
+ Hashtable env = createContextEnv();
+ MutableServerStartupConfiguration cfg = new MutableServerStartupConfiguration();
+ cfg.setAccessControlEnabled(false);
+ cfg.setAllowAnonymousAccess(true);
+ cfg.setEnableNetworking(true);
+ cfg.setLdapPort(ldapPort);
+ cfg.setEnableNtp(false);
+ cfg.setEnableKerberos(false);
+ cfg.setEnableChangePassword(false);
+ cfg.setWorkingDirectory(new File("unit-test-tmp"));
+// cfg.setLdifDirectory(new File("unit-test-tmp"));
+
+ Set schemas = cfg.getBootstrapSchemas();
+ schemas.add(Class.forName(
+ "org.apache.directory.server.core.schema.bootstrap.NisSchema")
+ .newInstance());
+ cfg.setBootstrapSchemas(schemas);
+
+ Set pcfgs = new HashSet<PartitionConfiguration>();
+ pcfgs.add(createCustomPartition("dc=test,dc=test"));
+ cfg.setContextPartitionConfigurations(pcfgs);
+
+ env.putAll(cfg.toJndiEnvironment());
+
+ new InitialDirContext(env);
+ }
+
+ private static PartitionConfiguration createCustomPartition(String name)
+ throws NamingException {
+ BasicAttributes attrs;
+ Set indexedAttrs;
+ BasicAttribute attr;
+ MutablePartitionConfiguration pcfg = new MutablePartitionConfiguration();
+
+ // construct partition name from DN
+ String nameParts[] = name.split(",");
+ StringBuffer partitionName = new StringBuffer();
+ for (int i = 0; i < nameParts.length; i++) {
+ int idx = nameParts[i].indexOf('=');
+ if (i > 0)
+ partitionName.append('_');
+ partitionName.append(idx > 0
+ ? nameParts[i].substring(idx + 1)
+ : nameParts[i]);
+ }
+
+ pcfg.setName(partitionName.toString());
+ pcfg.setSuffix(name);
+
+ indexedAttrs = new HashSet();
+ indexedAttrs.add("ou");
+ indexedAttrs.add("dc");
+ indexedAttrs.add("cn");
+ indexedAttrs.add("macAddress");
+ indexedAttrs.add("ipHostNumber");
+ indexedAttrs.add("objectClass");
+ pcfg.setIndexedAttributes(indexedAttrs);
+
+ attrs = new BasicAttributes(true);
+
+ attr = new BasicAttribute("objectClass");
+ attr.add("top");
+ attr.add("domain");
+ attr.add("extensibleObject");
+ attrs.put(attr);
+
+ attr = new BasicAttribute("dc");
+ attr.add(name);
+ attrs.put(attr);
+
+ pcfg.setContextEntry(attrs);
+
+ return pcfg;
+ }
+
+ @AfterClass
+ public static void cleanUp() throws NamingException {
+ ShutdownConfiguration cfg = new ShutdownConfiguration();
+ Hashtable env = createContextEnv();
+ env.putAll(cfg.toJndiEnvironment());
+ new InitialDirContext(env);
+
+ deleteRecursively(new File("unit-test-tmp"));
+ }
+
+ protected static LDAPConnectionDescriptor getConnectionDescriptor() {
+ final LDAPConnectionDescriptor lcd = new LDAPConnectionDescriptor();
+ lcd.setPortNumber(ldapPort);
+ lcd.setProviderType(ProviderType.SUN);
+
+ return lcd;
+ }
+
+ static void deleteRecursively(File file) {
+ if (!file.exists())
+ return;
+
+ if (file.isDirectory())
+ for (final File f : file.listFiles())
+ if (f.isDirectory())
+ deleteRecursively(f);
+ else
+ f.delete();
+
+ file.delete();
+ }
+
+ private static short getRandomNumber() {
+ final Random ran = new Random();
+ return (short) (11000 + ran.nextInt(999));
+ }
+
+ protected Mapping mapping;
+ protected LDAPConnectionDescriptor connectionDescriptor;
+
+ @Before
+ public void initEnv() throws Exception {
+ Mapping.disableCache = true;
+
+ connectionDescriptor = getConnectionDescriptor();
+ connectionDescriptor.setBaseDN(baseDN);
+
+ final DirectoryFacade facade = connectionDescriptor.createDirectoryFacade();
+ final DirContext ctx = facade.createDirContext();
+ try {
+ Util.deleteRecursively(ctx, facade.makeRelativeName(envDN));
+ } catch (final NameNotFoundException e) {
+ // ignore
+ } finally {
+ ctx.close();
+ }
+
+ mapping = Mapping.load(getClass().getResourceAsStream(
+ "/org/apache/odm/test/GENERIC_RFC.xml"));
+ mapping.initialize();
+
+ mapping.setConnectionDescriptor(connectionDescriptor);
+
+ final OrganizationalUnit ou = new OrganizationalUnit();
+ ou.setName("test");
+ ou.setDescription("openthinclient.org Console"); //$NON-NLS-1$
+ mapping.save(ou, "");
+
+ connectionDescriptor.setBaseDN(envDN);
+
+ // re-set mapping to the env DN
+ mapping.setConnectionDescriptor(connectionDescriptor);
+
+ final OrganizationalUnit users = new OrganizationalUnit();
+ users.setName("users");
+ mapping.save(users, "");
+
+ final OrganizationalUnit locations = new OrganizationalUnit();
+ locations.setName("locations");
+ mapping.save(locations, "");
+
+ final OrganizationalUnit usergroups = new OrganizationalUnit();
+ usergroups.setName("usergroups");
+ mapping.save(usergroups, "");
+
+ final OrganizationalUnit clients = new OrganizationalUnit();
+ clients.setName("clients");
+ mapping.save(clients, "");
+
+ final OrganizationalUnit hwtypes = new OrganizationalUnit();
+ hwtypes.setName("hwtypes");
+ mapping.save(hwtypes, "");
+ }
+
+ @After
+ public void destroyEnv() throws IOException, DirectoryException,
+ NamingException {
+ final LDAPConnectionDescriptor lcd = getConnectionDescriptor();
+ lcd.setBaseDN(baseDN);
+
+ final DirectoryFacade facade = lcd.createDirectoryFacade();
+ final DirContext ctx = facade.createDirContext();
+ try {
+ Util.deleteRecursively(ctx, facade.makeRelativeName(envDN));
+ } catch (final NameNotFoundException e) {
+ // ignore!
+ } finally {
+ ctx.close();
+ }
+ }
+}
\ No newline at end of file
Propchange: directory/sandbox/hennejg/odm/trunk/src/test/java/org/apache/directory/odm/test/AbstractEmbeddedDirectoryTest.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: directory/sandbox/hennejg/odm/trunk/src/test/java/org/apache/directory/odm/test/TestBasicMapping.java
URL: http://svn.apache.org/viewvc/directory/sandbox/hennejg/odm/trunk/src/test/java/org/apache/directory/odm/test/TestBasicMapping.java?rev=606228&view=auto
==============================================================================
--- directory/sandbox/hennejg/odm/trunk/src/test/java/org/apache/directory/odm/test/TestBasicMapping.java (added)
+++ directory/sandbox/hennejg/odm/trunk/src/test/java/org/apache/directory/odm/test/TestBasicMapping.java Fri Dec 21 08:03:46 2007
@@ -0,0 +1,503 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *******************************************************************************/
+package org.apache.directory.odm.test;
+
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.naming.NameNotFoundException;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.directory.odm.DirectoryException;
+import org.apache.directory.odm.DirectoryFacade;
+import org.apache.directory.odm.test.model.Client;
+import org.apache.directory.odm.test.model.HardwareType;
+import org.apache.directory.odm.test.model.Location;
+import org.apache.directory.odm.test.model.User;
+import org.apache.directory.odm.test.model.UserGroup;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestBasicMapping extends AbstractEmbeddedDirectoryTest {
+ private void createTestObjects() throws Exception {
+ final User u1 = new User();
+ u1.setName("jdoe");
+ u1.setGivenName("John");
+ u1.setSn("Doe");
+ mapping.save(u1);
+
+ final User u2 = new User();
+ u2.setName("hhirsch");
+ u2.setGivenName("Harry");
+ u2.setSn("Hirsch");
+ mapping.save(u2);
+
+ final UserGroup g1 = new UserGroup();
+ g1.setName("some users");
+ mapping.save(g1);
+
+ final UserGroup g2 = new UserGroup();
+ g2.setName("some users");
+ mapping.save(g2);
+
+ final Location l1 = new Location();
+ l1.setName("here");
+ mapping.save(l1);
+
+ final Location l2 = new Location();
+ l2.setName("there");
+ mapping.save(l2);
+ }
+
+ @Test
+ public void basicObjectProperties() throws DirectoryException {
+ User u = new User();
+
+ u.setName("someName");
+ u.setDescription("some description");
+ u.setGivenName("John");
+ u.setSn("Doe");
+ u.setUid(2345);
+ u.setUserPassword(new byte[]{1, 2, 3, 4, 5});
+
+ mapping.save(u);
+
+ // re-load the user
+ u = mapping.load(User.class, u.getDn());
+ Assert.assertNull("Location", u.getLocation());
+ Assert.assertEquals("Name", "someName", u.getName());
+ Assert.assertEquals("Description", "some description", u.getDescription());
+ Assert.assertEquals("GivenName", "John", u.getGivenName());
+ Assert.assertEquals("SN", "Doe", u.getSn());
+ Assert.assertEquals("uid", new Integer(2345), u.getUid());
+ Assert.assertArrayEquals("password", new byte[]{1, 2, 3, 4, 5}, u
+ .getUserPassword());
+
+ // check refresh as well
+ mapping.refresh(u);
+ Assert.assertNull("Location", u.getLocation());
+ Assert.assertEquals("Name", "someName", u.getName());
+ Assert.assertEquals("Description", "some description", u.getDescription());
+ Assert.assertEquals("GivenName", "John", u.getGivenName());
+ Assert.assertEquals("SN", "Doe", u.getSn());
+ Assert.assertEquals("uid", new Integer(2345), u.getUid());
+ Assert.assertArrayEquals("password", new byte[]{1, 2, 3, 4, 5}, u
+ .getUserPassword());
+ }
+
+ @Test
+ public void removeObject() throws Exception {
+ testUpdateProperty(); // will create tree of stuff
+
+ final Iterator<HardwareType> i = mapping.list(HardwareType.class)
+ .iterator();
+ final HardwareType t = i.next();
+
+ Assert.assertFalse("too many hardware types found", i.hasNext());
+
+ mapping.delete(t);
+
+ final DirectoryFacade f = connectionDescriptor.createDirectoryFacade();
+ final LdapContext ctx = f.createDirContext();
+ try {
+ ctx.getAttributes(f.makeRelativeName(t.getDn()));
+ Assert.fail("object has not been properly deleted");
+ } catch (final NameNotFoundException e) {
+ // expected
+ } finally {
+ ctx.close();
+ }
+ }
+
+ @Test
+ public void saveWithFixedDN() throws DirectoryException {
+ final User u = new User();
+
+ u.setDn("cn=foobar," + envDN); // doesn't exist!
+
+ try {
+ mapping.save(u);
+ Assert.fail("Expected exception not thrown");
+ } catch (final DirectoryException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void addManyToOne() throws DirectoryException {
+ Client c = new Client();
+ c.setName("someName");
+ c.setDescription("some description");
+
+ final Location l = new Location();
+ l.setName("whatever");
+
+ c.setLocation(l);
+
+ mapping.save(c);
+
+ // re-load the user
+ c = mapping.load(Client.class, c.getDn());
+ Assert.assertEquals("Location", l.getDn(), c.getLocation().getDn());
+
+ // check refresh as well
+ mapping.refresh(c);
+ Assert.assertEquals("Location", l.getDn(), c.getLocation().getDn());
+ }
+
+ @Test
+ public void addOneToMany() throws Exception {
+ createTestObjects();
+
+ final Set<User> users = mapping.list(User.class);
+ Assert.assertTrue("Doesn't have users", users.size() > 0);
+
+ final Set<UserGroup> userGroups = mapping.list(UserGroup.class);
+ Assert.assertTrue("Doesn't have groups", userGroups.size() > 0);
+
+ // add users to groups
+ for (final UserGroup group : userGroups) {
+ final Set<User> members = group.getMembers();
+ Assert.assertEquals("Group not empty", members.size(), 0);
+
+ members.addAll(users);
+
+ mapping.save(group);
+ }
+
+ for (UserGroup group : userGroups) {
+ // check with refresh
+ mapping.refresh(group);
+ Assert.assertTrue("Not all Objects were assigned (refresh)", group
+ .getMembers().containsAll(users));
+
+ // check with reload
+ group = mapping.load(UserGroup.class, group.getDn());
+ Assert.assertTrue("Not all Objects were assigned (reload)", group
+ .getMembers().containsAll(users));
+ }
+
+ // check inverse ends
+ for (User u : users) {
+ mapping.refresh(u);
+ Assert.assertTrue("User doesn't have all groups (refresh)", u
+ .getUserGroups().containsAll(userGroups));
+
+ u = mapping.load(User.class, u.getDn());
+ Assert.assertTrue("User doesn't have all groups (reload)", u
+ .getUserGroups().containsAll(userGroups));
+ }
+ }
+
+ @Test
+ public void removeAllFromOneToMany() throws Exception {
+ createTestObjects();
+
+ addOneToMany();
+
+ final Set<User> users = mapping.list(User.class);
+ final Set<UserGroup> userGroups = mapping.list(UserGroup.class);
+
+ for (final UserGroup group : userGroups) {
+ final Set<User> members = group.getMembers();
+ Assert.assertEquals("Group not full", members.size(), users.size());
+ members.clear();
+ mapping.save(group);
+ }
+
+ for (UserGroup group : userGroups) {
+ group = mapping.load(UserGroup.class, group.getDn(), true);
+ Assert.assertEquals("Not all Objects were removed", 0, group.getMembers()
+ .size());
+ }
+ }
+
+ @Test
+ public void removeOneFromOneToMany() throws Exception {
+ createTestObjects();
+
+ addOneToMany();
+
+ final Set<User> users = mapping.list(User.class);
+ final Set<UserGroup> userGroups = mapping.list(UserGroup.class);
+
+ final User toRemove = users.iterator().next();
+ users.remove(toRemove);
+
+ for (final UserGroup group : userGroups) {
+ final Set<User> members = group.getMembers();
+ Assert.assertEquals("Group not full", users.size() + 1, members.size());
+ members.remove(toRemove);
+ mapping.save(group);
+ }
+
+ for (UserGroup group : userGroups) {
+ group = mapping.load(UserGroup.class, group.getDn(), true);
+ Assert.assertEquals("Incorrect member count", users.size(), group
+ .getMembers().size());
+ Assert.assertTrue("Wrong members", group.getMembers().containsAll(users));
+ }
+ }
+
+ // This test tests a feature to be removed. See SUITE-69
+ @Test
+ public void addOneToManyInverse() throws Exception {
+ createTestObjects();
+
+ final User user = mapping.list(User.class).iterator().next();
+ final Set<UserGroup> userGroups = mapping.list(UserGroup.class);
+
+ Assert.assertEquals("User has groups", 0, user.getUserGroups().size());
+
+ user.setUserGroups(userGroups);
+
+ mapping.save(user);
+
+ mapping.refresh(user);
+
+ Assert.assertTrue("Not all Groups were assigned", user.getUserGroups()
+ .containsAll(userGroups));
+
+ for (final UserGroup userGroup : userGroups) {
+ mapping.refresh(userGroup);
+ Assert.assertTrue("Group doesn't contain user", userGroup.getMembers()
+ .contains(user));
+ }
+ }
+
+ // This test tests a feature to be removed. See SUITE-69
+ @Test
+ public void removeOneToManyInverse() throws Exception {
+ createTestObjects();
+
+ final User user = mapping.list(User.class).iterator().next();
+ final UserGroup group = mapping.list(UserGroup.class).iterator().next();
+
+ Assert.assertEquals("User has groups", 0, user.getUserGroups().size());
+ Assert.assertEquals("Group has users", 0, group.getMembers().size());
+
+ user.getUserGroups().add(group);
+ mapping.save(user);
+
+ mapping.refresh(group);
+ mapping.refresh(user);
+
+ Assert.assertTrue("User doesn't have group", user.getUserGroups().contains(
+ group));
+ Assert.assertTrue("Group doesn't have user", group.getMembers().contains(
+ user));
+
+ user.getUserGroups().remove(group);
+ mapping.save(user);
+
+ mapping.save(user);
+
+ mapping.refresh(group);
+ mapping.refresh(user);
+
+ Assert.assertFalse("User has group", user.getUserGroups().contains(group));
+ Assert.assertFalse("Group has user", group.getMembers().contains(user));
+ }
+
+ @SuppressWarnings("deprecation")
+ @Test
+ public void testUpdateProperty() throws Exception {
+ HardwareType type = new HardwareType();
+ type.setName("foo");
+ type.setValue("foo.bar", "foo");
+ type.setValue("foo.bar1", "foo");
+ type.setValue("foo.bar2", "foo");
+ type.setValue("foo.bar3", "foo");
+
+ mapping.save(type);
+
+ type = mapping.load(HardwareType.class, type.getDn());
+
+ Assert.assertEquals("Property", "foo", type.getValue("foo.bar"));
+ Assert.assertEquals("Property count", 4, type.getProperties().getMap()
+ .size());
+
+ type.setValue("foo.bar", "bar");
+
+ mapping.save(type);
+
+ type = mapping.load(HardwareType.class, type.getDn());
+
+ Assert.assertEquals("Property", "bar", type.getValue("foo.bar"));
+ Assert.assertEquals("Property count", 4, type.getProperties().getMap()
+ .size());
+ }
+
+ @SuppressWarnings("deprecation")
+ @Test
+ public void testAddProperty() throws Exception {
+ HardwareType type = new HardwareType();
+ type.setName("foo");
+ type.setValue("foo.bar", "foo");
+
+ mapping.save(type);
+
+ type = mapping.load(HardwareType.class, type.getDn());
+
+ Assert.assertEquals("Property", "foo", type.getValue("foo.bar"));
+ Assert.assertEquals("Property count", 1, type.getProperties().getMap()
+ .size());
+
+ type.setValue("foo.baz", "bar");
+
+ mapping.save(type);
+
+ type = mapping.load(HardwareType.class, type.getDn());
+
+ Assert.assertEquals("Property", "foo", type.getValue("foo.bar"));
+ Assert.assertEquals("Property", "bar", type.getValue("foo.baz"));
+ Assert.assertEquals("Property count", 2, type.getProperties().getMap()
+ .size());
+ }
+
+ @SuppressWarnings("deprecation")
+ @Test
+ public void testRemoveProperty() throws Exception {
+ HardwareType type = new HardwareType();
+ type.setName("foo");
+ type.setValue("foo.bar", "foo");
+ type.setValue("foo.baz", "bar");
+
+ mapping.save(type);
+
+ type = mapping.load(HardwareType.class, type.getDn());
+
+ Assert.assertEquals("Property", "foo", type.getValue("foo.bar"));
+ Assert.assertEquals("Property", "bar", type.getValue("foo.baz"));
+ Assert.assertEquals("Property count", 2, type.getProperties().getMap()
+ .size());
+
+ type.removeValue("foo.bar");
+
+ mapping.save(type);
+
+ type = mapping.load(HardwareType.class, type.getDn());
+
+ Assert.assertNull("Property", type.getValue("foo.bar"));
+ Assert.assertEquals("Property", "bar", type.getValue("foo.baz"));
+ Assert.assertEquals("Property count", 1, type.getProperties().getMap()
+ .size());
+ }
+
+ @Test
+ public void clearManyToOneReferencesOnDelete() throws DirectoryException {
+ Client c = new Client();
+ c.setName("someName");
+ c.setDescription("some description");
+
+ final Location l = new Location();
+ l.setName("whatever");
+
+ c.setLocation(l);
+
+ mapping.save(c);
+
+ mapping.delete(l);
+
+ // re-load the user
+ c = mapping.load(Client.class, c.getDn());
+ Assert.assertNull("Location still set", c.getLocation());
+
+ // check refresh as well
+ mapping.refresh(c);
+ Assert.assertNull("Location still set", c.getLocation());
+ }
+
+ @Test
+ public void clearOneToManyReferencesOnDelete() throws DirectoryException {
+ User u = new User();
+ u.setName("hhirsch");
+
+ UserGroup g = new UserGroup();
+ g.setName("some other group");
+
+ g.getMembers().add(u);
+
+ mapping.save(g);
+
+ u = mapping.load(User.class, u.getDn());
+ Assert.assertTrue("user is in group", u.getUserGroups().contains(g));
+
+ g = mapping.load(UserGroup.class, g.getDn());
+ Assert.assertTrue("group has user", g.getMembers().contains(u));
+
+ mapping.delete(u);
+
+ g = mapping.load(UserGroup.class, g.getDn());
+ Assert.assertEquals("group still has member", 0, g.getMembers().size());
+ }
+
+ @Test
+ public void updateManyToOneReferencesOnRename() throws DirectoryException {
+ Client c = new Client();
+ c.setName("someName");
+ c.setDescription("some description");
+
+ final Location l = new Location();
+ l.setName("whatever");
+
+ c.setLocation(l);
+
+ mapping.save(c);
+
+ c.setName("someOtherName");
+
+ mapping.save(c);
+
+ // re-load the user
+ c = mapping.load(Client.class, c.getDn());
+ Assert.assertEquals("Location no longer set", l, c.getLocation());
+
+ // check refresh as well
+ mapping.refresh(c);
+ Assert.assertEquals("Location no longer set", l, c.getLocation());
+ }
+
+ @Test
+ public void updateOneToManyReferencesOnRename() throws DirectoryException {
+ User u = new User();
+ u.setName("hhirsch");
+
+ UserGroup g = new UserGroup();
+ g.setName("some other group");
+
+ g.getMembers().add(u);
+
+ mapping.save(g);
+
+ u = mapping.load(User.class, u.getDn());
+ Assert.assertTrue("user is in group", u.getUserGroups().contains(g));
+
+ g = mapping.load(UserGroup.class, g.getDn());
+ Assert.assertTrue("group has user", g.getMembers().contains(u));
+
+ u.setName("hirschharry");
+ mapping.save(u);
+
+ g = mapping.load(UserGroup.class, g.getDn());
+ Assert.assertEquals("group no longer has member", 1, g.getMembers().size());
+ Assert.assertEquals("group no longer has member", u, g.getMembers()
+ .iterator().next());
+ }
+}
\ No newline at end of file
Propchange: directory/sandbox/hennejg/odm/trunk/src/test/java/org/apache/directory/odm/test/TestBasicMapping.java
------------------------------------------------------------------------------
svn:mime-type = text/plain