directory-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From akaras...@apache.org
Subject svn commit: r762433 [2/2] - in /directory/apacheds/branches/ldif-partition: avl-partition/src/main/java/org/apache/directory/server/core/partition/avl/ avl-partition/src/main/java/org/apache/directory/server/core/partition/impl/ oracle-partition/ oracl...
Date Mon, 06 Apr 2009 17:40:03 GMT
Added: directory/apacheds/branches/ldif-partition/oracle-partition/src/main/java/org/apache/directory/server/partition/impl/oracle/OracleAttribute.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/ldif-partition/oracle-partition/src/main/java/org/apache/directory/server/partition/impl/oracle/OracleAttribute.java?rev=762433&view=auto
==============================================================================
--- directory/apacheds/branches/ldif-partition/oracle-partition/src/main/java/org/apache/directory/server/partition/impl/oracle/OracleAttribute.java
(added)
+++ directory/apacheds/branches/ldif-partition/oracle-partition/src/main/java/org/apache/directory/server/partition/impl/oracle/OracleAttribute.java
Mon Apr  6 17:40:02 2009
@@ -0,0 +1,331 @@
+/*
+ *  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.server.partition.impl.oracle;
+
+import java.io.ByteArrayInputStream;
+import java.security.DigestInputStream;
+import java.security.MessageDigest;
+import java.sql.SQLData;
+import java.sql.SQLException;
+import java.sql.SQLInput;
+import java.sql.SQLOutput;
+
+import oracle.jdbc.OracleCallableStatement;
+import oracle.jdbc.OracleConnection;
+import oracle.jdbc.OraclePreparedStatement;
+import oracle.jdbc.OracleResultSet;
+import oracle.jdbc.OracleTypes;
+import oracle.sql.BLOB;
+import oracle.sql.CLOB;
+
+import org.apache.directory.shared.ldap.util.Base64;
+
+/**
+ * This class is used like a one to one mapping for the 
+ * LDAP_ATTRIBUTE pl/sql object. It is encoded and decoded
+ * directly from the jdbc driver calling the readSQL and
+ * writeSQL methods of the SQLData interface.
+ */
+public class OracleAttribute implements SQLData
+{
+    private String sqlType= "LDAP_ATTRIBUTE";
+    private OracleConnection connection;
+    
+    private String name;
+    private String svalue;
+    private byte[] bvalue;
+    private String type;
+    private Long   bvalueid;
+    private Long   cvalueid;
+    
+    /**
+     * Used from jdbc driver to instanciate the class 
+     */
+    public OracleAttribute ()
+    {}
+    
+    /**
+     * Used from OracleEntry when converting a ServerEntry
+     * for a string value
+     */
+    public OracleAttribute (String name, String value, String type, OracleConnection connection)
+    {
+        this.name= name;
+        this.svalue= value;
+        this.type= type;
+        this.connection= connection;
+    }
+
+    /**
+     * Used from OracleEntry when converting a ServerEntry
+     * for a binary value
+     */
+    public OracleAttribute (String name, byte[] value, String type, OracleConnection connection)
+    {
+        this.name= name;
+        this.bvalue= value;
+        this.type= type;
+        this.connection= connection;
+    }
+    
+    /**
+     * Sets a connection to read LOBs values
+     * @param connection
+     */
+    public void setConnection(OracleConnection connection)
+    {
+        this.connection= connection;
+    }
+
+    /**
+     * @see SQLData
+     */
+    public String getSQLTypeName() throws SQLException
+    {
+        return sqlType;
+    }
+
+    /**
+     * @see SQLData
+     */
+    public void readSQL( SQLInput stream, String sqlType ) throws SQLException
+    {
+        this.sqlType= sqlType;
+        
+        name= stream.readString();
+        type= stream.readString();
+        svalue= stream.readString();
+        bvalue= stream.readBytes();
+        cvalueid= stream.readLong();
+        bvalueid= stream.readLong();
+    }
+
+    /**
+     * @see SQLData
+     */
+    public void writeSQL( SQLOutput stream ) throws SQLException
+    {
+        stream.writeString(name);
+        stream.writeString(type);
+        
+        boolean clob= (svalue!=null&&svalue.length()>4000);
+        boolean blob= (bvalue!=null&&bvalue.length>2000);
+        
+        if (clob)
+            stream.writeString(null);
+        else    
+            stream.writeString(svalue);
+        
+        if (blob)
+            stream.writeBytes(null);
+        else    
+            stream.writeBytes(bvalue);
+     
+        try
+        {
+            if (clob)
+                stream.writeLong(writeClobValue( connection, svalue ));
+            else    
+                stream.writeLong(0L);
+            
+            if (blob)
+                stream.writeLong(writeBlobValue( connection, bvalue ));
+            else    
+                stream.writeLong(0L);
+        }
+        catch (Exception ex)
+        {
+            throw new SQLException(ex);
+        }
+        
+    }
+
+    /**
+     * 
+     * @return the attribute name
+     */
+    public String getName()
+    {
+        return name;
+    }
+
+    /**
+     * Get the attribute value
+     * @return the attribute value (either a String or a byte[])
+     * @throws Exception
+     */
+    public Object getValue()
+    throws Exception
+    {
+        if (svalue!=null)
+            return svalue;
+        else
+        if (bvalue!=null)
+            return bvalue;
+        else
+        if (cvalueid!=0L)
+            return readClobValue( connection, cvalueid );
+        else
+        if (bvalueid!=0L) 
+            return readBlobValue( connection, bvalueid );
+
+        return null;
+    }
+    
+    /**
+     * Create an hash for a LOB value to check if we already have one
+     * in the database
+     * 
+     * @param val
+     * @return
+     * @throws Exception
+     */
+    public static final String hash(byte[] val)
+    throws Exception
+    {
+        MessageDigest md = MessageDigest.getInstance("SHA");
+        DigestInputStream digestIn = new DigestInputStream(new ByteArrayInputStream(val),
md);
+        while (digestIn.read() != -1);
+        byte[] digest = md.digest();
+        return "{SHA}"+new String(Base64.encode(digest));
+    }
+    
+    /**
+     * Reads a CLOB value (used for >4000 string values)
+     * 
+     * @param connection
+     * @param cvalueid
+     * @return
+     * @throws Exception
+     */
+    public static final String readClobValue( OracleConnection connection, long cvalueid
)
+    throws Exception
+    {
+        OraclePreparedStatement stmt= ( OraclePreparedStatement ) connection.prepareStatement(
"select column_value from table(partition_facade.read_clob(?))" );
+        stmt.setLong( 1, cvalueid );
+        
+        OracleResultSet rs= ( OracleResultSet ) stmt.executeQuery();
+        
+        rs.next();
+
+        String cvalue= rs.getString( 1 );
+        
+        rs.close();
+        stmt.close();
+        
+        return cvalue;
+    }
+    
+    /**
+     * reads a BLOB value (used for >2000 binary values)
+     * 
+     * @param connection
+     * @param bvalueid
+     * @return
+     * @throws Exception
+     */
+    public static final byte[] readBlobValue( OracleConnection connection, long bvalueid
)
+    throws Exception
+    {
+        OraclePreparedStatement stmt= ( OraclePreparedStatement ) connection.prepareStatement(
"select column_value from table(partition_facade.read_blob(?))" );
+        stmt.setLong( 1, bvalueid );
+        
+        OracleResultSet rs= ( OracleResultSet ) stmt.executeQuery();
+        
+        rs.next();
+
+        byte[] bvalue= rs.getBytes( 1 );
+        
+        rs.close();
+        stmt.close();
+        
+        return bvalue;
+    }
+    
+    /**
+     * Writes a new CLOB value into the database only if cannot
+     * find its hash.
+     * 
+     * @param connection the connection to use
+     * @param value the value to write
+     * @return the value id
+     * @throws Exception
+     */
+    public static final Long writeClobValue( OracleConnection connection, String value )
+    throws Exception
+    {
+        String hash= hash(value.getBytes());
+        
+        long cvalueid= 0L;
+        OracleCallableStatement stmt= ( OracleCallableStatement ) connection.prepareCall(
"begin partition_facade.write_clob(?,?,?); end;" );
+        stmt.setString( 1, hash );
+        stmt.registerOutParameter( 2, OracleTypes.NUMBER);
+        stmt.registerOutParameter( 3, OracleTypes.CLOB);
+        
+        stmt.execute();
+        
+        cvalueid= stmt.getLong( 2 );
+        
+        CLOB c= stmt.getCLOB( 3 );
+        c.open( CLOB.MODE_READWRITE );
+        c.truncate( 0 );
+        c.setAsciiStream( 1 ).write( value.getBytes() );
+        c.close();
+
+        stmt.close();
+        
+        return cvalueid;
+    }
+    
+    /**
+     * Writes a new BLOB value into the database only if cannot
+     * find its hash.
+     * 
+     * @param connection the connection to use
+     * @param value the value to write
+     * @return the value id
+     * @throws Exception
+     */
+    public static final Long writeBlobValue( OracleConnection connection, byte[] value )
+    throws Exception
+    {
+        String hash= hash(value);
+        
+        long bvalueid= 0L;
+        OracleCallableStatement stmt= ( OracleCallableStatement ) connection.prepareCall(
"begin partition_facade.write_blob(?,?,?); end;" );
+        stmt.setString( 1, hash );
+        stmt.registerOutParameter( 2, OracleTypes.NUMBER);
+        stmt.registerOutParameter( 3, OracleTypes.BLOB);
+        
+        stmt.execute();
+        
+        bvalueid= stmt.getLong( 2 );
+        
+        BLOB b= stmt.getBLOB( 3 );
+        b.open( BLOB.MODE_READWRITE );
+        b.truncate( 0 );
+        b.setBinaryStream( 1 ).write( value );
+        b.close();
+
+        stmt.close();
+        
+        return bvalueid;
+    }
+}

Propchange: directory/apacheds/branches/ldif-partition/oracle-partition/src/main/java/org/apache/directory/server/partition/impl/oracle/OracleAttribute.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: directory/apacheds/branches/ldif-partition/oracle-partition/src/main/java/org/apache/directory/server/partition/impl/oracle/OracleEntry.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/ldif-partition/oracle-partition/src/main/java/org/apache/directory/server/partition/impl/oracle/OracleEntry.java?rev=762433&view=auto
==============================================================================
--- directory/apacheds/branches/ldif-partition/oracle-partition/src/main/java/org/apache/directory/server/partition/impl/oracle/OracleEntry.java
(added)
+++ directory/apacheds/branches/ldif-partition/oracle-partition/src/main/java/org/apache/directory/server/partition/impl/oracle/OracleEntry.java
Mon Apr  6 17:40:02 2009
@@ -0,0 +1,208 @@
+/*
+ *  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.server.partition.impl.oracle;
+
+import java.sql.Array;
+import java.sql.SQLData;
+import java.sql.SQLException;
+import java.sql.SQLInput;
+import java.sql.SQLOutput;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import oracle.jdbc.OracleConnection;
+import oracle.sql.ARRAY;
+import oracle.sql.ArrayDescriptor;
+
+import org.apache.directory.server.core.entry.DefaultServerEntry;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
+import org.apache.directory.shared.ldap.entry.EntryAttribute;
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.UsageEnum;
+
+/**
+ * This class is used like a one to one mapping for the 
+ * LDAP_ENTRY pl/sql object. It is encoded and decoded
+ * directly from the jdbc driver calling the readSQL and
+ * writeSQL methods of the SQLData interface.
+ */
+public class OracleEntry implements SQLData
+{
+    private String sqlType= "LDAP_ENTRY"; 
+    private OracleConnection connection;
+    
+    private String reversedDn;
+    private String upDn;
+    private ArrayList<OracleAttribute> attrs= new ArrayList<OracleAttribute>();

+    
+    /**
+     * A connection that can be used to set and get LOBs 
+     * values for this entry
+     * @param connection
+     * @throws Exception
+     */
+    public void setConnection(OracleConnection connection)
+    throws Exception
+    {
+        this.connection= connection;
+    }
+
+    /**
+     * @see SQLData
+     */
+    public String getSQLTypeName() throws SQLException
+    {
+        return sqlType;
+    }
+  
+    /**
+     * @see SQLData
+     */
+    public void readSQL( SQLInput stream, String sqlType ) throws SQLException
+    {
+        this.sqlType= sqlType;
+        
+        reversedDn= stream.readString();
+        upDn= stream.readString();
+        Array a= stream.readArray();
+        
+        Object[] oas= ( Object[] ) a.getArray();
+        
+        for (Object o: oas) 
+            attrs.add( ( OracleAttribute ) o );
+    }
+
+    /**
+     * @see SQLData
+     */
+    public void writeSQL( SQLOutput stream ) throws SQLException
+    {
+        stream.writeString( reversedDn );
+        stream.writeString( upDn );
+        
+        ArrayDescriptor desc = ArrayDescriptor.createDescriptor("LDAP_ATTRIBUTE_TABLE",connection);
+        ARRAY attrsArray = new ARRAY(desc, connection, attrs.toArray());
+
+        stream.writeArray( attrsArray );
+    }
+
+    /**
+     * @return the reversed and normalized entry DN
+     */
+    public String getReversedDn()
+    {
+        return reversedDn;
+    }
+
+    /**
+     * @return the user provided DN
+     */
+    public String getUpDn()
+    {
+        return upDn;
+    }
+
+    /**
+     * Set the reversed and normalized entry dn
+     * @param reversedDn
+     */
+    public void setReversedDn( String reversedDn )
+    {
+        this.reversedDn = reversedDn;
+    }
+
+    /**
+     * @return a list of entry attributes
+     */
+    public List<OracleAttribute> getAttrs()
+    {
+        return attrs;
+    }
+    
+    /**
+     * Converts this oracle entry to a ServerEntry
+     * @param partition the partition of this entry
+     * @return a ServerEntry version of this entry
+     * @throws Exception
+     */
+    public ServerEntry toServerEntry(OraclePartition partition)
+    throws Exception
+    {
+        DefaultServerEntry se= new DefaultServerEntry(partition.getRegistries());
+        se.setDn( new LdapDN(upDn) );
+        AttributeTypeRegistry atr= partition.getRegistries().getAttributeTypeRegistry();
+        
+        for (OracleAttribute attribute: attrs)
+        {
+           attribute.setConnection( partition.getConnectionWrapper().getConnection() ); //
used for clob and blob value handling
+           
+           Object value= attribute.getValue();
+           
+           if (value instanceof byte[])
+             se.add( atr.lookup(attribute.getName()), (byte[])attribute.getValue() );
+           else
+             se.add( atr.lookup(attribute.getName()), (String)attribute.getValue() );
+        }
+        
+        return se;
+    }
+
+    /**
+     * Convert a ServerEntry into an OracleEntry 
+     * @param entry the ServerEntry to convert
+     * @param partition the partition of this entry
+     * @return an OracleEntry version of the ServerEntry
+     * @throws Exception
+     */
+    public static OracleEntry fromServerEntry( ServerEntry entry, OraclePartition partition)
+    throws Exception
+    {
+        OracleEntry e= new OracleEntry();
+        
+        e.setConnection( partition.getConnectionWrapper().getConnection() );
+        e.reversedDn= OraclePartition.toReversedDn( entry.getDn() );
+        e.upDn= entry.getDn().getUpName();
+        
+        
+        for (AttributeType at: entry.getAttributeTypes())
+        {
+            String type= (at.getUsage().equals(UsageEnum.USER_APPLICATIONS) ? "u" : null);
+            EntryAttribute ea= entry.get( at );
+            Iterator<Value<?>> i= ea.getAll();
+            
+            while (i.hasNext())
+            {
+                 Value<?> v= i.next();
+                 
+                 if (v.get() instanceof byte[])
+                   e.attrs.add( new OracleAttribute(at.getOid(), (byte[])v.get(), type, e.connection)
);
+                 else
+                   e.attrs.add( new OracleAttribute(at.getOid(), (String)v.get(), type, e.connection)
);
+            }
+        }    
+        
+        return e;
+    }
+
+}

Propchange: directory/apacheds/branches/ldif-partition/oracle-partition/src/main/java/org/apache/directory/server/partition/impl/oracle/OracleEntry.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: directory/apacheds/branches/ldif-partition/oracle-partition/src/main/java/org/apache/directory/server/partition/impl/oracle/OracleEntryCursorAdaptor.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/ldif-partition/oracle-partition/src/main/java/org/apache/directory/server/partition/impl/oracle/OracleEntryCursorAdaptor.java?rev=762433&view=auto
==============================================================================
--- directory/apacheds/branches/ldif-partition/oracle-partition/src/main/java/org/apache/directory/server/partition/impl/oracle/OracleEntryCursorAdaptor.java
(added)
+++ directory/apacheds/branches/ldif-partition/oracle-partition/src/main/java/org/apache/directory/server/partition/impl/oracle/OracleEntryCursorAdaptor.java
Mon Apr  6 17:40:02 2009
@@ -0,0 +1,174 @@
+/*
+ *  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.server.partition.impl.oracle;
+
+import java.sql.SQLException;
+
+import oracle.jdbc.OracleResultSet;
+
+import org.apache.directory.server.core.cursor.AbstractCursor;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.interceptor.context.ListOperationContext;
+import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
+import org.apache.directory.server.partition.impl.oracle.OraclePartition.OracleCursorWrapper;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
+import org.apache.directory.shared.ldap.filter.PresenceNode;
+
+/**
+ * An adaptor for Oracle based result sets.
+ * From the fact we use oracle resultsets that contains
+ * PL/SQL objects, it is pretty simple to implement a cursor
+ * just wrapping the OracleResultSet  
+ */
+public final class OracleEntryCursorAdaptor extends AbstractCursor<ServerEntry>
+{
+    private OraclePartition partition;
+    private OracleResultSet resultSet;
+    private OracleCursorWrapper cw;
+    
+    /**
+     * Create a cursor for a list operation context
+     * @param partition
+     * @param ctx
+     * @throws Exception
+     */
+    public OracleEntryCursorAdaptor(OraclePartition partition, ListOperationContext ctx)
+    throws Exception
+    {
+        this.partition= partition;
+        cw= partition.prepareCursor("select value(o) from table(partition_facade.list(?))
o", OraclePartition.toReversedDn( ctx.getDn() ));
+        resultSet= cw.getResultSet();
+    }
+
+    /**
+     * Create a cursor for a search operation context
+     * @param partition
+     * @param ctx
+     * @throws Exception
+     */
+    public OracleEntryCursorAdaptor(OraclePartition partition, SearchOperationContext ctx)
+    throws Exception
+    {
+        this.partition= partition;
+        int searchScope= ctx.getSearchControls().getSearchScope();
+        String[] returningAttributes= ctx.getSearchControls().getReturningAttributes();
+        returningAttributes= (returningAttributes == null ? new String[]{"*"} : returningAttributes);
+        long countLimit= ctx.getSearchControls().getCountLimit();
+        
+        String filter= null;
+        
+        if (!(ctx.getFilter() instanceof PresenceNode&&
+              ( ((PresenceNode)ctx.getFilter()).getAttribute().equals( SchemaConstants.OBJECT_CLASS_AT_OID
)
+                || ((PresenceNode)ctx.getFilter()).getAttribute().equals( SchemaConstants.OBJECT_CLASS_AT
)
+              )
+             )
+           )
+            filter= partition.getXStream().toXML(ctx.getFilter()); 
+        
+        
+        cw= partition.prepareCursor("select value(o) from table(partition_facade.search(?,?,?,?,?))
o", OraclePartition.toReversedDn( ctx.getDn() ), searchScope, filter, returningAttributes,
countLimit);
+        resultSet= cw.getResultSet();
+    }
+
+    public void after( ServerEntry element ) throws Exception
+    {
+        resultSet.beforeFirst();
+        
+        while (resultSet.next())
+           if (resultSet.getObject( 1 ).equals( element ))
+             break;
+    }
+
+    public void afterLast() throws Exception
+    {
+        resultSet.afterLast();
+    }
+
+    public boolean available()
+    {
+        try
+        {
+            return !resultSet.isClosed();
+        }
+        catch ( SQLException e )
+        {
+            return false;
+        }
+    }
+
+    public void before( ServerEntry element ) throws Exception
+    {
+        resultSet.beforeFirst();
+        
+        while (resultSet.next())
+           if (resultSet.getObject( 1 ).equals( element ))
+             break;
+        
+        resultSet.previous();
+    }
+
+    public void beforeFirst() throws Exception
+    {
+        resultSet.beforeFirst();
+    }
+
+    public void close() throws Exception
+    {
+        resultSet.close();
+    }
+
+    public void close( Exception reason ) throws Exception
+    {
+        resultSet.close();        
+        super.close(reason);
+    }
+
+    public boolean first() throws Exception
+    {
+        return resultSet.first();
+    }
+
+    public ServerEntry get() throws Exception
+    {
+        return ((OracleEntry)resultSet.getObject( 1 )).toServerEntry( partition );
+    }
+
+
+    public boolean isElementReused()
+    {
+        return false;
+    }
+
+    public boolean last() throws Exception
+    {
+        return resultSet.last();
+    }
+
+    public boolean next() throws Exception
+    {
+        return resultSet.next();
+    }
+
+    public boolean previous() throws Exception
+    {
+        return resultSet.previous();
+    }
+
+}

Propchange: directory/apacheds/branches/ldif-partition/oracle-partition/src/main/java/org/apache/directory/server/partition/impl/oracle/OracleEntryCursorAdaptor.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: directory/apacheds/branches/ldif-partition/oracle-partition/src/main/java/org/apache/directory/server/partition/impl/oracle/OraclePartition.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/ldif-partition/oracle-partition/src/main/java/org/apache/directory/server/partition/impl/oracle/OraclePartition.java?rev=762433&view=auto
==============================================================================
--- directory/apacheds/branches/ldif-partition/oracle-partition/src/main/java/org/apache/directory/server/partition/impl/oracle/OraclePartition.java
(added)
+++ directory/apacheds/branches/ldif-partition/oracle-partition/src/main/java/org/apache/directory/server/partition/impl/oracle/OraclePartition.java
Mon Apr  6 17:40:02 2009
@@ -0,0 +1,599 @@
+/*
+ *  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.server.partition.impl.oracle;
+
+
+import java.sql.ResultSet;
+import java.util.Map;
+
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+
+import oracle.jdbc.OracleConnection;
+import oracle.jdbc.OracleResultSet;
+import oracle.jdbc.driver.OraclePreparedStatement;
+import oracle.jdbc.pool.OracleDataSource;
+import oracle.sql.ARRAY;
+import oracle.sql.ArrayDescriptor;
+
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.core.DirectoryService;
+import org.apache.directory.server.core.entry.ClonedServerEntry;
+import org.apache.directory.server.core.entry.ServerEntry;
+import org.apache.directory.server.core.filtering.BaseEntryFilteringCursor;
+import org.apache.directory.server.core.filtering.EntryFilteringCursor;
+import org.apache.directory.server.core.interceptor.context.AddOperationContext;
+import org.apache.directory.server.core.interceptor.context.BindOperationContext;
+import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
+import org.apache.directory.server.core.interceptor.context.EntryOperationContext;
+import org.apache.directory.server.core.interceptor.context.ListOperationContext;
+import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
+import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
+import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
+import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
+import org.apache.directory.server.core.interceptor.context.UnbindOperationContext;
+import org.apache.directory.server.core.partition.AbstractPartition;
+import org.apache.directory.server.core.partition.Partition;
+import org.apache.directory.server.schema.registries.Registries;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.thoughtworks.xstream.XStream;
+
+/**
+ * A {@link Partition} that stores entries in
+ * <a href="http://www.oracle.com/">Oracle</a> database.
+ *
+ */
+public final class OraclePartition extends AbstractPartition
+{
+
+    private static final Logger log = LoggerFactory.getLogger( OraclePartition.class ); 
+    private static final ThreadLocal<OracleConnectionWrapper> connectionWrapper= new
ThreadLocal<OracleConnectionWrapper>();
+    
+    private int FETCH_SIZE= 3;
+    private XStream xstream= new XStream();
+    private OracleDataSource dataSource;
+
+    private String id;
+    private String suffix;
+    private LdapDN normSuffixDN;
+    private DirectoryService directoryService;
+    private ServerEntry contextEntry;
+    
+    
+    public OraclePartition ()
+    {
+    }
+    
+    public ServerEntry getContextEntry()
+    {
+        return contextEntry;
+    }
+    
+    /*
+     * NOTE: do not store registries on startup because they will change 
+     * on inizialization
+     */
+    public Registries getRegistries()
+    {
+        return directoryService.getRegistries();
+    }
+
+    public void setContextEntry( ServerEntry contextEntry )
+    {
+        this.contextEntry = contextEntry;
+    }
+
+    public void setDirectoryService(DirectoryService ds)
+    {
+        directoryService= ds;
+        configureXStream( xstream );        
+    }
+
+    /**
+     * Configures the <a href="http://xstream.codehaus.org/">XStream</a> instance
+     * used to pass filter infos from java to pl/sql
+     * 
+     * @param xstream: the stream to configure
+     */
+    public void configureXStream(XStream xstream)
+    {
+        xstream.registerConverter(new FilterConverter(directoryService));
+        xstream.setMode(XStream.NO_REFERENCES);
+        xstream.alias("OrNode", org.apache.directory.shared.ldap.filter.OrNode.class);
+        xstream.alias("AndNode", org.apache.directory.shared.ldap.filter.AndNode.class);
+        xstream.alias("NotNode", org.apache.directory.shared.ldap.filter.NotNode.class);
+        xstream.alias("AbstractExprNode", org.apache.directory.shared.ldap.filter.AbstractExprNode.class);
+        xstream.alias("ApproximateNode", org.apache.directory.shared.ldap.filter.ApproximateNode.class);
+        xstream.alias("AssertionNode", org.apache.directory.shared.ldap.filter.AssertionNode.class);
+        xstream.alias("EqualityNode", org.apache.directory.shared.ldap.filter.EqualityNode.class);
+        xstream.alias("ExtensibleNode", org.apache.directory.shared.ldap.filter.ExtensibleNode.class);
+        xstream.alias("GreaterEqNode", org.apache.directory.shared.ldap.filter.GreaterEqNode.class);
+        xstream.alias("LessEqNode", org.apache.directory.shared.ldap.filter.LessEqNode.class);
+        xstream.alias("SubstringNode", org.apache.directory.shared.ldap.filter.SubstringNode.class);
+        xstream.alias("PresenceNode", org.apache.directory.shared.ldap.filter.PresenceNode.class);
+        xstream.alias("ScopeNode", org.apache.directory.shared.ldap.filter.ScopeNode.class);
+    }
+
+    /**
+     * 
+     * @return the configured datasource used to connect to the oracle database
+     */
+    public OracleDataSource getDataSource()
+    {
+        return dataSource;
+    }
+
+    /**
+     * Used by spring configuration to set the OracleDataSource instance 
+     * that this partition has to use to connect to the database
+     * 
+     * @param dataSource
+     */
+    public void setDataSource( OracleDataSource dataSource )
+    {
+        this.dataSource = dataSource;
+    }
+
+    /**
+     * This method returns the connection associated with this thread:
+     * using MINA the partition code is executed inside a thread pool,
+     * each thread should have its database connection. Opening and closing
+     * connections on each partition method results in a serious server 
+     * slowdown. 
+     * 
+     * @return a connection wrapper that closes the wrapped connection 
+     * once this thread ends.
+     * 
+     * @throws Exception
+     */
+    public OracleConnectionWrapper getConnectionWrapper()
+    throws Exception
+    {
+        OracleConnectionWrapper w= connectionWrapper.get();
+        
+        if (w==null||w.getConnection().isClosed())
+        {
+            w= new OracleConnectionWrapper((OracleConnection)dataSource.getConnection());
+            w.getConnection().setAutoCommit(false);
+            Map typeMap = w.getConnection().getTypeMap();
+            typeMap.put( "LDAP_ENTRY", OracleEntry.class );
+            typeMap.put( "LDAP_ATTRIBUTE", OracleAttribute.class );
+            typeMap.put( "LDAP_ATTRIBUTE_TABLE", OracleAttribute[].class );
+            typeMap.put( "VC_ARR", String[].class );
+
+            connectionWrapper.set(w);
+        }
+            
+        return w;
+    }
+
+    /**
+     * 
+     * @return the <a href="http://xstream.codehaus.org/">XStream</a> instance
+     * used to pass filter infos from java to pl/sql.
+     */
+    public XStream getXStream()
+    {
+        return xstream;
+    }
+    
+    /**
+     * Normalize and reverse the dn to use it in a database index:
+     * using a dn "as is" will not use the database index in a good
+     * way and will create <a href="http://en.wikipedia.org/wiki/Block_contention">block
contention</a>, 
+     * because a lot of DNs (uid=1222,ou=People,dc=example,dc=com) are likely to 
+     * end up in the same block. Using a reverse index will create somenthing
+     * like this (moc=cd,elpmaxe=cd,elpoeP=uo,2221=diu) that is better but
+     * still a little ugly because values come first. Reversing the dn by rdn
+     * will generate a pretty indexable value instead (dc=com,dc=example,ou=People,uid=1222).
  
+     * 
+     * 
+     * @param dn an {@link LdapDN}
+     * @return the dn normalized and reversed
+     */
+    public static final String toReversedDn(LdapDN dn)
+    {
+        StringBuffer sb= new StringBuffer();
+        String[] pieces= dn.getNormName().split(",");
+        
+        for (int i = pieces.length-1; i > -1 ; i--)
+            sb.append(","+pieces[i]);
+        
+        
+        return sb.toString().substring(1);
+    }
+
+    /**
+     * @see Partition
+     */
+    public void add( final AddOperationContext ctx ) throws Exception
+    {
+        // FIXME: bypass error on 2nd startup: DefaultPatitionNexus tries to add an existing
entry (check issue DIRSERVER-1344)
+        if (ServerDNConstants.SYSTEM_DN.equals( ctx.getDn().getUpName() )&&lookup(
ctx.getDn() ) != null) return;
+        
+        executeDml("begin partition_facade.add(?); end;",OracleEntry.fromServerEntry((ServerEntry)((ClonedServerEntry)ctx.getEntry()).getClonedEntry(),this));
+    }
+
+
+    /**
+     * @see Partition
+     */
+    public void bind( BindOperationContext ctx ) throws Exception
+    {
+        
+        if (lookup(ctx.getDn())==null)
+            throw new NamingException( "Unknown dn: "+ctx.getDn().getUpName() );
+          
+    }
+
+
+    /**
+     * @see Partition
+     */
+    public void delete(final DeleteOperationContext ctx ) throws Exception
+    {
+        executeDml("begin partition_facade.delete(?); end;",toReversedDn( ctx.getDn() ));
+    }
+
+
+    /**
+     * @see Partition
+     */
+    public int getCacheSize()
+    {
+        return 0;
+    }
+
+
+    /**
+     * @see Partition
+     */
+    public String getId()
+    {
+        return id;
+    }
+
+
+    /**
+     * @see Partition
+     */
+    public String getSuffix()
+    {
+        return suffix;
+    }
+
+    
+    /**
+     * @see Partition
+     */
+    public LdapDN getSuffixDn() throws Exception
+    {
+        return normSuffixDN;
+    }
+
+
+    /**
+     * @see Partition
+     */
+    public LdapDN getUpSuffixDn() throws Exception
+    {
+        return new LdapDN(suffix);
+    }
+
+
+    /**
+     * @see Partition
+     */
+    public EntryFilteringCursor list(final ListOperationContext ctx ) throws Exception
+    {
+        return new BaseEntryFilteringCursor( new OracleEntryCursorAdaptor(this, ctx), ctx
);
+    }
+
+    /**
+     * 
+     * @param dn
+     * @return the looked up {@link OracleEntry}
+     * @throws Exception
+     */
+    private OracleEntry lookup(LdapDN dn)
+    throws Exception
+    {
+        OracleCursorWrapper cw= prepareCursor( "select partition_facade.lookup_dn(?) from
dual", toReversedDn( dn ) );
+        OracleResultSet rs= cw.getResultSet();
+        
+        while (rs.next())
+          return (OracleEntry)rs.getObject( 1 );
+        
+        return null;
+    }
+
+    /**
+     * @see Partition
+     */
+    public ClonedServerEntry lookup( LookupOperationContext ctx ) throws Exception
+    {
+        return new ClonedServerEntry(lookup(ctx.getDn()).toServerEntry( this ));
+    }
+    
+    /**
+     * @see Partition
+     */
+    public ClonedServerEntry lookup( Long id ) throws Exception
+    {
+        return null;
+    }
+    
+    /** 
+     * A partition implementation that aims to be used for ou=system must implement this
method because 
+     * it is called before the DirectoryServer start and the default AbstractPartition implementation
+     * goes through some logic that checks the server to be started
+     * 
+     * @see org.apache.directory.server.core.partition.AbstractPartition#hasEntry(org.apache.directory.server.core.interceptor.context.EntryOperationContext)
+     */
+    public boolean hasEntry( EntryOperationContext ctx ) throws Exception
+    {
+        try
+        {
+            return lookup( ctx.getDn() ) != null; 
+        }
+        catch ( NameNotFoundException e )
+        {
+            return false;
+        }
+    }    
+
+    /**
+     * @see Partition
+     */
+    public void modify( ModifyOperationContext ctx ) throws Exception
+    {
+        executeDml("begin partition_facade.modify(?); end;",OracleEntry.fromServerEntry(
((ServerEntry)ctx.getEntry().getClonedEntry()), this));
+    }
+
+
+    /**
+     * @see Partition
+     */
+    public void move( MoveOperationContext ctx ) throws Exception
+    {
+        executeDml("begin partition_facade.move(?,?,?); end;", toReversedDn( ctx.getParent()
),
+                                                               ctx.getDn().getRdn().getUpName()+","+ctx.getParent().getUpName(),
+                                                               toReversedDn( ctx.getDn()
));
+    }
+
+
+    /**
+     * @see Partition
+     */
+    public void moveAndRename( MoveAndRenameOperationContext ctx ) throws Exception
+    {
+        executeDml("begin partition_facade.move_and_rename(?,?,?,?,?); end;", toReversedDn(
ctx.getParent() ),
+                                                                              ctx.getNewRdn().getNormName(),
+                                                                              ctx.getParent().getUpName(),
+                                                                              ctx.getNewRdn().getUpName(),
+                                                                              toReversedDn(
ctx.getDn() ));
+    }
+
+
+    /**
+     * @see Partition
+     */
+    public void rename( RenameOperationContext ctx ) throws Exception
+    {
+        executeDml("begin partition_facade.rename(?,?,?); end;", ctx.getNewRdn().getNormName(),
+                                                                 ctx.getNewRdn().getUpName(),
+                                                                 toReversedDn( ctx.getDn()
));
+    }
+
+
+    /**
+     * @see Partition
+     */
+    public EntryFilteringCursor search( SearchOperationContext ctx ) throws Exception
+    {
+        return new BaseEntryFilteringCursor( new OracleEntryCursorAdaptor(this, ctx), ctx
);
+    }
+
+
+    /**
+     * @see Partition
+     */
+    public void setCacheSize( int cacheSize )
+    {}
+
+
+    /**
+     * @see Partition
+     */
+    public void setId( String id )
+    {
+        this.id= id;
+    }
+
+
+    /**
+     * @see Partition
+     */
+    public void setSuffix( String suffix )
+    {
+        this.suffix= suffix;
+        
+        // NOTE: needed from partition nexus bootstrap to find system partition for uid=admin
+        try
+        {
+            this.normSuffixDN= LdapDN.normalize( new LdapDN(suffix), getRegistries().getAttributeTypeRegistry().getNormalizerMapping()
);
+        }
+        catch ( Exception e )
+        {
+            throw new RuntimeException();
+        }
+    }
+
+
+    /**
+     * @see Partition
+     */
+    public void unbind( UnbindOperationContext ctx ) throws Exception
+    {}
+
+    
+    /**
+     * Used to query database data
+     *
+     * NOTE: Each database access pass through the partition_facade pl/sql package
+     * to map Partition functions one by one on the database and make
+     * the tuning easier for Oracle DBAs 
+     * 
+     * @param statement the sql or plsql statement to prepare and execute
+     * @param objects bind variables to bind on 
+     * @return a resultset wrapper that closes it on instance finalization
+     * @throws Exception
+     */
+    public OracleCursorWrapper prepareCursor(String statement, Object... objects)
+    throws Exception
+    {
+        OracleConnection connection= getConnectionWrapper().getConnection();
+        OraclePreparedStatement  stmt=  ( OraclePreparedStatement ) connection.prepareStatement(statement,OracleResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);
+        
+        stmt.setFetchSize( FETCH_SIZE );
+        stmt.setFetchDirection( OracleResultSet.FETCH_UNKNOWN );
+        
+        int idx= 1;
+        
+        for (Object obj: objects)
+           if (obj instanceof String[])
+           {
+               ArrayDescriptor desc = ArrayDescriptor.createDescriptor("VC_ARR",connection);
+               ARRAY array = new ARRAY(desc, connection, obj);
+               stmt.setARRAY(idx++,array);
+           }
+           else
+               stmt.setObject(idx++,obj);
+        
+        return new OracleCursorWrapper(stmt,( OracleResultSet ) stmt.executeQuery());
+    }
+    
+    /**
+     * 
+     * Used to make changes to database data
+     *
+     * NOTE: Each database access pass through the partition_facade pl/sql package
+     * to map Partition functions one by one on the database and make
+     * the tuning easier for Oracle DBAs 
+     * 
+     * @param statement the DML statement 
+     * @param objects
+     * @throws Exception
+     */
+    public void executeDml(String statement, Object... objects)
+    throws Exception
+    {
+        OraclePreparedStatement  stmt=  ( OraclePreparedStatement ) getConnectionWrapper().getConnection().prepareStatement(statement);
+
+        int idx= 1;
+        
+        for (Object obj: objects)
+            stmt.setObject(idx++,obj);
+
+        stmt.execute();
+        stmt.close();
+    }
+
+    /**
+     * A commodity method to lookup attribute OID
+     * 
+     * @param directoryService
+     * @param att the attribute to lookup
+     * @return the attribute OID
+     * @throws Exception
+     */
+    public static final String normAtt(DirectoryService directoryService, String att)
+    throws Exception
+    {
+        return directoryService.getRegistries().getAttributeTypeRegistry().lookup(att).getOid();
+    }   
+    
+    /**
+     * A commodity class to wrap oracle connections
+     * and close them only when needed 
+     */
+    public static class OracleConnectionWrapper
+    {
+        private OracleConnection connection;
+        
+        public OracleConnectionWrapper(OracleConnection connection)
+        {
+            this.connection= connection;
+        }
+        
+        public OracleConnection getConnection()
+        {
+            return connection;
+        }
+        
+        @Override
+        protected void finalize() throws Throwable
+        {
+            try { connection.close(); } catch (Exception e) {}
+            super.finalize();
+        }
+        
+    }
+    
+    /**
+     * A commodity class to wrap oracle resultsets
+     * and close them only when needed 
+     */
+    public static class OracleCursorWrapper
+    {
+        private OraclePreparedStatement statement;
+        private OracleResultSet resultSet;
+        
+        public OracleCursorWrapper(OraclePreparedStatement statement, OracleResultSet resultSet)
+        {
+            this.statement= statement;
+            this.resultSet= resultSet;
+        }
+        
+        public OraclePreparedStatement getStatement()
+        {
+            return statement;
+        }
+        
+        public OracleResultSet getResultSet()
+        {
+            return resultSet;
+        }
+        
+        @Override
+        protected void finalize() throws Throwable
+        {
+            try { resultSet.close(); } catch (Exception e) {}
+            try { statement.close(); } catch (Exception e) {}
+            super.finalize();
+        }
+    }
+
+}

Propchange: directory/apacheds/branches/ldif-partition/oracle-partition/src/main/java/org/apache/directory/server/partition/impl/oracle/OraclePartition.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: directory/apacheds/branches/ldif-partition/oracle-partition/src/test/java/org/apache/directory/server/AppTest.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/ldif-partition/oracle-partition/src/test/java/org/apache/directory/server/AppTest.java?rev=762433&view=auto
==============================================================================
--- directory/apacheds/branches/ldif-partition/oracle-partition/src/test/java/org/apache/directory/server/AppTest.java
(added)
+++ directory/apacheds/branches/ldif-partition/oracle-partition/src/test/java/org/apache/directory/server/AppTest.java
Mon Apr  6 17:40:02 2009
@@ -0,0 +1,38 @@
+package org.apache.directory.server;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Unit test for simple App.
+ */
+public class AppTest 
+    extends TestCase
+{
+    /**
+     * Create the test case
+     *
+     * @param testName name of the test case
+     */
+    public AppTest( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * @return the suite of tests being tested
+     */
+    public static Test suite()
+    {
+        return new TestSuite( AppTest.class );
+    }
+
+    /**
+     * Rigourous Test :-)
+     */
+    public void testApp()
+    {
+        assertTrue( true );
+    }
+}

Propchange: directory/apacheds/branches/ldif-partition/oracle-partition/src/test/java/org/apache/directory/server/AppTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: directory/apacheds/branches/ldif-partition/xdbm-base/src/test/java/org/apache/directory/server/xdbm/TableTest.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/ldif-partition/xdbm-base/src/test/java/org/apache/directory/server/xdbm/TableTest.java?rev=762433&r1=762432&r2=762433&view=diff
==============================================================================
--- directory/apacheds/branches/ldif-partition/xdbm-base/src/test/java/org/apache/directory/server/xdbm/TableTest.java
(original)
+++ directory/apacheds/branches/ldif-partition/xdbm-base/src/test/java/org/apache/directory/server/xdbm/TableTest.java
Mon Apr  6 17:40:02 2009
@@ -51,9 +51,9 @@
      */
     public void testNoDuplicatesPutGet() throws Exception
     {
-        Assume.assumeNotNull( factory );
+        //Assume.assumeNotNull( factory );
         
-        Table<Integer, Integer> table = factory.createTable();
+        //Table<Integer, Integer> table = factory.createTable();
         
     }
 }



Mime
View raw message