Author: kayyagari Date: Thu Nov 13 02:27:45 2008 New Revision: 713699 URL: http://svn.apache.org/viewvc?rev=713699&view=rev Log: o added javadoc o added many new methods Modified: directory/sandbox/kayyagari/apacheds-olm/src/main/java/org/apache/directory/olm/util/LdapPersistenceUtil.java Modified: directory/sandbox/kayyagari/apacheds-olm/src/main/java/org/apache/directory/olm/util/LdapPersistenceUtil.java URL: http://svn.apache.org/viewvc/directory/sandbox/kayyagari/apacheds-olm/src/main/java/org/apache/directory/olm/util/LdapPersistenceUtil.java?rev=713699&r1=713698&r2=713699&view=diff ============================================================================== --- directory/sandbox/kayyagari/apacheds-olm/src/main/java/org/apache/directory/olm/util/LdapPersistenceUtil.java (original) +++ directory/sandbox/kayyagari/apacheds-olm/src/main/java/org/apache/directory/olm/util/LdapPersistenceUtil.java Thu Nov 13 02:27:45 2008 @@ -19,6 +19,7 @@ */ package org.apache.directory.olm.util; + import java.io.InputStream; import java.lang.reflect.Field; import java.util.ArrayList; @@ -40,6 +41,7 @@ import org.apache.directory.server.core.entry.DefaultServerEntry; import org.apache.directory.server.core.entry.ServerEntry; import org.apache.directory.server.core.entry.ServerModification; +import org.apache.directory.server.core.filtering.EntryFilteringCursor; import org.apache.directory.server.schema.registries.AttributeTypeRegistry; import org.apache.directory.server.schema.registries.ObjectClassRegistry; import org.apache.directory.server.schema.registries.Registries; @@ -55,26 +57,33 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; + /** * - * TODO LdapPersistenceUtil. - * + * Utility class for persisting/updating the user created/fetched entries using CoreSession API. + * + * TODO a similar class needs to be created with JNDI interface for the users who don't depend on ADS core library. + * * @author Apache Directory Project * @version $Rev$, $Date$ */ public class LdapPersistenceUtil { + /** the session of the user*/ private CoreSession session; + private Registries registries; + private SyntaxRegistry syntaxRegistry; + private AttributeTypeRegistry attrRegistry; - - private final static Properties NAME_CLASS_MAP = new Properties(); - - /** static logger */ + + /** the map containing the mapping of Attribut/Objectclass names to corresponding FQCN */ + private final static Properties NAME_CLASS_MAP = new Properties(); + private static final Logger LOG = LoggerFactory.getLogger( LdapPersistenceUtil.class ); - - static + + static { try { @@ -82,52 +91,89 @@ NAME_CLASS_MAP.load( in ); in.close(); } - catch( Exception e ) + catch ( Exception e ) { + e.printStackTrace(); LOG.error( "could not load class mappings file classmapping.properties" ); } } - - public LdapPersistenceUtil(){} - + + + /** + * + * Creates an instance of LdapPersistenceUtil. + * + * @param registries the registries containing schema related info + * @param session the session oject of the user + */ public LdapPersistenceUtil( Registries registries, CoreSession session ) { - this.session = session; - this.registries = registries; - syntaxRegistry = registries.getSyntaxRegistry(); - attrRegistry = registries.getAttributeTypeRegistry(); + this.session = session; + this.registries = registries; + syntaxRegistry = registries.getSyntaxRegistry(); + attrRegistry = registries.getAttributeTypeRegistry(); } - - + + + /** + * + * Saves a user created entry in the server. + * + * @param entry the user created entry + * @throws Exception if any problems occur during saving an entry + */ public void save( Entry entry ) throws Exception { ServerEntry serverEntry = new DefaultServerEntry( registries ); - + LdapDN dn = new LdapDN( entry.getDN() ); dn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() ); - + serverEntry.setDn( dn ); - serverEntry.add( SchemaConstants.OBJECT_CLASS_AT, entry.getName() ); - Set attributes = entry.getAttributes(); - for( Attribute attr: attributes ) + addAttributesToServerEntry( entry, serverEntry ); + + Iterator itr = entry.getAdditionalEntries(); + while ( itr.hasNext() ) { + addAttributesToServerEntry( itr.next(), serverEntry ); + } + + session.add( serverEntry ); + } + + + /* + * get all the Attribute and add them to the ServerEntry + */ + private void addAttributesToServerEntry( Entry olmEntry, ServerEntry serverEntry ) throws Exception + { + serverEntry.add( SchemaConstants.OBJECT_CLASS_AT, olmEntry.getName() ); + + Set attributes = olmEntry.getAttributes(); + for ( Attribute attr : attributes ) + { + if ( attr.isNoUserModification() ) + { + continue; + } + boolean isHr = syntaxRegistry.lookup( attr.getSyntax() ).isHumanReadable(); - if( isHr ) + if ( isHr ) { - if( attr.isSingleValue() ) + if ( attr.isSingleValue() ) { - serverEntry.add( attr.getAttrName(), String.valueOf( attr.getAttrValue() ) ); + serverEntry.add( attr.getName(), String.valueOf( attr.getValue() ) ); } else { - Iterator values = attr.getAttrValues(); - if( values != null ) + Iterator values = attr.getValues(); + if ( values != null ) { - while( values.hasNext() ) + while ( values.hasNext() ) { Object v = values.next(); - serverEntry.add( attr.getAttrName(), String.valueOf( v ) ); + serverEntry.add( attr.getName(), String.valueOf( v ) ); } } } @@ -135,215 +181,473 @@ else { // assuming all non-HR values as byte[] - serverEntry.add( attr.getAttrName(), ( byte[] ) attr.getAttrValue() ); + serverEntry.add( attr.getName(), ( byte[] ) attr.getValue() ); } } - - session.add( serverEntry ); } - + + /** + * + * Fetches an entry from the server associated with the given DN + * + * @param dn the distinguished name of entry to be fetched + * @return an Entry + * @throws Exception if any problems occur while fetching the entry + */ public Entry fetch( String dn ) throws Exception { return fetch( new LdapDN( dn ) ); } - - //FIXME need to support extensible objectclasses + + + /** + * @see #fetch(String) + */ public Entry fetch( LdapDN dn ) throws Exception { - if( ! dn.isNormalized() ) + if ( !dn.isNormalized() ) { dn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() ); } - - ClonedServerEntry clonedServerEntry = session.lookup( dn ); - - if( clonedServerEntry == null ) + + ClonedServerEntry clonedServerEntry = null; + + try + { + clonedServerEntry = session.lookup( dn ); + } + catch ( Exception e ) + { + LOG.error( "No entry found with dn '{}'", dn ); + } + + if ( clonedServerEntry == null ) { return null; } - + ServerEntry serverEntry = clonedServerEntry.getOriginalEntry(); - + + return convertToOlmEntry( serverEntry ); + } + + + /* + * creates a new OLM Entry object and fills with the data from the given ServerEntry object + */ + private Entry convertToOlmEntry( ServerEntry serverEntry ) throws Exception + { EntryAttribute objectClassAttr = serverEntry.get( SchemaConstants.OBJECT_CLASS_AT ); - + Iterator> itr = objectClassAttr.iterator(); - + List ocList = new ArrayList(); List ocNameList = new ArrayList(); - + ObjectClassRegistry ocRegistry = registries.getObjectClassRegistry(); - - while( itr.hasNext() ) + + while ( itr.hasNext() ) { String objClassName = ( String ) itr.next().get(); ObjectClass oc = ocRegistry.lookup( objClassName ); - if( oc.isStructural() ) + if ( !oc.getOid().equals( SchemaConstants.TOP_OC_OID ) ) { ocList.add( oc ); ocNameList.add( objClassName.toLowerCase() ); } } - - for( ObjectClass oc : ocList ) + + for ( ObjectClass oc : ocList ) { - if( oc.getSuperClasses() != null ) + if ( oc.getSuperClasses() != null ) { - for( ObjectClass superOc : oc.getSuperClasses() ) + for ( ObjectClass superOc : oc.getSuperClasses() ) { - if( superOc.isStructural() ) + if ( superOc.isStructural() ) { ocNameList.remove( superOc.getName().toLowerCase() ); } } } } - - System.out.println( "ocNameList: " + ocNameList ); - Entry entry = ( Entry ) getClass( ocNameList.get( 0 ) ).newInstance(); + if ( LOG.isDebugEnabled() ) + { + LOG.debug( "ocNameList: " + ocNameList ); + } + + Set additionalEntires = new HashSet(); + + Entry parentEntry = null; + + for ( String s : ocNameList ) + { + ObjectClass oc = registries.getObjectClassRegistry().lookup( s ); + Entry entry = null; + + entry = ( Entry ) getClass( s ).newInstance(); + loadServerEntry( serverEntry, entry ); + + if ( oc.isStructural() ) // one and the only one structural class exists + { + parentEntry = entry; + parentEntry.setDN( serverEntry.getDn().getUpName() ); + } + + // should be set last to avoid any interception during initialization + setEntryInterceptor( entry ); + } + + for ( Entry e : additionalEntires ) + { + parentEntry.addEntry( e ); + } - entry.setDN( serverEntry.getDn().getUpName() ); - + return parentEntry; + } + + + /* + * copies the attributes present in the ServerEntry to the entry object + */ + private void loadServerEntry( ServerEntry serverEntry, Entry entry ) throws Exception + { Field[] fields = entry.getClass().getDeclaredFields(); - - Map nameToFieldMap = new HashMap(); - for( Field f : fields ) + + Map nameToFieldMap = new HashMap(); + for ( Field f : fields ) { f.setAccessible( true ); nameToFieldMap.put( f.getName().toLowerCase(), f ); } - + Set attrTypes = serverEntry.getAttributeTypes(); - for( EntryAttribute attr : serverEntry ) + for ( EntryAttribute attr : serverEntry ) { Field f = nameToFieldMap.get( attr.getId() ); - - if( f == null ) + + if ( f == null ) { continue; } - + Attribute olmAttr = ( Attribute ) f.get( entry ); - - if( olmAttr == null ) + + if ( olmAttr == null ) { olmAttr = ( Attribute ) Class.forName( f.getType().getName() ).newInstance(); } - if( attr.isHR() ) + if ( attr.isHR() ) { - if( ! registries.getAttributeTypeRegistry().lookup( attr.getId() ).isSingleValue() ) + if ( !registries.getAttributeTypeRegistry().lookup( attr.getId() ).isSingleValue() ) { Iterator> values = attr.getAll(); - + Set set = new HashSet(); - while( values.hasNext() ) + while ( values.hasNext() ) { set.add( values.next().get() ); } - + // java.util.Set is the common container for all the multi valued attribute values olmAttr.getClass().getDeclaredMethod( "setValues", Set.class ).invoke( olmAttr, set ); } else { - olmAttr.getClass().getDeclaredMethod( "setValue", olmAttr.getJavaType() ).invoke( olmAttr, attr.get().get() ); + olmAttr.getClass().getDeclaredMethod( "setValue", olmAttr.getJavaType() ).invoke( olmAttr, + convertToCorrectType( attr.get().get(), olmAttr.getJavaType() ) ); } - ( ( InterceptFieldEnabled ) olmAttr ).setInterceptFieldCallback( new AttributeFieldInterceptor( entry ) ); - f.set( entry, olmAttr ); + + } + else + { + olmAttr.getClass().getDeclaredMethod( "setValue", olmAttr.getJavaType() ).invoke( olmAttr, + attr.get().get() ); } + + ( ( InterceptFieldEnabled ) olmAttr ).setInterceptFieldCallback( new AttributeFieldInterceptor( entry ) ); + f.set( entry, olmAttr ); } - - ( ( InterceptFieldEnabled ) entry ).setInterceptFieldCallback( new EntryFieldInterceptor() ); - return entry; } - - - public void update( Entry entry ) throws Exception + + + /** + * + * updates the given entry. + * + * @param entry the entry to be updated + * @throws Exception if any problems occur during modify operation + */ + public void modify( Entry entry ) throws Exception { - ServerEntry serverEntry = new DefaultServerEntry( registries ); - LdapDN dn = new LdapDN( entry.getDN() ); dn.normalize( registries.getAttributeTypeRegistry().getNormalizerMapping() ); - - serverEntry.setDn( dn ); -// serverEntry.add( SchemaConstants.OBJECT_CLASS_AT, entry.getName() ); - EntryFieldInterceptor interceptor = ( EntryFieldInterceptor ) ( ( InterceptFieldEnabled ) entry ).getInterceptFieldCallback(); - - if( ! interceptor.isDirty() ) + List modList = new ArrayList(); + + EntryFieldInterceptor interceptor = getEntryInterceptor( entry ); + + // get the mod items from the main structural entry + if ( interceptor.isDirty() ) { - LOG.debug( "None of the Attributes of Entry changed no need to update" ); - return; + collectModItemsFromEntry( entry, interceptor, modList ); + } + + Iterator itr = entry.getAdditionalEntries(); + while ( itr.hasNext() ) + { + Entry e = itr.next(); + interceptor = getEntryInterceptor( e ); + if ( interceptor == null ) // for a newly added entry the interceptor will be null + { + EntryAttribute tmpAttr = new DefaultServerAttribute( attrRegistry + .lookup( SchemaConstants.OBJECT_CLASS_AT ), e.getName() ); + Modification mod = new ServerModification( ModificationOperation.ADD_ATTRIBUTE, tmpAttr ); + modList.add( mod ); + + for ( Attribute attr : e.getAttributes() ) + { + tmpAttr = convertToEntryAttribute( attr ); + mod = new ServerModification( ModificationOperation.ADD_ATTRIBUTE, tmpAttr ); + modList.add( mod ); + } + } + else + { + collectModItemsFromEntry( e, interceptor, modList ); + } } - + + // then check the dirty *field* map for possible addition of new object classes for adding + // Map dirtyFieldMap = interceptor.getDirtyFieldMap(); + // if( ! dirtyFieldMap.isEmpty() ) + // { + // Set oldEntries = ( Set ) dirtyFieldMap.get( "additionalEntries" ); + // } + + session.modify( dn, modList ); + } + + + /** + * + * adds an Attribute to an exisitng entry. + * + * @param dn the DN of existing entry + * @param attributeName name of the attribute to be added + * @param values one or more values of the attribute to be added + * @throws Exception if the attribute cannot be added + */ + public void addAttributeToEntry( LdapDN dn, String attributeName, Object... values ) throws Exception + { + List modList = new ArrayList( 1 ); + + Modification mod = new ServerModification(); + mod.setOperation( ModificationOperation.ADD_ATTRIBUTE ); + EntryAttribute attr = new DefaultServerAttribute( attrRegistry.lookup( attributeName ) ); + mod.setAttribute( attr ); + + for ( Object obj : values ) + { + if ( obj == null ) + { + throw new NullPointerException( "Null value for attribute " + attributeName ); + } + + if ( obj instanceof byte[] ) + { + attr.add( ( byte[] ) obj ); + } + else + { + attr.add( String.valueOf( obj ) ); + } + } + + modList.add( mod ); + + session.modify( dn, modList ); + } + + + /** + * + * replaces an attributes value Or removes an attribute from the entry associated with the given dn. + * + * @param dn the DN of the entry to be modified + * @param attributeName the attribute's name + * @param obj value to be replaced, if it is null the attribute will be completely removed from entry + * @throws Exception if there are problems in modifying the entry + */ + public void replaceOrRemove( LdapDN dn, String attributeName, Object obj ) throws Exception + { List modList = new ArrayList(); - + + Modification mod = new ServerModification(); + EntryAttribute attr; + + if ( obj == null ) + { + mod.setOperation( ModificationOperation.REMOVE_ATTRIBUTE ); + attr = new DefaultServerAttribute( attrRegistry.lookup( attributeName ) ); + } + else + { + mod.setOperation( ModificationOperation.REPLACE_ATTRIBUTE ); + + if ( obj instanceof byte[] ) + { + attr = new DefaultServerAttribute( attrRegistry.lookup( attributeName ), ( byte[] ) obj ); + } + else + { + attr = new DefaultServerAttribute( attrRegistry.lookup( attributeName ), String.valueOf( obj ) ); + } + } + + mod.setAttribute( attr ); + modList.add( mod ); + + session.modify( dn, modList ); + } + + + /** + * + * searches the server with the given filter. + * + * @param dn the DN on/under which the search should be performed + * @param filter the search filter expression + * @return a list of entries + * @throws Exception if the search fails + */ + public List search( LdapDN dn, String filter ) throws Exception + { + List entries = new ArrayList(); + + EntryFilteringCursor cursor = session.search( dn, filter ); + + while ( cursor.next() ) + { + ServerEntry serverEntry = cursor.get().getOriginalEntry(); + entries.add( convertToOlmEntry( serverEntry ) ); + } + + return entries; + } + + + public void modifyDN( LdapDN dn, LdapDN newParent ) throws Exception + { + session.move( dn, newParent ); + } + + + public void delete( String dn ) throws Exception + { + if ( dn == null ) + { + return; + } + + delete( new LdapDN( dn ) ); + } + + + public void delete( LdapDN dn ) throws Exception + { + if ( dn == null ) + { + return; + } + + if ( !dn.isNormalized() ) + { + dn.normalize( attrRegistry.getNormalizerMapping() ); + } + + session.delete( dn ); + } + + + /* + * created ModItems based on the dirty status of fields present in the given entry + */ + private void collectModItemsFromEntry( Entry entry, EntryFieldInterceptor interceptor, List modList ) + throws Exception + { Map dirtyMap = interceptor.getDirtyAttrMap(); Class entryClass = entry.getClass(); - - for( String fieldName : dirtyMap.keySet() ) + + for ( String fieldName : dirtyMap.keySet() ) { - Field f = entryClass.getDeclaredField( fieldName ); - // TODO need to remove this if block, this is for finding errors related to fieldnames during development - if( f == null ) + Field f = null; + f = entryClass.getDeclaredField( fieldName ); + if ( f == null ) { - throw new Exception( "Attribute field not found with name " + fieldName ); + continue; // TODO need to add support for adding DSA params like timestamp, modifiedUser etc.. as these fields won't exist in any objclass definition } - + f.setAccessible( true ); - + Attribute newAttr = ( Attribute ) f.get( entry ); Attribute oldAttr = dirtyMap.get( fieldName ); - if( newAttr == null ) // remove + if ( newAttr == null ) // remove { - EntryAttribute tmpAttr = convertToEntryAttribute( oldAttr ); + EntryAttribute tmpAttr = new DefaultServerAttribute( attrRegistry.lookup( oldAttr.getName() ) ); Modification mod = new ServerModification( ModificationOperation.REMOVE_ATTRIBUTE, tmpAttr ); modList.add( mod ); } - else if( oldAttr != null && newAttr != null ) // replace + else if ( oldAttr != null && newAttr != null ) // replace { + // if attribute cannot be modified by user skip it + if ( oldAttr.isNoUserModification() ) + { + continue; + } + // TODO need to have an efficient way to calculate delta of differences // for performing the update operation currently it send the whole attribute EntryAttribute tmpAttr = convertToEntryAttribute( newAttr ); Modification mod = new ServerModification( ModificationOperation.REPLACE_ATTRIBUTE, tmpAttr ); modList.add( mod ); } - else if( oldAttr == null ) // add + else if ( oldAttr == null ) // add { EntryAttribute tmpAttr = convertToEntryAttribute( newAttr ); Modification mod = new ServerModification( ModificationOperation.ADD_ATTRIBUTE, tmpAttr ); modList.add( mod ); } } - - session.modify( dn, modList ); } - - - + + + /* + * converts a OLM Attribute to EntryAttribute + */ private EntryAttribute convertToEntryAttribute( Attribute attr ) throws Exception { - if( attr == null ) + if ( attr == null ) { return null; } - - EntryAttribute entryAttr = new DefaultServerAttribute( attrRegistry.lookup( attr.getAttrName() ) ); - - if( syntaxRegistry.lookup( attr.getSyntax() ).isHumanReadable() ) + + EntryAttribute entryAttr = new DefaultServerAttribute( attrRegistry.lookup( attr.getName() ) ); + + if ( syntaxRegistry.lookup( attr.getSyntax() ).isHumanReadable() ) { - if( attr.isSingleValue() ) + if ( attr.isSingleValue() ) { - entryAttr.add( String.valueOf( attr.getAttrValue() ) ); + entryAttr.add( String.valueOf( attr.getValue() ) ); } else { - Iterator itr = attr.getAttrValues(); - while( itr.hasNext() ) + Iterator itr = attr.getValues(); + while ( itr.hasNext() ) { entryAttr.add( String.valueOf( itr.next() ) ); } @@ -351,16 +655,100 @@ } else { - entryAttr.add( ( byte[] ) attr.getAttrValue() ); + entryAttr.add( ( byte[] ) attr.getValue() ); } - + return entryAttr; } - + private Class getClass( String propName ) throws Exception { String className = NAME_CLASS_MAP.getProperty( propName ); return Class.forName( className ); } + + + private EntryFieldInterceptor getEntryInterceptor( Entry entry ) + { + return ( EntryFieldInterceptor ) ( ( InterceptFieldEnabled ) entry ).getInterceptFieldCallback(); + } + + + private void setEntryInterceptor( Entry entry ) + { + ( ( InterceptFieldEnabled ) entry ).setInterceptFieldCallback( new EntryFieldInterceptor() ); + } + + + private Object convertToCorrectType( Object value, Class claz ) + { + if ( value == null ) + { + return null; + } + + if ( claz == String.class ) + { + return String.valueOf( value ); + } + else if ( claz == Integer.class ) + { + return Integer.valueOf( value.toString() ); + } + else if ( claz == Long.class ) + { + return Long.valueOf( value.toString() ); + } + else if ( claz == Boolean.class ) + { + return Boolean.valueOf( value.toString() ); + } + else if ( claz == Short.class ) + { + return Short.valueOf( value.toString() ); + } + + // this will help during early development to figure out the missing pieces + throw new IllegalArgumentException( "Invalid class type " + claz + " for the attribute value " + value ); + } + + + /** + * @see #exists(LdapDN) + */ + public boolean exists( String dn ) throws Exception + { + if ( dn == null ) + { + return false; + } + + return exists( new LdapDN( dn ) ); + } + + + /** + * + * checks the existene of the given DN. + * + * @param dn the DN to be checked + * @return true if the DN exists, false otherwise + * @throws Exception if the search for given DN fails + */ + public boolean exists( LdapDN dn ) throws Exception + { + if ( dn == null ) + { + return false; + } + + if ( !dn.isNormalized() ) + { + dn.normalize( attrRegistry.getNormalizerMapping() ); + } + + return session.exists( dn ); + } + }