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 [1/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...
Author: hennejg
Date: Fri Dec 21 08:03:46 2007
New Revision: 606228
URL: http://svn.apache.org/viewvc?rev=606228&view=rev
Log:
Share project 'apache odm' into 'https://svn.apache.org/repos/asf/directory'
Added:
directory/sandbox/hennejg/odm/trunk/src/
directory/sandbox/hennejg/odm/trunk/src/main/
directory/sandbox/hennejg/odm/trunk/src/main/java/
directory/sandbox/hennejg/odm/trunk/src/main/java/org/
directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/
directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/
directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/
directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/AttributeMapping.java (with props)
directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/Cardinality.java (with props)
directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/ChildMapping.java (with props)
directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/DirectoryException.java (with props)
directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/DirectoryFacade.java (with props)
directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/DiropLogger.java (with props)
directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/EhCacheSecondLevelCache.java (with props)
directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/Filter.java (with props)
directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/GroupMapping.java (with props)
directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/LDAPConnectionDescriptor.java (with props)
directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/ManyToManyMapping.java (with props)
directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/ManyToOneMapping.java (with props)
directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/Mapping.java (with props)
directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/OneToManyMapping.java (with props)
directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/RDNAttributeMapping.java (with props)
directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/ReferenceAttributeMapping.java (with props)
directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/RollbackAction.java (with props)
directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/RollbackException.java (with props)
directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/SecondLevelCache.java (with props)
directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/Transaction.java (with props)
directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/TypeMapping.java (with props)
directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/UnmodifiableHashtable.java (with props)
directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/Util.java (with props)
directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/auth/
directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/auth/CachingCallbackHandler.java (with props)
directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/auth/UsernamePasswordHandler.java (with props)
directory/sandbox/hennejg/odm/trunk/src/main/resources/
directory/sandbox/hennejg/odm/trunk/src/main/resources/org/
directory/sandbox/hennejg/odm/trunk/src/main/resources/org/apache/
directory/sandbox/hennejg/odm/trunk/src/main/resources/org/apache/directory/
directory/sandbox/hennejg/odm/trunk/src/main/resources/org/apache/directory/odm/
directory/sandbox/hennejg/odm/trunk/src/main/resources/org/apache/directory/odm/ldap-mapping.xml (with props)
directory/sandbox/hennejg/odm/trunk/src/test/
directory/sandbox/hennejg/odm/trunk/src/test/java/
directory/sandbox/hennejg/odm/trunk/src/test/java/org/
directory/sandbox/hennejg/odm/trunk/src/test/java/org/apache/
directory/sandbox/hennejg/odm/trunk/src/test/java/org/apache/directory/
directory/sandbox/hennejg/odm/trunk/src/test/java/org/apache/directory/odm/
directory/sandbox/hennejg/odm/trunk/src/test/java/org/apache/directory/odm/test/
directory/sandbox/hennejg/odm/trunk/src/test/java/org/apache/directory/odm/test/AbstractEmbeddedDirectoryTest.java (with props)
directory/sandbox/hennejg/odm/trunk/src/test/java/org/apache/directory/odm/test/TestBasicMapping.java (with props)
directory/sandbox/hennejg/odm/trunk/src/test/java/org/apache/directory/odm/test/TestCaching.java (with props)
directory/sandbox/hennejg/odm/trunk/src/test/java/org/apache/directory/odm/test/model/
directory/sandbox/hennejg/odm/trunk/src/test/java/org/apache/directory/odm/test/model/Client.java (with props)
directory/sandbox/hennejg/odm/trunk/src/test/java/org/apache/directory/odm/test/model/DirectoryObject.java (with props)
directory/sandbox/hennejg/odm/trunk/src/test/java/org/apache/directory/odm/test/model/Group.java (with props)
directory/sandbox/hennejg/odm/trunk/src/test/java/org/apache/directory/odm/test/model/HardwareType.java (with props)
directory/sandbox/hennejg/odm/trunk/src/test/java/org/apache/directory/odm/test/model/Location.java (with props)
directory/sandbox/hennejg/odm/trunk/src/test/java/org/apache/directory/odm/test/model/OrganizationalUnit.java (with props)
directory/sandbox/hennejg/odm/trunk/src/test/java/org/apache/directory/odm/test/model/Profile.java (with props)
directory/sandbox/hennejg/odm/trunk/src/test/java/org/apache/directory/odm/test/model/Properties.java (with props)
directory/sandbox/hennejg/odm/trunk/src/test/java/org/apache/directory/odm/test/model/Property.java (with props)
directory/sandbox/hennejg/odm/trunk/src/test/java/org/apache/directory/odm/test/model/User.java (with props)
directory/sandbox/hennejg/odm/trunk/src/test/java/org/apache/directory/odm/test/model/UserGroup.java (with props)
directory/sandbox/hennejg/odm/trunk/src/test/resources/
directory/sandbox/hennejg/odm/trunk/src/test/resources/log4j.properties (with props)
directory/sandbox/hennejg/odm/trunk/src/test/resources/org/
directory/sandbox/hennejg/odm/trunk/src/test/resources/org/apache/
directory/sandbox/hennejg/odm/trunk/src/test/resources/org/apache/odm/
directory/sandbox/hennejg/odm/trunk/src/test/resources/org/apache/odm/test/
directory/sandbox/hennejg/odm/trunk/src/test/resources/org/apache/odm/test/GENERIC_RFC.xml (with props)
Added: directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/AttributeMapping.java
URL: http://svn.apache.org/viewvc/directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/AttributeMapping.java?rev=606228&view=auto
==============================================================================
--- directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/AttributeMapping.java (added)
+++ directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/AttributeMapping.java Fri Dec 21 08:03:46 2007
@@ -0,0 +1,364 @@
+/*******************************************************************************
+ * 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.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import javax.naming.Name;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttributes;
+import javax.naming.directory.DirContext;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author levigo
+ */
+public class AttributeMapping implements Cloneable {
+ private static final Logger logger = LoggerFactory.getLogger(AttributeMapping.class);
+
+ protected final String fieldName;
+
+ private Class fieldType;
+
+ private Method getMethod;
+
+ private String getMethodName;
+
+ private Method setMethod;
+
+ private String setMethodName;
+
+ protected TypeMapping type;
+
+ protected Cardinality cardinality = Cardinality.MANY;
+
+ public AttributeMapping(String fieldName, String fieldType)
+ throws ClassNotFoundException {
+ this.fieldName = fieldName;
+ this.setFieldType(Class.forName(fieldType));
+ }
+
+ /**
+ * @param targetName
+ * @param tx
+ */
+ protected void cascadeDelete(Name targetName, Transaction tx)
+ throws DirectoryException {
+ // nothing to do here
+ }
+
+ /**
+ * @param o
+ * @param tx TODO
+ * @throws DirectoryException
+ */
+ protected void cascadePostLoad(Object o, Transaction tx)
+ throws DirectoryException {
+ // nothing to do here
+ }
+
+ /**
+ * @param o
+ * @param tx
+ * @param ctx TODO
+ * @throws DirectoryException
+ */
+ protected void cascadePostSave(Object o, Transaction tx, DirContext ctx)
+ throws DirectoryException {
+ // nothing to do
+ }
+
+ /**
+ * @param o TODO
+ * @param newDN
+ * @param tx TODO
+ * @param targetName
+ * @throws NamingException
+ */
+ public void cascadeRDNChange(Object o, String oldDN, String newDN,
+ Transaction tx) throws DirectoryException, NamingException {
+ // nothing to do here
+ }
+
+ /**
+ * @return
+ */
+ protected boolean checkNull(Attributes a) {
+ return null == a.get(fieldName);
+ }
+
+ /**
+ * Dehydrates the attribute value into the supplied attribute container and
+ * returns the value.
+ *
+ * @param o
+ * @param a
+ * @return the dehydrated value
+ * @throws DirectoryException
+ * @throws NamingException
+ */
+ public Object dehydrate(Object o, BasicAttributes a)
+ throws DirectoryException, NamingException {
+ if (logger.isDebugEnabled())
+ logger.debug("dehydrating " + fieldName + " for instance of "
+ + o.getClass());
+
+ try {
+ final Object v = getValue(o);
+ return valueToAttributes(a, v);
+ } catch (final Exception e) {
+ throw new DirectoryException("Can't read attribute from object", e);
+ }
+ }
+
+ /**
+ * @param a
+ * @param v
+ * @return
+ */
+ protected Object valueToAttributes(BasicAttributes a, Object v) {
+ if (null != v)
+ if (fieldType.equals(String.class)) {
+ if (((String) v).length() > 0)
+ a.put(fieldName, v);
+ } else if (fieldType.equals(byte[].class)) {
+ if (((byte[]) v).length > 0)
+ a.put(fieldName, v);
+ } else
+ a.put(fieldName, v.toString());
+ return v;
+ }
+
+ protected Class getFieldType() {
+ return fieldType;
+ }
+
+ protected Method getGetter() throws NoSuchMethodException {
+ if (null == this.getMethod) {
+ if (null == getMethodName)
+ getMethodName = "get" + fieldName.substring(0, 1).toUpperCase()
+ + fieldName.substring(1);
+ this.getMethod = getMethod(type.getMappedType(), getMethodName,
+ new Class[]{});
+ }
+ return getMethod;
+ }
+
+ /**
+ * @param modelClass
+ * @param getMethod2
+ * @param classes
+ * @return
+ * @throws NoSuchMethodException
+ */
+ protected Method getMethod(Class targetClass, String methodName,
+ Class[] parameterTypes) throws NoSuchMethodException {
+ NoSuchMethodException firstException = null;
+ while (null != targetClass) {
+ try {
+ return targetClass.getMethod(methodName, parameterTypes);
+ } catch (final NoSuchMethodException e) {
+ if (null == firstException)
+ firstException = e;
+ }
+
+ targetClass = targetClass.getSuperclass();
+ }
+
+ throw firstException;
+ }
+
+ protected Method getSetter() throws NoSuchMethodException {
+ if (null == this.setMethod) {
+ if (null == setMethodName)
+ setMethodName = "set" + fieldName.substring(0, 1).toUpperCase()
+ + fieldName.substring(1);
+ this.setMethod = getMethod(type.getMappedType(), setMethodName,
+ new Class[]{getFieldType()});
+ }
+ return setMethod;
+ }
+
+ /**
+ * @param o
+ * @return
+ * @throws IllegalAccessException
+ * @throws InvocationTargetException
+ * @throws NoSuchMethodException
+ */
+ protected Object getValue(Object o) throws DirectoryException {
+ try {
+ return getGetter().invoke(o, new Object[]{});
+ } catch (final Exception e) {
+ throw new DirectoryException("Can't get value for " + this, e);
+ }
+ }
+
+ /**
+ * @param o
+ * @param a
+ * @param tx TODO
+ * @throws NamingException
+ * @throws InvocationTargetException
+ * @throws IllegalAccessException
+ * @throws IllegalArgumentException
+ * @throws NoSuchMethodException
+ * @throws DirectoryException
+ */
+ public void hydrate(Object o, Attributes a, Transaction tx)
+ throws DirectoryException {
+ if (logger.isDebugEnabled())
+ logger.debug("hydrating " + this + " for object of type " + o.getClass()
+ + " from " + a);
+
+ if (checkNull(a))
+ return;
+
+ try {
+ setValue(o, valueFromAttributes(a, o, tx));
+ } catch (final DirectoryException e) {
+ throw e;
+ } catch (final Exception e) {
+ throw new DirectoryException("Can't hydrate attribute " + fieldName, e);
+ }
+ }
+
+ /**
+ * @param instance
+ * @throws DirectoryException
+ */
+ protected void initNewInstance(Object instance) throws DirectoryException {
+ // nothing to do
+ }
+
+ /**
+ *
+ */
+ protected void initPostLoad() {
+ // nothing to do
+ }
+
+ protected void setFieldType(Class fieldType) {
+ this.fieldType = fieldType;
+ this.getMethod = this.setMethod = null;
+ }
+
+ public void setGetMethod(String getMethodName) {
+ this.getMethodName = getMethodName;
+ }
+
+ public void setSetMethod(String setMethodName) {
+ this.setMethodName = setMethodName;
+ }
+
+ public void setTypeMapping(TypeMapping type) {
+ this.type = type;
+ }
+
+ /**
+ * @param o
+ * @param a
+ * @param dn
+ * @return
+ * @throws DirectoryException
+ * @throws IllegalAccessException
+ * @throws InvocationTargetException
+ * @throws NoSuchMethodException
+ * @throws NamingException
+ * @throws DirectoryException
+ */
+ Object setValue(Object o, Object value) throws DirectoryException {
+ try {
+ return getSetter().invoke(o, new Object[]{value});
+ } catch (final Exception e) {
+ throw new DirectoryException("Can't set value for " + this, e);
+ }
+ }
+
+ /*
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "[AttributeMapping name=" + fieldName + " type=" + fieldType + "]";
+ }
+
+ /**
+ * @param a
+ * @param o TODO
+ * @param tx TODO
+ * @return
+ * @throws NamingException
+ * @throws DirectoryException
+ */
+ protected Object valueFromAttributes(Attributes a, Object o, Transaction tx)
+ throws NamingException, DirectoryException {
+ final Attribute attribute = a.get(fieldName);
+ if (null != attribute) {
+ Object v = attribute.get();
+ if (null != v)
+ // handle various value types
+ if (fieldType.equals(Integer.class))
+ try {
+ v = new Integer(v.toString());
+ } catch (final NumberFormatException e) {
+ logger.error("Can't convert this value to an Integer: "
+ + v.toString());
+ v = null;
+ }
+ return v;
+ } else
+ return null;
+ }
+
+ /**
+ * @param o
+ * @param tx
+ * @throws DirectoryException
+ */
+ protected void cascadePreSave(Object o, Transaction tx)
+ throws DirectoryException {
+ // nothing to be done for basic attributes
+ }
+
+ /*
+ * @see java.lang.Object#clone()
+ */
+ @Override
+ protected AttributeMapping clone() throws CloneNotSupportedException {
+ return (AttributeMapping) super.clone();
+ }
+
+ public void setCardinality(String cardinality) {
+ this.cardinality = Cardinality.valueOf(cardinality);
+ }
+
+ protected String getFieldName() {
+ return fieldName;
+ }
+
+ protected TypeMapping getTypeMapping() {
+ return type;
+ }
+}
Propchange: directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/AttributeMapping.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/Cardinality.java
URL: http://svn.apache.org/viewvc/directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/Cardinality.java?rev=606228&view=auto
==============================================================================
--- directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/Cardinality.java (added)
+++ directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/Cardinality.java Fri Dec 21 08:03:46 2007
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * 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;
+
+enum Cardinality {
+ ONE, ZERO_OR_ONE, ONE_OR_MANY, MANY
+}
\ No newline at end of file
Propchange: directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/Cardinality.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/ChildMapping.java
URL: http://svn.apache.org/viewvc/directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/ChildMapping.java?rev=606228&view=auto
==============================================================================
--- directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/ChildMapping.java (added)
+++ directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/ChildMapping.java Fri Dec 21 08:03:46 2007
@@ -0,0 +1,386 @@
+/*******************************************************************************
+ * 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.io.Serializable;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttributes;
+import javax.naming.directory.DirContext;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author levigo
+ */
+public class ChildMapping extends AttributeMapping implements Serializable {
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+ private static final Logger logger = LoggerFactory
+ .getLogger(Cardinality.class);
+
+ private String filter;
+ private final Class childType;
+ private TypeMapping childMapping;
+
+ public ChildMapping(String fieldName, String fieldType)
+ throws ClassNotFoundException {
+ super(fieldName, fieldType);
+ this.childType = Class.forName(fieldType);
+
+ if (!Object.class.isAssignableFrom(this.childType))
+ throw new IllegalArgumentException("The field " + fieldName
+ + " is not a subclass of Object");
+ }
+
+ /*
+ * @see org.apache.directory.odm.AttributeMapping#initPostLoad()
+ */
+ @Override
+ protected void initPostLoad() {
+ super.initPostLoad();
+ final TypeMapping child = type.getMapping().getMapping(childType);
+ if (null == child)
+ throw new IllegalStateException(this + ": no mapping for peer type "
+ + childType);
+
+ this.childMapping = child;
+
+ child.addReferrer(this);
+ }
+
+ /*
+ * @see org.openthinclient.common.directory.ldap.AttributeMapping#hydrate(java.lang.Object,
+ * javax.naming.directory.Attributes)
+ */
+ @Override
+ public void hydrate(Object o, Attributes a, Transaction tx)
+ throws DirectoryException {
+ // nothing to do here
+ }
+
+ /*
+ * @see org.openthinclient.common.directory.ldap.AttributeMapping#cascadePostLoad(java.lang.Object)
+ */
+ @Override
+ protected void cascadePostLoad(final Object parent, Transaction tx)
+ throws DirectoryException {
+ if (cardinality == Cardinality.MANY
+ || cardinality == Cardinality.ONE_OR_MANY)
+ setValue(parent, Proxy.newProxyInstance(parent.getClass()
+ .getClassLoader(), new Class[]{Set.class}, new InvocationHandler() {
+ private Object childSet;
+
+ public Object invoke(Object proxy, Method method, Object[] args)
+ throws Throwable {
+ if (null == childSet) {
+ final Transaction tx = new Transaction(type.getMapping());
+ try {
+ DiropLogger.LOG.logReadComment("LAZY LOAD: children for {0}",
+ fieldName);
+
+ childSet = loadChildren(parent, tx);
+
+ // set real loaded object to original instance.
+ setValue(parent, childSet);
+ } finally {
+ tx.commit();
+ }
+ }
+ return method.invoke(childSet, args);
+ };
+ }));
+ else
+ setValue(parent, loadChildren(parent, tx));
+ }
+
+ /**
+ * Load the referenced children for the given parent.
+ *
+ * @param parent the parent
+ * @param tx the current transaction
+ * @return
+ * @throws DirectoryException
+ */
+ private Object loadChildren(Object parent, Transaction tx)
+ throws DirectoryException {
+ final String dn = type.getDN(parent);
+
+ final Set set = childMapping.list(null != filter
+ ? new Filter(filter, dn)
+ : null, dn, null, tx);
+
+ switch (cardinality){
+ case ONE :
+ if (set.size() == 0) {
+ logger.warn("No child for " + this
+ + " with cardinality ONE found at " + dn + ", filter: " + filter);
+ return null;
+ }
+ // fall through!
+ case ZERO_OR_ONE :
+ if (set.size() == 0)
+ return null;
+ if (set.size() == 1)
+ return set.iterator().next();
+ throw new DirectoryException("More than one child for " + this
+ + " with cardinality ONE found at " + dn + ", filter: " + filter);
+ case ONE_OR_MANY :
+ if (set.size() == 0)
+ throw new DirectoryException("No child for " + this
+ + " with cardinality ONE_OR_MANY found at " + dn + ", filter: "
+ + filter);
+ // fall through
+ case MANY :
+ default :
+ return set;
+ }
+ }
+
+ /*
+ * @see org.openthinclient.common.directory.ldap.AttributeMapping#checkNull(javax.naming.directory.Attributes)
+ */
+ @Override
+ protected boolean checkNull(Attributes a) {
+ return false;
+ }
+
+ @Override
+ public void setCardinality(String cardinality) {
+ super.setCardinality(cardinality);
+
+ if (this.cardinality == Cardinality.ONE_OR_MANY
+ || this.cardinality == Cardinality.MANY)
+ setFieldType(Set.class);
+ }
+
+ public void setFilter(String filter) {
+ this.filter = filter;
+ }
+
+ /*
+ * @see org.openthinclient.common.directory.ldap.AttributeMapping#initNewInstance(org.openthinclient.common.directory.Object)
+ */
+ @Override
+ protected void initNewInstance(Object instance) throws DirectoryException {
+ if (cardinality == Cardinality.ONE
+ || cardinality == Cardinality.ONE_OR_MANY) {
+ Object v = type.getMapping().create(childType);
+
+ if (cardinality == Cardinality.ONE_OR_MANY) {
+ final Set tmp = new HashSet();
+ tmp.add(v);
+ v = tmp;
+ }
+
+ setValue(instance, v);
+ }
+ }
+
+ /*
+ * @see org.openthinclient.common.directory.ldap.AttributeMapping#dehydrate(org.openthinclient.common.directory.Object,
+ * javax.naming.directory.BasicAttributes)
+ */
+ @Override
+ public Object dehydrate(Object o, BasicAttributes a)
+ throws DirectoryException {
+ // nothing to do
+ return null;
+ }
+
+ /*
+ * @see org.openthinclient.common.directory.ldap.AttributeMapping#cascadePostSave(org.openthinclient.common.directory.Object)
+ */
+ @Override
+ protected void cascadePostSave(Object o, Transaction tx, DirContext ctx)
+ throws DirectoryException {
+ switch (cardinality){
+ case ONE :
+ final Object child = getValue(o);
+ if (null == child)
+ throw new DirectoryException(
+ "No child for child mapping with cardinality ONE present");
+ save(o, child, tx);
+ break;
+ case ZERO_OR_ONE :
+ save(o, getValue(o), tx);
+ break;
+ case ONE_OR_MANY :
+ Set set = (Set) getValue(o);
+ if (Proxy.isProxyClass(set.getClass())) {
+ if (logger.isDebugEnabled())
+ logger.debug("Still got the dynamic proxy for " + o);
+ } else {
+ if (set.size() == 0)
+ throw new DirectoryException(
+ "No child for child mapping with cardinality ONE_OR_MANY present");
+ save(o, set, tx);
+ }
+ break;
+ case MANY :
+ default :
+ set = (Set) getValue(o);
+ if (Proxy.isProxyClass(set.getClass())) {
+ if (logger.isDebugEnabled())
+ logger.debug("Still got the dynamic proxy for " + o);
+ } else
+ save(o, set, tx);
+ break;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.directory.odm.AttributeMapping#cascadeRDNChange(java.lang.Object,
+ * javax.naming.Name, javax.naming.Name,
+ * org.apache.directory.odm.Transaction)
+ */
+ @Override
+ public void cascadeRDNChange(Object parent, String oldDN, String newDN,
+ Transaction tx) throws DirectoryException, NamingException {
+ switch (cardinality){
+ case ONE :
+ case ZERO_OR_ONE :
+ final Object child = getValue(parent);
+ childMapping.handleParentNameChange(child, oldDN, newDN, tx);
+ break;
+
+ case ONE_OR_MANY :
+ case MANY :
+ final Set children = (Set) getValue(parent);
+ if (Proxy.isProxyClass(children.getClass()))
+ // still got the proxy - just replace it
+ cascadePostLoad(parent, tx);
+ else
+ for (final Object c : children)
+ childMapping.handleParentNameChange(c, oldDN, newDN, tx);
+ break;
+ }
+ }
+
+ /**
+ * @param child
+ * @throws DirectoryException
+ */
+ private void save(Object parent, Set children, Transaction tx)
+ throws DirectoryException {
+ final TypeMapping parentMapping = getParentMapping(parent);
+
+ String parentDNrelative;
+ // String parentDNabsolute;
+ try {
+ parentDNrelative = type.getDirectoryFacade().makeRelativeName(
+ parentMapping.getDN(parent)).toString();
+ // parentDNabsolute = type.getDirectoryFacade().makeAbsoluteName(
+ // parentDNrelative).toString();
+ } catch (final NamingException e) {
+ throw new DirectoryException(
+ "Parent DN can't be turned into relative one", e);
+ }
+
+ final Set existing = type.getMapping().list(childType,
+ null != filter ? new Filter(filter, parentDNrelative) : null,
+ parentDNrelative, null);
+
+ // see SUITE-81
+ // FIXME: This will prevent saveNewObject() in TypeMapping.save():
+ // make sure that all child DNs are set. Otherwise we might try to do
+ // spurious saves
+ // for (final Object child : children)
+ // childMapping.ensureDNSet(child, parentDNabsolute, tx);
+
+ // sync existing children with the ones contained in the object.
+ final Set missing = new HashSet();
+ if (null != children)
+ missing.addAll(children);
+
+ for (final Iterator i = missing.iterator(); i.hasNext();)
+ if (existing.remove(i.next()))
+ i.remove();
+
+ // missing now has the missing ones, existing the ones to be
+ // removed
+ for (final Object object : existing)
+ childMapping.delete(object, tx);
+ for (final Object missingObject : missing)
+ childMapping.save(missingObject, parentDNrelative, tx);
+ }
+
+ // private void ensureDNSet(Object child, String baseDN, Transaction tx)
+ // throws DirectoryException {
+ // if (null == childMapping.getDN(child))
+ // try {
+ // childMapping.fillEmptyDN(child, tx.getContext(childMapping
+ // .getDirectoryFacade()), baseDN);
+ // } catch (final Exception e) {
+ // throw new DirectoryException("Can't fill DN", e);
+ // }
+ // }
+
+ /**
+ * @param child
+ * @param tx
+ * @throws DirectoryException
+ */
+ private void save(Object parent, Object child, Transaction tx)
+ throws DirectoryException {
+ final TypeMapping parentMapping = getParentMapping(parent);
+
+ final String parentDN = parentMapping.getDN(parent);
+
+ if (null == child) {
+ // child is null - just delete the existing children
+ final Set set = type.getMapping().list(childType,
+ null != filter ? new Filter(filter, parentDN) : null, parentDN, null);
+ for (final Object existingChild : set)
+ childMapping.delete(existingChild, tx);
+ } else
+ // just save it (let the type mapping for the child do the chores)
+ childMapping.save(child, parentDN, tx);
+ }
+
+ private TypeMapping getParentMapping(Object parent) {
+ final TypeMapping parentMapping = type.getMapping().getMapping(
+ parent.getClass(), type.getDirectoryFacade());
+ if (null == parentMapping)
+ throw new IllegalStateException("Parent " + parent.getClass() + " for "
+ + this + " is not mapped");
+ return parentMapping;
+ }
+
+ /*
+ * @see org.apache.directory.odm.AttributeMapping#toString()
+ */
+ @Override
+ public String toString() {
+ return "[ChildMapping name=" + fieldName + ", cardinality=" + cardinality
+ + ", type=" + childType + "]";
+ }
+}
Propchange: directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/ChildMapping.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/DirectoryException.java
URL: http://svn.apache.org/viewvc/directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/DirectoryException.java?rev=606228&view=auto
==============================================================================
--- directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/DirectoryException.java (added)
+++ directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/DirectoryException.java Fri Dec 21 08:03:46 2007
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * 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;
+
+/**
+ * FIXME: the DirectoryException should probably just subclass from
+ * NameingException instead of wrapping one, most of the time.
+ *
+ * @author levigo
+ */
+public class DirectoryException extends Exception {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ *
+ */
+ public DirectoryException() {
+ super();
+ }
+
+ /**
+ * @param message
+ * @param cause
+ */
+ public DirectoryException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * @param message
+ */
+ public DirectoryException(String message) {
+ super(message);
+ }
+
+ /**
+ * @param cause
+ */
+ public DirectoryException(Throwable cause) {
+ super(cause);
+ }
+
+}
Propchange: directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/DirectoryException.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/DirectoryFacade.java
URL: http://svn.apache.org/viewvc/directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/DirectoryFacade.java?rev=606228&view=auto
==============================================================================
--- directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/DirectoryFacade.java (added)
+++ directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/DirectoryFacade.java Fri Dec 21 08:03:46 2007
@@ -0,0 +1,507 @@
+/*******************************************************************************
+ * 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.net.InetAddress;
+import java.net.MalformedURLException;
+import java.net.UnknownHostException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import javax.naming.AuthenticationException;
+import javax.naming.Context;
+import javax.naming.InvalidNameException;
+import javax.naming.Name;
+import javax.naming.NameClassPair;
+import javax.naming.NameParser;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+import javax.naming.ldap.LdapName;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+
+import org.apache.directory.odm.LDAPConnectionDescriptor.ConnectionMethod;
+import org.apache.directory.odm.LDAPConnectionDescriptor.DirectoryType;
+import org.apache.directory.odm.LDAPConnectionDescriptor.ProviderType;
+import org.apache.directory.odm.auth.CachingCallbackHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A facade providing useful services around an LDAP directory connection as
+ * described by the {@link LDAPConnectionDescriptor} supplied via the
+ * constructor.
+ *
+ * The {@link DirectoryFacade} is immutable with respect to the connection it
+ * uses.
+ *
+ * @author levigo
+ */
+public class DirectoryFacade {
+ private static final Logger logger = LoggerFactory.getLogger(DirectoryFacade.class);
+
+ private Name baseDNName;
+ private DirectoryType directoryType;
+ private Hashtable<Object, Object> ldapEnvironment;
+ private NameParser nameParser;
+ private final LDAPConnectionDescriptor connectionDescriptor;
+
+ private String dummyDN;
+
+ DirectoryFacade(LDAPConnectionDescriptor lcd) {
+ // make copy so that changes to the original descriptor don't end up
+ // affecting this facade.
+ this.connectionDescriptor = new LDAPConnectionDescriptor(lcd);
+ }
+
+ /**
+ * Get the baseDN for the described context as a {@link Name}
+ *
+ * @return
+ * @throws NamingException
+ */
+ public Name getBaseDNName() throws NamingException {
+ if (null == baseDNName)
+ baseDNName = getNameParser().parse(connectionDescriptor.getBaseDN());
+
+ return baseDNName;
+ }
+
+ public Hashtable<Object, Object> getLDAPEnv() throws NamingException {
+ if (null == ldapEnvironment) {
+ final Hashtable<Object, Object> env = new Hashtable<Object, Object>(
+ connectionDescriptor.getExtraEnv());
+ populateDefaultEnv(env);
+
+ switch (connectionDescriptor.getConnectionMethod()){
+ case PLAIN :
+ env.put(Context.SECURITY_AUTHENTICATION, "none");
+ break;
+ case SSL :
+ env.put(Context.SECURITY_PROTOCOL, "ssl");
+ break;
+ case START_TLS :
+ // not yet...
+ // LdapContext ctx = new InitialLdapContext(env, null);
+ // StartTlsResponse tls =
+ // (StartTlsResponse) ctx.extendedOperation(new StartTlsRequest());
+ // SSLSession sess = tls.negotiate();
+ throw new IllegalArgumentException("Start TLS not yet supported");
+ }
+
+ switch (connectionDescriptor.getAuthenticationMethod()){
+ case NONE :
+ env.put(Context.SECURITY_AUTHENTICATION, "none");
+ break;
+ case SIMPLE :
+ env.put(Context.SECURITY_AUTHENTICATION, "simple");
+
+ final NameCallback nc = new NameCallback("Bind DN");
+ final PasswordCallback pc = new PasswordCallback("Password", false);
+ try {
+ connectionDescriptor.getCallbackHandler().handle(
+ new Callback[]{nc, pc});
+ } catch (final Exception e) {
+ throw new NamingException("Can't get authentication information: "
+ + e);
+ }
+
+ env.put(Context.SECURITY_PRINCIPAL, nc.getName());
+ env.put(Context.SECURITY_CREDENTIALS, new String(pc.getPassword())
+ .getBytes());
+ break;
+ case SASL :
+ // not yet...
+ throw new IllegalArgumentException("SASL not yet supported");
+ }
+
+ env.put(Context.PROVIDER_URL, connectionDescriptor.getLDAPUrl());
+
+ try {
+ final InetAddress[] hostAddresses = InetAddress
+ .getAllByName(connectionDescriptor.getHostname());
+ final InetAddress localHost = InetAddress.getLocalHost();
+ for (final InetAddress element : hostAddresses)
+ if (element.isLoopbackAddress() || element.equals(localHost)) {
+ env.put(Mapping.PROPERTY_FORCE_SINGLE_THREADED, Boolean.TRUE);
+ break;
+ }
+ } catch (final UnknownHostException e) {
+ // should not happen
+ logger.error("Can't look up local interface address", e);
+ }
+
+ // create unmodifiable copy.
+ this.ldapEnvironment = new UnmodifiableHashtable<Object, Object>(env);
+ }
+
+ return ldapEnvironment;
+ }
+
+ /**
+ * Return the {@link NameParser} for the context described by this connection
+ * descriptor.
+ *
+ * @return
+ * @throws NamingException
+ */
+ public NameParser getNameParser() throws NamingException {
+ if (null == nameParser) {
+ final DirContext ctx = createDirContext();
+ try {
+ nameParser = ctx.getNameParser("");
+ } finally {
+ ctx.close();
+ }
+ }
+
+ return nameParser;
+ }
+
+ /**
+ * FIXME: the schema heuristic is, ... well, ... debatable.
+ *
+ * @param d
+ * @return
+ * @throws NamingException
+ * @throws MalformedURLException
+ * @throws DirectoryException
+ */
+ public DirectoryType guessDirectoryType() throws NamingException {
+ if (null == directoryType)
+ if (connectionDescriptor.getProviderType() == ProviderType.APACHE_DS_EMBEDDED) {
+ this.dummyDN = "DC=dummy";
+ directoryType = DirectoryType.GENERIC_RFC;
+ } else {
+ // try to determine the type of server. at this point we distinguish
+ // only rfc-style and MS-ADS style. temporarily switch to RootDSE.
+ final DirContext ctx = createRootDSEContext();
+ try {
+ // Apache DS?
+ String vendorName = "";
+ final Attribute vendorNameAttr = ctx.getAttributes("",
+ new String[]{"vendorName"}).get("vendorName");
+ if (null != vendorNameAttr)
+ vendorName = vendorNameAttr.get().toString();
+
+ if (vendorName.toUpperCase().startsWith("APACHE")) {
+ this.dummyDN = "DC=dummy";
+ return DirectoryType.GENERIC_RFC;
+ }
+
+ // MS-ADS style?
+ final Attributes attrs = ctx.getAttributes("",
+ new String[]{"dsServiceName"});
+ String nextattr = "";
+ // Get a list of the attributes
+ final NamingEnumeration enums = attrs.getIDs();
+ // Print out each attribute and its values
+ while (enums != null && enums.hasMore())
+ nextattr = (String) enums.next();
+ if (attrs.get(nextattr) == null) {
+ directoryType = DirectoryType.GENERIC_RFC;
+
+ if (logger.isDebugEnabled())
+ logger.debug("This is GENERIC_RFC");
+ } else {
+ final DirContext schema2 = (DirContext) ctx.getSchema("").lookup(
+ "ClassDefinition");
+ // List the contents of root
+ final NamingEnumeration bds = schema2.list("");
+ final boolean[] hasClassesR2 = new boolean[3];
+ final boolean[] hasClassesSFU = new boolean[4];
+
+ // check Classes
+ while (bds.hasMore()) {
+ final String s = ((NameClassPair) bds.next()).getName()
+ .toString();
+ // Classes 2003R2
+ if (s.equals("nisMap"))
+ hasClassesR2[0] = true;
+ if (s.equals("nisObject"))
+ hasClassesR2[1] = true;
+ if (s.equals("device"))
+ hasClassesR2[2] = true;
+ // Classes ADS with SFU
+ if (s.equals("msSFU30NisMap") || s.equals("msSFUNISMap"))
+ hasClassesSFU[0] = true;
+ if (s.equals("msSFU30NisObject") || s.equals("msSFUNisObject"))
+ hasClassesSFU[1] = true;
+ if (s.equals("msSFU30Ieee802Device")
+ || s.equals("msSFUIeee802Device"))
+ hasClassesSFU[2] = true;
+ if (s.equals("msSFU30IpHost") || s.equals("msSFUIpHost"))
+ hasClassesSFU[3] = true;
+ }
+ if (hasClassesR2[0] == true && hasClassesR2[1] == true
+ && hasClassesR2[2] == true) {
+ directoryType = DirectoryType.MS_2003R2;
+
+ if (logger.isDebugEnabled())
+ logger.debug("This is an MS ADS - MS_2003R2");
+
+ this.dummyDN = getDummyDN(ctx);
+ }
+ if (hasClassesSFU[0] == true && hasClassesSFU[1] == true
+ && hasClassesSFU[2] == true && hasClassesSFU[3] == true) {
+ directoryType = DirectoryType.MS_SFU;
+
+ if (logger.isDebugEnabled())
+ logger.debug("This is an MS ADS - MS_SFU");
+
+ this.dummyDN = getDummyDN(ctx);
+ }
+ }
+ if (null == directoryType)
+ throw new NamingException("Unrecognized directory server");
+ } finally {
+ ctx.close();
+ }
+ }
+
+ return directoryType;
+ }
+
+ /**
+ * Return whether the specified name resides within the described context.
+ *
+ * @param name
+ * @return
+ * @throws NamingException
+ */
+ public boolean contains(Name name) throws NamingException {
+ return name.startsWith(getBaseDNName());
+ }
+
+ public LdapContext createDirContext() throws NamingException {
+ while (true)
+ try {
+ return new InitialLdapContext(getLDAPEnv(), null);
+ } catch (final AuthenticationException e) {
+ if (connectionDescriptor.getCallbackHandler() instanceof CachingCallbackHandler) {
+ try {
+ ((CachingCallbackHandler) connectionDescriptor.getCallbackHandler())
+ .purgeCache();
+ } catch (final Exception e1) {
+ // if this method call failed, we give up instead of
+ // retrying.
+ throw new NamingException("Authentication with directory failed: "
+ + e1 + " (was: " + e + ")");
+ }
+ continue;
+ }
+ throw e;
+ }
+ }
+
+ private LdapContext createRootDSEContext() throws NamingException {
+ while (true)
+ try {
+ final Hashtable<Object, Object> env = new Hashtable<Object, Object>(
+ getLDAPEnv());
+ env.put(Context.PROVIDER_URL, getLDAPUrlForRootDSE());
+
+ return new InitialLdapContext(env, null);
+ } catch (final AuthenticationException e) {
+ if (connectionDescriptor.getCallbackHandler() instanceof CachingCallbackHandler) {
+ try {
+ ((CachingCallbackHandler) connectionDescriptor.getCallbackHandler())
+ .purgeCache();
+ } catch (final Exception e1) {
+ // if this method call failed, we give up instead of
+ // retrying.
+ throw new NamingException("Authentication with directory failed: "
+ + e1 + " (was: " + e + ")");
+ }
+ continue;
+ }
+ throw e;
+ }
+ }
+
+ /**
+ * Populate the environment with a few default settings.
+ *
+ * @param env
+ */
+ private void populateDefaultEnv(Hashtable<Object, Object> env) {
+ env.put(Context.INITIAL_CONTEXT_FACTORY, connectionDescriptor
+ .getProviderType().getClassName());
+ env.put(Context.REFERRAL, connectionDescriptor.getReferralPreference());
+
+ // Enable connection pooling
+ env.put("com.sun.jndi.ldap.connect.pool", "true");
+ env.put(Context.BATCHSIZE, "100");
+ }
+
+ private String getLDAPUrlForRootDSE() {
+ switch (connectionDescriptor.getProviderType()){
+ case SUN :
+ default :
+ return (connectionDescriptor.getConnectionMethod() != ConnectionMethod.SSL
+ ? "ldap"
+ : "ldaps")
+ + "://"
+ + connectionDescriptor.getHostname()
+ + ":"
+ + connectionDescriptor.getPortNumber();
+ case APACHE_DS_EMBEDDED :
+ return "";
+ }
+ }
+
+ /**
+ * Determine a dummy-DN based on the first ROOT DSE name.
+ *
+ * @param ctx
+ * @return
+ * @throws NamingException
+ */
+ private String getDummyDN(DirContext ctx) throws NamingException {
+ final Attributes dummyMember = ctx.getAttributes("",
+ new String[]{"rootDomainNamingContext"});
+ String nextDummy = "";
+
+ // get the first root DSE name.
+ for (final NamingEnumeration e = dummyMember.getIDs(); e != null
+ && e.hasMore();)
+ nextDummy = (String) e.next();
+
+ return dummyMember.get(nextDummy).get().toString();
+ }
+
+ /**
+ * Adjust the case of the attribute names in the given name according to the
+ * needs of the target directory. E.g. ActiveDirectory wants all upper-case
+ * names.
+ *
+ * FIXME: making use of the fact that the parsed names are actually
+ * {@link LdapName}s could make this more efficient.
+ *
+ * @param memberDN
+ * @param connectionDescriptor
+ * @return
+ * @throws NamingException
+ */
+ public String fixNameCase(String memberDN) throws NamingException {
+
+ if (!guessDirectoryType().requiresUpperCaseRDNAttributeNames())
+ return memberDN;
+
+ // use context's name parser to split the name into parts
+ final Name parsed = getNameParser().parse(memberDN);
+ Name adjusted = null;
+ for (final Enumeration<String> e = parsed.getAll(); e.hasMoreElements();) {
+ String part = e.nextElement();
+
+ final int idx = part.indexOf('=');
+ final char c[] = part.toCharArray();
+ for (int i = 0; i < idx; i++)
+ c[i] = Character.toUpperCase(c[i]);
+ part = new String(c);
+
+ if (null == adjusted)
+ adjusted = getNameParser().parse(part);
+ else
+ adjusted.add(part);
+ }
+
+ return adjusted.toString();
+ }
+
+ /**
+ * @param name
+ * @param connectionDescriptor
+ * @return
+ * @throws NamingException
+ *
+ * FIXME: respect upper case DN requirements
+ */
+ public Name makeAbsoluteName(String name) throws NamingException {
+ final Name parsedName = getNameParser().parse(name);
+
+ // if the name is relative, append ctx base dn
+ if (!contains(parsedName))
+ parsedName.addAll(0, getBaseDNName());
+
+ return parsedName;
+ }
+
+ /**
+ * @param name
+ * @param connectionDescriptor
+ * @return
+ * @throws NamingException
+ *
+ * FIXME: respect upper case DN requirements
+ */
+ public Name makeRelativeName(String name) throws NamingException {
+ final Name parsedName = getNameParser().parse(name);
+
+ // return name directly if it is not absolute
+ if (!parsedName.startsWith(getBaseDNName()))
+ return parsedName;
+
+ // don't remove suffix, if the connections base DN is zero-sized
+ if (getBaseDNName().size() == 0)
+ return parsedName;
+
+ return parsedName.getSuffix(getBaseDNName().size());
+ }
+
+ /**
+ * Get the dummy member to be used in 1..n attributes for groups when the
+ * group doesn't have a member.
+ *
+ * @return
+ * @throws NamingException
+ */
+ public String getDummyMember() throws NamingException {
+ if (null == dummyDN)
+ guessDirectoryType(); // this'll initialize it!
+ return dummyDN;
+ }
+
+ /**
+ * Return whether the given DN is a dummy DN.
+ *
+ * @return
+ */
+ public boolean isDummyMember(String dn) {
+ return dummyDN.equalsIgnoreCase(dn);
+ }
+
+ public boolean isReadOnly() {
+ return connectionDescriptor.isReadOnly();
+ }
+
+ public Name makeAbsoluteName(Name name) throws InvalidNameException {
+ // is name already absolute?
+ if (baseDNName.startsWith(name))
+ return name;
+
+ return ((Name) baseDNName.clone()).addAll(name);
+ }
+}
Propchange: directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/DirectoryFacade.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/DiropLogger.java
URL: http://svn.apache.org/viewvc/directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/DiropLogger.java?rev=606228&view=auto
==============================================================================
--- directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/DiropLogger.java (added)
+++ directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/DiropLogger.java Fri Dec 21 08:03:46 2007
@@ -0,0 +1,218 @@
+/*******************************************************************************
+ * 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.text.MessageFormat;
+
+import javax.naming.Name;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.ModificationItem;
+import javax.naming.directory.SearchControls;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The DiropLogger logs directory operations by formatting them as LDIF.
+ *
+ * @author levigo
+ */
+public class DiropLogger {
+ /**
+ * Logger to be used to log directory read operations
+ */
+ private final Logger readLogger;
+
+ /**
+ * Logger to be used to log directory write operations
+ */
+ private final Logger writeLogger;
+
+ /** Singleton instance */
+ public static DiropLogger LOG = new DiropLogger(DiropLogger.class
+ .getPackage().getName()
+ + ".DIROP");
+
+ private DiropLogger(String prefix) {
+ readLogger = LoggerFactory.getLogger(prefix + ".READ");
+ writeLogger = LoggerFactory.getLogger(prefix + ".WRITE");
+ }
+
+ public boolean isReadEnabled() {
+ return readLogger.isDebugEnabled();
+ }
+
+ public boolean isWriteEnabled() {
+ return writeLogger.isDebugEnabled();
+ }
+
+ public void logModify(Name dn, ModificationItem mods[], String comment) {
+ if (isWriteEnabled())
+ logModify(dn.toString(), mods, comment);
+ }
+
+ public void logModify(String dn, ModificationItem mods[], String comment) {
+ if (isWriteEnabled())
+ try {
+ writeLogger.debug("# " + comment);
+
+ writeLogger.debug("dn: " + dn);
+ writeLogger.debug("changetype: modify");
+ for (final ModificationItem mi : mods) {
+ final String id = mi.getAttribute().getID();
+ switch (mi.getModificationOp()){
+ case DirContext.ADD_ATTRIBUTE :
+ writeLogger.debug("add: " + id);
+ break;
+ case DirContext.REMOVE_ATTRIBUTE :
+ writeLogger.debug("remove: " + id);
+ break;
+ case DirContext.REPLACE_ATTRIBUTE :
+ writeLogger.debug("replace: " + id);
+ break;
+ }
+ for (final NamingEnumeration<?> e = mi.getAttribute().getAll(); e
+ .hasMore();)
+ writeLogger.debug(id + ": " + e.next());
+ writeLogger.debug("-");
+ }
+ writeLogger.debug("");
+ } catch (final Exception e) {
+ writeLogger.error("Can't log operation: ", e);
+ }
+ }
+
+ public void logAdd(Name dn, Attributes attributes, String comment) {
+ if (isWriteEnabled())
+ logAdd(dn.toString(), attributes, comment);
+ }
+
+ public void logAdd(String dn, Attributes attributes, String comment) {
+ if (isWriteEnabled())
+ try {
+ writeLogger.debug("# " + comment);
+
+ writeLogger.debug("dn: " + dn);
+ writeLogger.debug("changetype: add");
+ logAttributes(attributes);
+ writeLogger.debug("");
+ } catch (final NamingException e) {
+ writeLogger.error("Can't log operation: ", e);
+ }
+ }
+
+ public void logDelete(Name dn, String comment) {
+ if (isWriteEnabled())
+ logDelete(dn.toString(), comment);
+ }
+
+ public void logDelete(String dn, String comment) {
+ if (isWriteEnabled()) {
+ writeLogger.debug("# " + comment);
+
+ writeLogger.debug("dn: " + dn);
+ writeLogger.debug("changetype: delete");
+ writeLogger.debug("");
+ }
+ }
+
+ private void logAttributes(Attributes attributes) throws NamingException {
+ for (final NamingEnumeration<? extends Attribute> e = attributes.getAll(); e
+ .hasMore();) {
+ final Attribute a = e.next();
+ final String id = a.getID();
+ for (final NamingEnumeration<?> f = a.getAll(); f.hasMore();)
+ writeLogger.debug(id + ": " + f.next());
+ }
+ }
+
+ public void logModRDN(Name oldName, Name newName, String comment) {
+ if (isWriteEnabled()) {
+ writeLogger.debug("# " + comment);
+ writeLogger.debug("dn: " + oldName.toString());
+ writeLogger.debug("changetype: modrdn");
+ writeLogger.debug("newrdn: " + newName.get(newName.size() - 1));
+ writeLogger.debug("");
+ }
+ }
+
+ /**
+ * Enable/disable the logging of reads/writes.
+ *
+ * @param read set to <code>true</code> to enable read logging
+ * @param write set to <code>true</code> to enable write logging
+ */
+ public void enable(boolean read, boolean write) {
+ readLogger.warn("Can't enable/disable programmatically");
+ writeLogger.warn("Can't enable/disable programmatically");
+
+ // this worked under plain log4j, but no longer under SLF4J...
+ // readLogger.setLevel(read ? Level.DEBUG : Level.WARN);
+ // writeLogger.setLevel(write ? Level.DEBUG : Level.WARN);
+ }
+
+ public void logReadComment(String pattern, Object... args) {
+ if (isReadEnabled())
+ readLogger.debug(MessageFormat.format("# " + pattern, args));
+ }
+
+ public void logGetAttributes(Name name, String[] attributes, String comment) {
+ if (isReadEnabled())
+ logGetAttributes(name.toString(), attributes, comment);
+ }
+
+ public void logGetAttributes(String dn, String[] attributes, String comment) {
+ if (isReadEnabled()) {
+ readLogger.debug("# GET ATTRIBUTES: " + comment);
+ readLogger.debug("# dn: " + dn);
+ if (null != attributes) {
+ final StringBuilder sb = new StringBuilder("# fetching only: ");
+ for (final String s : attributes)
+ sb.append(s).append(" ");
+ readLogger.debug(sb.toString());
+ }
+ readLogger.debug("");
+ }
+ }
+
+ public void logSearch(String dn, String filter, Object filterArgs[],
+ SearchControls sc, String comment) {
+ if (isReadEnabled()) {
+ readLogger.debug("# SEARCH: " + comment);
+ readLogger.debug("# base: " + dn);
+
+ readLogger.debug("# filter: " + filter);
+ if (null != filterArgs)
+ for (final Object arg : filterArgs)
+ readLogger.debug("# " + arg);
+
+ readLogger.debug("# scope: "
+ + (sc.getSearchScope() == SearchControls.OBJECT_SCOPE ? "object" : sc
+ .getSearchScope() == SearchControls.ONELEVEL_SCOPE
+ ? "onelevel"
+ : "sub") + " timelimit: " + sc.getTimeLimit() + " countlimit: "
+ + sc.getCountLimit());
+ readLogger.debug("");
+ }
+ }
+}
Propchange: directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/DiropLogger.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/EhCacheSecondLevelCache.java
URL: http://svn.apache.org/viewvc/directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/EhCacheSecondLevelCache.java?rev=606228&view=auto
==============================================================================
--- directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/EhCacheSecondLevelCache.java (added)
+++ directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/EhCacheSecondLevelCache.java Fri Dec 21 08:03:46 2007
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * 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.io.IOException;
+
+import javax.naming.Name;
+import javax.naming.directory.Attributes;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.CacheException;
+import net.sf.ehcache.CacheManager;
+import net.sf.ehcache.Element;
+import net.sf.ehcache.hibernate.EhCache;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A SecondLevelCache implementation using {@link EhCache} as its backing store.
+ *
+ * @author levigo
+ */
+public class EhCacheSecondLevelCache implements SecondLevelCache {
+ private static final Logger logger = LoggerFactory
+ .getLogger(EhCacheSecondLevelCache.class);
+
+ private Cache cache;
+
+ public EhCacheSecondLevelCache() {
+ try {
+ if (CacheManager.getInstance().cacheExists("mapping"))
+ cache = CacheManager.getInstance().getCache("mapping");
+ else {
+ cache = new Cache("mapping", 5000, false, false, 120, 120);
+ CacheManager.getInstance().addCache(cache);
+ }
+ } catch (final CacheException e) {
+ logger.error("Can't create cache. Caching is disabled", e);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.directory.odm.SecondLevelCache#getEntry(javax.naming.Name)
+ */
+ public Attributes getEntry(Name name) {
+ if (null == cache || Mapping.disableCache)
+ return null;
+ try {
+ final Element element = cache.get(name);
+ if (null != element && logger.isDebugEnabled())
+ logger.debug("Global cache hit for " + name);
+ return (Attributes) (null != element ? element.getValue() : null);
+ } catch (final Throwable e) {
+ logger.warn("Can't get from cache", e);
+ return null;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.directory.odm.SecondLevelCache#purgeEntry(javax.naming.Name)
+ */
+ public boolean purgeEntry(Name name) throws IllegalStateException {
+ if (null != cache)
+ return cache.remove(name);
+ return false;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.directory.odm.SecondLevelCache#putEntry(javax.naming.Name,
+ * javax.naming.directory.Attributes)
+ */
+ public void putEntry(Name name, Attributes a) {
+ if (null != cache) {
+ cache.put(new Element(name, a));
+ if (logger.isDebugEnabled())
+ logger.debug("Caching entry for " + name);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.directory.odm.SecondLevelCache#clear()
+ */
+ public void clear() throws IllegalStateException, IOException {
+ if (null != cache)
+ cache.removeAll();
+ }
+}
Propchange: directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/EhCacheSecondLevelCache.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/Filter.java
URL: http://svn.apache.org/viewvc/directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/Filter.java?rev=606228&view=auto
==============================================================================
--- directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/Filter.java (added)
+++ directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/Filter.java Fri Dec 21 08:03:46 2007
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * 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.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author levigo
+ */
+public class Filter {
+ private static final Pattern SHIFT_PATTERN = Pattern
+ .compile("(.*?)(?:\\{(\\d+)\\}(.*?))*");
+
+ private String expression;
+ private Object args[];
+
+ public Filter(String expression, Object... args) {
+ this.expression = expression;
+ this.args = args;
+ }
+
+ public String getExpression(int argumentOffset) {
+ if (argumentOffset == 0) {
+ return expression;
+ }
+
+ Matcher m = SHIFT_PATTERN.matcher(expression);
+ assert m.matches() : "SHIFT_PATTERN doesn't match";
+
+ int groupCount = m.groupCount();
+ StringBuffer sb = new StringBuffer(m.group(1));
+ int i = 2;
+ while (i <= groupCount) {
+ int groupNumber = Integer.parseInt(m.group(i));
+ sb.append("{").append(groupNumber + argumentOffset).append("}");
+ i++;
+ if (i <= groupCount) {
+ sb.append(m.group(i));
+ i++;
+ }
+ }
+
+ return sb.toString();
+ }
+
+ public void fillArguments(Object target[], int argumentOffset) {
+ System.arraycopy(args, 0, target, argumentOffset, args.length);
+ }
+
+ public Object[] getArgs() {
+ return args;
+ }
+
+ public int getArgumentCount() {
+ return args.length;
+ }
+}
Propchange: directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/Filter.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/GroupMapping.java
URL: http://svn.apache.org/viewvc/directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/GroupMapping.java?rev=606228&view=auto
==============================================================================
--- directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/GroupMapping.java (added)
+++ directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/GroupMapping.java Fri Dec 21 08:03:46 2007
@@ -0,0 +1,260 @@
+/*******************************************************************************
+ * 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.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.ModificationItem;
+
+/**
+ * This class maps a group type (group, groupOfNames, groupOfUniqueNames, etc.)
+ * where the group members are represented by a multi-valued attribute.
+ *
+ * @author levigo
+ */
+public final class GroupMapping extends TypeMapping {
+ /**
+ * The name of the attribute holding the member references
+ */
+ private final String memberAttribute;
+ private AttributeMapping memberMapping;
+
+ /**
+ * @param className
+ * @param baseDN
+ * @param searchFilter
+ * @param objectClasses
+ * @param canUpdate
+ * @param keyClass
+ * @throws Exception
+ */
+ public GroupMapping(String className, String baseDN, String searchFilter,
+ String objectClasses, String keyClass, String memberAttribute)
+ throws Exception {
+ super(className, baseDN, searchFilter, objectClasses, keyClass);
+ this.memberAttribute = memberAttribute;
+ }
+
+ public void addMembers(AttributeMapping memberAttribute) {
+ // we just handle the members like any other attribute. This may change in
+ // the future.
+ add(memberAttribute);
+ }
+
+ /**
+ * Add a member to the mapped group. This method is called by the mapper of a
+ * member object if it detects a missing association from this group to
+ * itself.
+ *
+ * @param group the group object
+ * @param memberField the name of the member field
+ * @param memberDN member's DN
+ * @param tx current transaction
+ * @throws DirectoryException
+ * @throws NamingException
+ */
+ void addMember(Object group, String memberField, String memberDN,
+ Transaction tx) throws DirectoryException, NamingException {
+ memberDN = getDirectoryFacade().fixNameCase(memberDN);
+
+ final DirContext ctx = tx.getContext(getDirectoryFacade());
+ final String groupDN = getDN(group);
+
+ final Name groupName = getDirectoryFacade().makeRelativeName(groupDN);
+
+ ModificationItem mod = null;
+
+ // if the member attribute requires a dummy and the dummy is present, remove
+ // it.
+ if (memberMapping.cardinality == Cardinality.ONE_OR_MANY
+ && !memberField.equals("member") && !memberField.equals("memberOf")) {
+ final Attribute membersAttribute = ctx.getAttributes(groupName,
+ new String[]{memberField}).get(memberAttribute);
+
+ if (null != membersAttribute) {
+ final String dummy = getDirectoryFacade().getDummyMember();
+ if (membersAttribute.contains(dummy))
+ membersAttribute.remove(dummy);
+ membersAttribute.add(memberDN);
+
+ mod = new ModificationItem(DirContext.REPLACE_ATTRIBUTE,
+ membersAttribute);
+ }
+ }
+
+ if (null == mod)
+ mod = new ModificationItem(DirContext.ADD_ATTRIBUTE, new BasicAttribute(
+ memberField, memberDN));
+
+ final ModificationItem[] mods = new ModificationItem[]{mod};
+
+ DiropLogger.LOG.logModify(groupDN, mods, "add member to group");
+
+ ctx.modifyAttributes(getDirectoryFacade().makeRelativeName(groupDN), mods);
+ }
+
+ /**
+ * Remove a member from the mapped group
+ *
+ * @param group the group object
+ * @param memberField the member field name
+ * @param memberDN the member DN
+ * @param tx current transaction
+ * @throws DirectoryException
+ * @throws NamingException
+ */
+ void removeMember(Object group, String memberField, String memberDN,
+ Transaction tx) throws DirectoryException, NamingException {
+ memberDN = getDirectoryFacade().fixNameCase(memberDN);
+
+ final DirContext ctx = tx.getContext(getDirectoryFacade());
+ final String groupDN = getDN(group);
+
+ final Name groupName = getDirectoryFacade().makeRelativeName(groupDN);
+
+ ModificationItem mod = null;
+
+ // if the member attribute requires a dummy and the last member is removed,
+ // re-add dummy.
+ if (memberMapping.cardinality == Cardinality.ONE_OR_MANY
+ && !memberField.equals("member") && !memberField.equals("memberOf")) {
+ final Attribute membersAttribute = ctx.getAttributes(groupName,
+ new String[]{memberField}).get(memberAttribute);
+
+ // members attribute not present - should not happen
+ if (null == membersAttribute)
+ mod = new ModificationItem(DirContext.ADD_ATTRIBUTE,
+ new BasicAttribute(memberField, getDirectoryFacade()
+ .getDummyMember()));
+ else {
+ membersAttribute.remove(memberDN);
+
+ // if we removed the last value, add the dummy member
+ if (membersAttribute.size() == 0)
+ membersAttribute.add(getDirectoryFacade().getDummyMember());
+
+ mod = new ModificationItem(DirContext.REPLACE_ATTRIBUTE,
+ membersAttribute);
+ }
+ }
+
+ if (null == mod)
+ mod = new ModificationItem(DirContext.REMOVE_ATTRIBUTE,
+ new BasicAttribute(memberField, memberDN));
+
+ final ModificationItem[] mods = new ModificationItem[]{mod};
+
+ DiropLogger.LOG.logModify(groupDN, mods, "remove member from group");
+
+ ctx.modifyAttributes(getDirectoryFacade().makeRelativeName(groupDN), mods);
+ }
+
+ public boolean isInDirectory(Object group, String memberField, String dn,
+ Transaction tx) throws NamingException, DirectoryException {
+ // to query if the Object is in the Directory or not
+ final DirContext ctx = tx.getContext(getDirectoryFacade());
+ final String groupDN = getDN(group);
+
+ final Attributes attrs = ctx.getAttributes(getDirectoryFacade()
+ .makeRelativeName(groupDN), new String[]{memberField});
+ final Attribute a = attrs.getAll().next();
+ int k = 0;
+ while (k <= a.size() - 1) {
+ final String as = a.get(k).toString();
+ if (as.equalsIgnoreCase(dn))
+ return true;
+ k++;
+ }
+ return false;
+ }
+
+ @Override
+ protected void initPostLoad() {
+ super.initPostLoad();
+
+ // make sure that the member attribute points to a OneToManyMapping
+ for (final AttributeMapping am : attributes)
+ if (am.fieldName.equals(memberAttribute))
+ if (am instanceof OneToManyMapping) {
+ this.memberMapping = am;
+ break;
+ } else
+ throw new IllegalStateException("MemberAttribute " + memberAttribute
+ + " of GroupMapping " + getMappedType()
+ + " is not mapped using one-to-many");
+
+ if (null == memberMapping)
+ throw new IllegalStateException("MemberAttribute " + memberAttribute
+ + " of GroupMapping missing corresponding one-to-many mapping");
+ }
+
+ //
+ // @Override
+ // protected void updateAttributes(Attributes currentAttributes,
+ // Attribute currentValues, Attribute newValues,
+ // List<ModificationItem> mods, Object o) throws NamingException,
+ // DirectoryException {
+ // if (newValues.getID().equalsIgnoreCase(memberAttribute))
+ // updateMembers(currentValues, currentAttributes, newValues, mods, o);
+ // else
+ // super.updateAttributes(currentAttributes, currentValues, newValues, mods,
+ // o);
+ // }
+ //
+ // /**
+ // * Update the members-attribute.
+ // *
+ // * @param currentValues
+ // * @param currentAttributes
+ // * @param newValues
+ // * @param mods
+ // * @param o
+ // * @throws NamingException
+ // * @throws DirectoryException
+ // */
+ // private void updateMembers(Attribute currentValues,
+ // Attributes currentAttributes, Attribute newValues,
+ // List<ModificationItem> mods, Object o) throws NamingException,
+ // DirectoryException {
+ //
+ // final Group group = (Group) o;
+ //
+ // final Set members = group.getMembers();
+ // if (!Proxy.isProxyClass(members.getClass())) {
+ // final Attribute attributeToEdit = new BasicAttribute(newValues.getID());
+ //
+ // for (final Object member : members) {
+ // final TypeMapping memberMapping = getMapping().getMapping(
+ // member.getClass());
+ // String memberDn = memberMapping.getDN(member);
+ // memberDn = getDirectoryFacade().fixNameCase(memberDn);
+ // attributeToEdit.add(memberDn);
+ // }
+ // if (attributeToEdit.size() == 0)
+ // attributeToEdit.add(OneToManyMapping.getDUMMY_MEMBER());
+ //
+ // mods.add(new ModificationItem(DirContext.REPLACE_ATTRIBUTE,
+ // attributeToEdit));
+ // }
+ // }
+}
Propchange: directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/GroupMapping.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/LDAPConnectionDescriptor.java
URL: http://svn.apache.org/viewvc/directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/LDAPConnectionDescriptor.java?rev=606228&view=auto
==============================================================================
--- directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/LDAPConnectionDescriptor.java (added)
+++ directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/LDAPConnectionDescriptor.java Fri Dec 21 08:03:46 2007
@@ -0,0 +1,344 @@
+/*******************************************************************************
+ * 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.io.Serializable;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import javax.security.auth.callback.CallbackHandler;
+
+/**
+ * A simple structure object to hold information about an LDAP connection.
+ *
+ * @author levigo
+ */
+public class LDAPConnectionDescriptor implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * The LDAP service provider type to be used.
+ */
+ public enum ProviderType {
+ /**
+ * Default SUN provider
+ */
+ SUN("com.sun.jndi.ldap.LdapCtxFactory"),
+
+ /**
+ * Apache directory server in embedded mode.
+ */
+ APACHE_DS_EMBEDDED("org.apache.directory.server.jndi.ServerContextFactory");
+
+ private final String className;
+
+ ProviderType(String className) {
+ this.className = className;
+ }
+
+ public String getClassName() {
+ return className;
+ }
+ }
+
+ /**
+ * The connection method to be used
+ */
+ public enum ConnectionMethod {
+ /**
+ * Unencrypted
+ */
+ PLAIN,
+
+ /**
+ * Use Secure Socket Layer
+ */
+ SSL,
+
+ /**
+ * Use Start TLS
+ */
+ START_TLS;
+ }
+
+ /**
+ * The authentication method to use
+ */
+ public enum AuthenticationMethod {
+ /**
+ * Anonymous bind
+ */
+ NONE,
+ /**
+ * Username/password authentication
+ */
+ SIMPLE,
+ /**
+ * SASL
+ */
+ SASL;
+ }
+
+ /**
+ * The DirectoryType describes a directory server implementation.
+ */
+ public enum DirectoryType {
+ /**
+ * Microsoft Active Directory Windows 2003 R2
+ */
+ MS_2003R2(true),
+ /**
+ * Microsoft Active Directory + Services For Unix (SFU)
+ */
+ MS_SFU(true),
+ /**
+ * Generic RFC style directory (OpenLDAP, Apache DS, ...)
+ */
+ GENERIC_RFC(false);
+
+ /**
+ * Flag indicating whether the directory implementations uses all upper case
+ * RDN names.
+ */
+ private final boolean upperCaseRDNAttributeNames;
+
+ private DirectoryType(boolean upperCaseRDNAttributeNames) {
+ this.upperCaseRDNAttributeNames = upperCaseRDNAttributeNames;
+ }
+
+ /**
+ * Return whether directories of this type require all RDN attribute names
+ * to be upper case. I.e. <code>CN=foo,DC=bar,DC=baz</code> instead of
+ * <code>cn=foo,dc=bar,dc=baz</code>.
+ *
+ * @return
+ */
+ public boolean requiresUpperCaseRDNAttributeNames() {
+ return upperCaseRDNAttributeNames;
+ }
+ }
+
+ private ProviderType providerType = ProviderType.SUN;
+
+ private ConnectionMethod connectionMethod;
+
+ private AuthenticationMethod authenticationMethod;
+
+ private String hostname;
+
+ private short portNumber;
+
+ private String baseDN = "";
+
+ private CallbackHandler callbackHandler;
+
+ private Hashtable<String, Object> extraEnv = new Hashtable<String, Object>();
+
+ // private String referralPreference = "ignore";
+ private final String referralPreference = "follow";
+
+ /**
+ * Flag indicating whether this connection should be read-only. This is
+ * currently only a hint and may or may not be honored by downstream classes.
+ */
+ private boolean readOnly;
+
+ /**
+ * Default constructor. Sets a few reasonable defaults.
+ *
+ * @param connectionDescriptor
+ */
+ public LDAPConnectionDescriptor() {
+ this.connectionMethod = ConnectionMethod.PLAIN;
+ this.authenticationMethod = AuthenticationMethod.NONE;
+ this.hostname = "localhost";
+ this.portNumber = -1; // getPortNumber() will figure out a default!
+ this.baseDN = "";
+ }
+
+ /**
+ * Copy constructor
+ *
+ * @param connectionDescriptor
+ */
+ public LDAPConnectionDescriptor(LDAPConnectionDescriptor lcd) {
+ this.providerType = lcd.providerType;
+ this.connectionMethod = lcd.connectionMethod;
+ this.authenticationMethod = lcd.authenticationMethod;
+ this.hostname = lcd.hostname;
+ this.portNumber = lcd.portNumber;
+ this.baseDN = lcd.baseDN;
+ this.callbackHandler = lcd.callbackHandler;
+ this.extraEnv.putAll(lcd.extraEnv);
+ this.readOnly = lcd.readOnly;
+ }
+
+ public String getLDAPUrl() {
+ switch (providerType){
+ case SUN :
+ default :
+ return (connectionMethod != ConnectionMethod.SSL ? "ldap" : "ldaps")
+ + "://" + hostname + ":" + getPortNumber() + "/" + baseDN;
+ case APACHE_DS_EMBEDDED :
+ return baseDN;
+ }
+ }
+
+ /*
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ try {
+ final Hashtable<String, Object> env = extraEnv;
+ int hashCode = 0;
+ for (final Enumeration i = env.keys(); i.hasMoreElements();) {
+ final Object key = i.nextElement();
+ if (null != key)
+ hashCode ^= key.hashCode();
+ else
+ hashCode ^= 98435234;
+ if (null != env.get(key))
+ hashCode ^= env.get(key).hashCode();
+ else
+ hashCode ^= 21876381;
+ }
+
+ return hashCode ^ portNumber ^ hostname.hashCode()
+ ^ callbackHandler.hashCode() ^ connectionMethod.hashCode()
+ ^ authenticationMethod.hashCode();
+ } catch (final Exception e) {
+ return -9999;
+ }
+ }
+
+ public void setAuthenticationMethod(AuthenticationMethod authenticationMethod) {
+ this.authenticationMethod = authenticationMethod;
+ }
+
+ public AuthenticationMethod getAuthenticationMethod() {
+ return authenticationMethod;
+ }
+
+ public void setBaseDN(String baseDN) {
+ this.baseDN = baseDN;
+ }
+
+ public String getBaseDN() {
+ return baseDN;
+ }
+
+ /**
+ * Set the JAAS {@link CallbackHandler} used to supply authentication
+ * information.
+ *
+ * @param callbackHandler
+ */
+ public void setCallbackHandler(CallbackHandler callbackHandler) {
+ this.callbackHandler = callbackHandler;
+ }
+
+ /**
+ * Get the JAAS {@link CallbackHandler}.
+ *
+ * @return
+ */
+ public CallbackHandler getCallbackHandler() {
+ return callbackHandler;
+ }
+
+ public void setConnectionMethod(ConnectionMethod connectionMethod) {
+ this.connectionMethod = connectionMethod;
+ }
+
+ public ConnectionMethod getConnectionMethod() {
+ return connectionMethod;
+ }
+
+ /**
+ * Set extra environment settings to use.
+ *
+ * @param extraEnv
+ */
+ public void setExtraEnv(Hashtable<String, Object> extraEnv) {
+ this.extraEnv = extraEnv;
+ }
+
+ /**
+ * Get Hashtable of extra environment settings to use.
+ *
+ * @return
+ */
+ public Hashtable<String, Object> getExtraEnv() {
+ return extraEnv;
+ }
+
+ public void setHostname(String hostname) {
+ this.hostname = hostname;
+ }
+
+ public String getHostname() {
+ return hostname;
+ }
+
+ public void setPortNumber(short portNumber) {
+ this.portNumber = portNumber;
+ }
+
+ public short getPortNumber() {
+ if (portNumber > 0)
+ return portNumber;
+
+ if (connectionMethod == ConnectionMethod.SSL)
+ return 686;
+ else
+ return 389;
+ }
+
+ /**
+ * @return
+ */
+ public boolean isBaseDnSet() {
+ return getBaseDN() != null && getBaseDN().length() > 0;
+ }
+
+ public ProviderType getProviderType() {
+ return providerType;
+ }
+
+ public void setProviderType(ProviderType providerType) {
+ this.providerType = providerType;
+ }
+
+ public String getReferralPreference() {
+ return referralPreference;
+ }
+
+ public DirectoryFacade createDirectoryFacade() {
+ return new DirectoryFacade(this);
+ }
+
+ public void setReadOnly(boolean readOnly) {
+ this.readOnly = readOnly;
+ }
+
+ public boolean isReadOnly() {
+ return readOnly;
+ }
+}
Propchange: directory/sandbox/hennejg/odm/trunk/src/main/java/org/apache/directory/odm/LDAPConnectionDescriptor.java
------------------------------------------------------------------------------
svn:mime-type = text/plain