Return-Path: Delivered-To: apmail-directory-commits-archive@www.apache.org Received: (qmail 16566 invoked from network); 8 Mar 2006 13:32:22 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 8 Mar 2006 13:32:22 -0000 Received: (qmail 19764 invoked by uid 500); 8 Mar 2006 13:32:22 -0000 Delivered-To: apmail-directory-commits-archive@directory.apache.org Received: (qmail 19724 invoked by uid 500); 8 Mar 2006 13:32:21 -0000 Mailing-List: contact commits-help@directory.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@directory.apache.org Delivered-To: mailing list commits@directory.apache.org Received: (qmail 19713 invoked by uid 99); 8 Mar 2006 13:32:21 -0000 Received: from asf.osuosl.org (HELO asf.osuosl.org) (140.211.166.49) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 08 Mar 2006 05:32:21 -0800 X-ASF-Spam-Status: No, hits=-9.4 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received: from [209.237.227.194] (HELO minotaur.apache.org) (209.237.227.194) by apache.org (qpsmtpd/0.29) with SMTP; Wed, 08 Mar 2006 05:32:20 -0800 Received: (qmail 16030 invoked by uid 65534); 8 Mar 2006 13:31:54 -0000 Message-ID: <20060308133153.16027.qmail@minotaur.apache.org> Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r384211 - in /directory/trunks/apacheds: core-unit/src/test/java/org/apache/directory/server/core/sp/ core/src/main/java/org/apache/directory/server/core/sp/ Date: Wed, 08 Mar 2006 13:31:53 -0000 To: commits@directory.apache.org From: ersiner@apache.org X-Mailer: svnmailer-1.0.7 X-Virus-Checked: Checked by ClamAV on apache.org X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N Author: ersiner Date: Wed Mar 8 05:31:52 2006 New Revision: 384211 URL: http://svn.apache.org/viewcvs?rev=384211&view=rev Log: Added a basic implementation of the Ldap Class Loader Added a test case which includes both of the search methods available: - first test finds the class entry somewhere in the DIT - second test finds the class entry in a predetermined (via a configuration entry) location Added: directory/trunks/apacheds/core-unit/src/test/java/org/apache/directory/server/core/sp/ directory/trunks/apacheds/core-unit/src/test/java/org/apache/directory/server/core/sp/LdapClassLoaderTest.java directory/trunks/apacheds/core/src/main/java/org/apache/directory/server/core/sp/ directory/trunks/apacheds/core/src/main/java/org/apache/directory/server/core/sp/LdapClassLoader.java Added: directory/trunks/apacheds/core-unit/src/test/java/org/apache/directory/server/core/sp/LdapClassLoaderTest.java URL: http://svn.apache.org/viewcvs/directory/trunks/apacheds/core-unit/src/test/java/org/apache/directory/server/core/sp/LdapClassLoaderTest.java?rev=384211&view=auto ============================================================================== --- directory/trunks/apacheds/core-unit/src/test/java/org/apache/directory/server/core/sp/LdapClassLoaderTest.java (added) +++ directory/trunks/apacheds/core-unit/src/test/java/org/apache/directory/server/core/sp/LdapClassLoaderTest.java Wed Mar 8 05:31:52 2006 @@ -0,0 +1,124 @@ +/* + * Copyright 2006 The Apache Software Foundation + * + * Licensed 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.core.sp; + + +import javax.naming.Context; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.directory.BasicAttribute; +import javax.naming.directory.BasicAttributes; + +import org.apache.directory.server.core.jndi.ServerLdapContext; +import org.apache.directory.server.core.unit.AbstractAdminTestCase; +import org.apache.directory.shared.ldap.util.Base64; + + +/** + * Test case for LdapClassLoader. + * + * @author Apache Directory Project + * @version $Rev$ $Date$ + */ +public class LdapClassLoaderTest extends AbstractAdminTestCase +{ + private static final String HELLOWORLD_CLASS_BASE64 = "yv66vgAAADEAHQoABgAPCQAQABEIABIKABMAFAcAFQcAFgEABjxpbml0PgEAAygpV" + + "gEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAARtYWluAQAWKFtMamF2YS9sYW5nL1N0cmluZzsp" + + "VgEAClNvdXJjZUZpbGUBAA9IZWxsb1dvcmxkLmphdmEMAAcACAcAFwwAGAAZAQAMSGVsbG8gV29" + + "ybGQhBwAaDAAbABwBAApIZWxsb1dvcmxkAQAQamF2YS9sYW5nL09iamVjdAEAEGphdmEvbGFuZy" + + "9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZ" + + "WFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgAhAAUABgAAAAAAAgABAAcACAAB" + + "AAkAAAAdAAEAAQAAAAUqtwABsQAAAAEACgAAAAYAAQAAAAEACQALAAwAAQAJAAAAJQACAAEAAAA" + + "JsgACEgO2AASxAAAAAQAKAAAACgACAAAABQAIAAYAAQANAAAAAgAO"; + + private static final byte[] HELLOWORLD_CLASS_BYTES = Base64.decode( HELLOWORLD_CLASS_BASE64.toCharArray() ); + + + protected void setUp() throws Exception + { + // bind to RootDSE + overrideEnvironment( Context.PROVIDER_URL, "" ); + super.setUp(); + } + + + public void testLdapClassLoaderWithClassLoadedAnywhere() throws Exception + { + // get default naming context to work on + ServerLdapContext defaultContext = ( ServerLdapContext ) sysRoot.lookup( "ou=system" ); + + // set up + Attributes attributes = new BasicAttributes( "objectClass", "top", true ); + attributes.get( "objectClass" ).add( "javaClass" ); + attributes.put( "fullyQualifiedClassName", "HelloWorld" ); + attributes.put( "byteCode", HELLOWORLD_CLASS_BYTES ); + defaultContext.createSubcontext( "fullyQualifiedClassName=HelloWorld", attributes ); + + // assert set up successfull + assertNotNull( defaultContext.lookup( "fullyQualifiedClassName=HelloWorld" ) ); + + // load the class + LdapClassLoader loader = new LdapClassLoader( ( ServerLdapContext ) ( sysRoot.lookup( "" ) ) ); + Class clazz = loader.loadClass( "HelloWorld" ); + + // assert class loaded successfully + assertEquals( clazz.getName(), "HelloWorld" ); + } + + + public void testLdapClassLoaderWithClassLoadedAtDefaultSearchSubtree() throws Exception + { + + // get default naming context to work on + ServerLdapContext defaultContext = ( ServerLdapContext ) sysRoot.lookup( "ou=system" ); + + // create an extensible object for holding custom config data + Attributes classLoaderDefaultSearchContextConfig = new BasicAttributes(); + Attribute objectClass = new BasicAttribute( "objectClass" ); + objectClass.add( "top" ); + objectClass.add( "extensibleObject" ); + + // create custom config entry + classLoaderDefaultSearchContextConfig.put( objectClass ); + classLoaderDefaultSearchContextConfig.put( new BasicAttribute( "cn", "classLoaderDefaultSearchContext" ) ); + + // add a default search context to the configuration + classLoaderDefaultSearchContextConfig.put( new BasicAttribute( "classLoaderDefaultSearchContext", "ou=system" ) ); + + // add the configuration entry to the DIT + ServerLdapContext configContext = ( ServerLdapContext ) defaultContext.lookup( "ou=configuration" ); + configContext.createSubcontext( "cn=classLoaderDefaultSearchContext", classLoaderDefaultSearchContextConfig ); + + // create a class holder entry and add it to the DIT + Attributes attributes = new BasicAttributes( "objectClass", "top", true ); + attributes.get( "objectClass" ).add( "javaClass" ); + attributes.put( "fullyQualifiedClassName", "HelloWorld" ); + attributes.put( "byteCode", HELLOWORLD_CLASS_BYTES ); + defaultContext.createSubcontext( "fullyQualifiedClassName=HelloWorld", attributes ); + + // assert set up successfull + assertNotNull( defaultContext.lookup( "fullyQualifiedClassName=HelloWorld" ) ); + + // load the class + LdapClassLoader loader = new LdapClassLoader( ( ServerLdapContext ) ( sysRoot.lookup( "" ) ) ); + Class clazz = loader.loadClass( "HelloWorld" ); + + // assert class loaded successfully + assertEquals( clazz.getName(), "HelloWorld" ); + } +} Added: directory/trunks/apacheds/core/src/main/java/org/apache/directory/server/core/sp/LdapClassLoader.java URL: http://svn.apache.org/viewcvs/directory/trunks/apacheds/core/src/main/java/org/apache/directory/server/core/sp/LdapClassLoader.java?rev=384211&view=auto ============================================================================== --- directory/trunks/apacheds/core/src/main/java/org/apache/directory/server/core/sp/LdapClassLoader.java (added) +++ directory/trunks/apacheds/core/src/main/java/org/apache/directory/server/core/sp/LdapClassLoader.java Wed Mar 8 05:31:52 2006 @@ -0,0 +1,169 @@ +/* + * Copyright 2006 The Apache Software Foundation + * + * Licensed 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.core.sp; + + +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.SearchControls; +import javax.naming.directory.SearchResult; + +import org.apache.directory.server.core.jndi.ServerLdapContext; +import org.apache.directory.shared.ldap.filter.BranchNode; +import org.apache.directory.shared.ldap.filter.LeafNode; +import org.apache.directory.shared.ldap.filter.SimpleNode; +import org.apache.directory.shared.ldap.name.LdapName; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * A class loader that loads classes from an LDAP DIT. + * + *

+ * This loader looks for an configuration entry whose DN is + * determined by defaultSearchContextsConfig variable. If there is such + * an entry it gets the search contexts from the entry and searches the + * class to be loaded in those contexts. + * If there is no default search context configuration entry it searches + * the class in the whole DIT. + * + * @author Apache Directory Project + * @version $Rev$ $Date$ + */ +public class LdapClassLoader extends ClassLoader +{ + private static final Logger log = LoggerFactory.getLogger( LdapClassLoader.class ); + public static String defaultSearchContextsConfig = "cn=classLoaderDefaultSearchContext,ou=configuration,ou=system"; + private ServerLdapContext RootDSE; + + public LdapClassLoader( ServerLdapContext RootDSE ) throws NamingException + { + this.RootDSE = ( ( ServerLdapContext ) RootDSE.lookup( "" ) ); + } + + private byte[] findClassInDIT( NamingEnumeration searchContexts, String name ) throws ClassNotFoundException + { + String currentSearchContextName = null; + ServerLdapContext currentSearchContext = null; + NamingEnumeration javaClassEntries = null; + byte[] classBytes = null; + + BranchNode filter = new BranchNode( BranchNode.AND ); + filter.addNode( new SimpleNode( "fullyQualifiedClassName", name, LeafNode.EQUALITY ) ); + filter.addNode( new SimpleNode( "objectClass", "javaClass", LeafNode.EQUALITY ) ); + + SearchControls controls = new SearchControls(); + controls.setSearchScope( SearchControls.SUBTREE_SCOPE ); + + try + { + while( searchContexts.hasMore() ) + { + currentSearchContextName = ( String ) searchContexts.next(); + currentSearchContext = ( ServerLdapContext ) RootDSE.lookup( currentSearchContextName ); + + javaClassEntries = currentSearchContext.search( new LdapName(), filter, controls ); + if ( javaClassEntries.hasMore() ) // there should be only one! + { + SearchResult javaClassEntry = ( SearchResult ) javaClassEntries.next(); + Attribute byteCode = javaClassEntry.getAttributes().get( "byteCode" ); + classBytes = ( byte[] ) byteCode.get(); + continue; + } + } + } + catch ( NamingException e ) + { + throw new ClassNotFoundException(); + } + + return classBytes; + } + + public Class findClass( String name ) throws ClassNotFoundException + { + byte[] classBytes = null; + + NamingEnumeration defaultSearchContexts = null; + NamingEnumeration namingContexts = null; + + ServerLdapContext defaultSearchContextsConfigContext = null; + + try + { + try + { + defaultSearchContextsConfigContext = + ( ServerLdapContext ) RootDSE.lookup( defaultSearchContextsConfig ); + } + catch ( NamingException e ) + { + log.debug( "No configuration data found for class loader default search contexts." ); + } + + if ( defaultSearchContextsConfigContext != null ) + { + defaultSearchContexts = defaultSearchContextsConfigContext + .getAttributes( "", new String[] { "classLoaderDefaultSearchContext" } ) + .get( "classLoaderDefaultSearchContext" ).getAll(); + + try + { + classBytes = findClassInDIT( defaultSearchContexts, name ); + + log.debug( "Class " + name + " found under default search contexts." ); + } + catch ( ClassNotFoundException e ) + { + log.debug( "Class " + name + " could not be found under default search contexts." ); + } + } + + if ( classBytes == null ) + { + namingContexts = RootDSE + .getAttributes( "", new String[] { "namingContexts" } ) + .get( "namingContexts" ).getAll(); + + classBytes = findClassInDIT( namingContexts, name ); + } + } + catch ( NamingException e ) + { + String msg = "Encountered JNDI failure while searching directory for class: " + name; + log.error( msg, e ); + throw new ClassNotFoundException( msg ); + } + catch ( ClassNotFoundException e ) + { + String msg = "Class " + name + " not found in DIT."; + log.warn( msg ); + throw new ClassNotFoundException( msg ); + } + finally + { + if ( defaultSearchContexts != null ) { try { defaultSearchContexts.close(); } catch( Exception e ) {} }; + if ( namingContexts != null ) { try { namingContexts.close(); } catch( Exception e ) {} }; + } + + return defineClass( name, classBytes, 0, classBytes.length ); + } +}