View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *  
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *  
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
18   *  
19   */
20  package org.apache.directory.server.core.sp;
21  
22  
23  import java.util.ArrayList;
24  import java.util.Iterator;
25  import java.util.List;
26  
27  import javax.naming.NamingException;
28  
29  import org.apache.directory.server.constants.ApacheSchemaConstants;
30  import org.apache.directory.server.core.DirectoryService;
31  import org.apache.directory.server.core.entry.ServerEntry;
32  import org.apache.directory.server.core.filtering.EntryFilteringCursor;
33  import org.apache.directory.server.core.interceptor.context.ListSuffixOperationContext;
34  import org.apache.directory.shared.ldap.entry.EntryAttribute;
35  import org.apache.directory.shared.ldap.entry.Value;
36  import org.apache.directory.shared.ldap.entry.client.ClientStringValue;
37  import org.apache.directory.shared.ldap.constants.SchemaConstants;
38  import org.apache.directory.shared.ldap.filter.AndNode;
39  import org.apache.directory.shared.ldap.filter.BranchNode;
40  import org.apache.directory.shared.ldap.filter.EqualityNode;
41  import org.apache.directory.shared.ldap.filter.SearchScope;
42  import org.apache.directory.shared.ldap.message.AliasDerefMode;
43  import org.apache.directory.shared.ldap.name.LdapDN;
44  
45  import org.slf4j.Logger;
46  import org.slf4j.LoggerFactory;
47  
48  
49  /**
50   * A class loader that loads classes from an LDAP DIT.
51   * 
52   * <p>
53   * This loader looks for an configuration entry whose DN is
54   * determined by defaultSearchContextsConfig variable. If there is such
55   * an entry it gets the search contexts from the entry and searches the 
56   * class to be loaded in those contexts.
57   * If there is no default search context configuration entry it searches
58   * the class in the whole DIT. 
59   * 
60   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
61   * @version $Rev$ $Date$
62   */
63  public class LdapClassLoader extends ClassLoader
64  {
65      private static final Logger log = LoggerFactory.getLogger( LdapClassLoader.class );
66      public static String defaultSearchContextsConfig = "cn=classLoaderDefaultSearchContext,ou=configuration,ou=system";
67      
68      private LdapDN defaultSearchDn;
69      private DirectoryService directoryService;
70  
71      
72      public LdapClassLoader( DirectoryService directoryService ) throws NamingException
73      {
74          super( LdapClassLoader.class.getClassLoader() );
75          this.directoryService = directoryService;
76          defaultSearchDn = new LdapDN( defaultSearchContextsConfig );
77          defaultSearchDn.normalize( directoryService.getRegistries().getAttributeTypeRegistry().getNormalizerMapping() );
78      }
79  
80      
81      private byte[] findClassInDIT( List<LdapDN> searchContexts, String name ) throws ClassNotFoundException
82      {
83          // Set up the search filter
84          BranchNode filter = new AndNode( );
85          filter.addNode( new EqualityNode<String>( "fullyQualifiedJavaClassName", 
86              new ClientStringValue( name ) ) );
87          filter.addNode( new EqualityNode<String>( SchemaConstants.OBJECT_CLASS_AT, 
88              new ClientStringValue( ApacheSchemaConstants.JAVA_CLASS_OC ) ) );
89          
90          try
91          {
92              for ( LdapDN base : searchContexts )
93              {
94                  EntryFilteringCursor cursor = null;
95                  try
96                  {
97                      cursor = directoryService.getAdminSession()
98                          .search( base, SearchScope.SUBTREE, filter, AliasDerefMode.DEREF_ALWAYS, null );
99                      
100                     cursor.beforeFirst();
101                     if ( cursor.next() ) // there should be only one!
102                     {
103                         log.debug( "Class {} found under {} search context.", name, base );
104                         ServerEntry classEntry = cursor.get();
105 
106                         if ( cursor.next() )
107                         {
108                             ServerEntry other = cursor.get();
109                             log.warn( "More than one class found on classpath at locations: {} \n\tand {}", 
110                                 classEntry, other );
111                         }
112 
113                         return classEntry.get( "javaClassByteCode" ).getBytes();
114                     }
115                 }
116                 finally
117                 {
118                     if ( cursor != null )
119                     {
120                         cursor.close();
121                     }
122                 }
123             }
124         }
125         catch ( Exception e )
126         {
127             log.error( "Exception while searching the DIT for class: " + name, e );
128         }
129 
130         throw new ClassNotFoundException();
131     }
132     
133     
134     public Class<?> findClass( String name ) throws ClassNotFoundException
135     {
136         byte[] classBytes = null;
137 
138         try 
139         {   
140             // TODO we should cache this information and register with the event
141             // service to get notified if this changes so we can update the cached
142             // copy - there's absolutely no reason why we should be performing this
143             // lookup every time!!!
144             
145             ServerEntry configEntry = null;
146             
147             try
148             {
149                 configEntry = directoryService.getAdminSession().lookup( defaultSearchDn );
150             }
151             catch ( NamingException e )
152             {
153                 log.debug( "No configuration data found for class loader default search contexts." );
154             }
155             
156             if ( configEntry != null )
157             {
158                 List<LdapDN> searchContexts = new ArrayList<LdapDN>();
159                 EntryAttribute attr = configEntry.get( "classLoaderDefaultSearchContext" );
160                 
161                 for ( Value<?> val : attr )
162                 {
163                     LdapDN dn = new LdapDN( ( String ) val.get() );
164                     dn.normalize( directoryService.getRegistries().getAttributeTypeRegistry().getNormalizerMapping() );
165                     searchContexts.add( dn );
166                 }
167                 
168                 try
169                 {
170                     classBytes = findClassInDIT( searchContexts, name );
171                     
172                     log.debug( "Class " + name + " found under default search contexts." );
173                 }
174                 catch ( ClassNotFoundException e )
175                 {
176                     log.debug( "Class " + name + " could not be found under default search contexts." );
177                 }
178             }
179             
180             if ( classBytes == null )
181             {
182                 List<LdapDN> namingContexts = new ArrayList<LdapDN>();
183                 
184                 // TODO - why is this an operation????  Why can't we just list these damn things
185                 // who went stupid crazy making everything into a damn operation  !!!! grrrr 
186                 Iterator<String> suffixes = 
187                     directoryService.getPartitionNexus().listSuffixes( 
188                         new ListSuffixOperationContext( directoryService.getAdminSession() ) );
189 
190                 while ( suffixes.hasNext() )
191                 {
192                     LdapDN dn = new LdapDN( suffixes.next() );
193                     dn.normalize( directoryService.getRegistries().getAttributeTypeRegistry().getNormalizerMapping() );
194                     namingContexts.add( dn );
195                 }
196                 
197                 classBytes = findClassInDIT( namingContexts, name );
198             }
199         } 
200         catch ( ClassNotFoundException e )
201         {
202             String msg = "Class " + name + " not found in DIT.";
203             log.debug( msg );
204             throw new ClassNotFoundException( msg );
205         }
206         catch ( Exception e ) 
207         {
208             String msg = "Encountered failure while searching directory for class: " + name;
209             log.error( msg + e );
210             throw new ClassNotFoundException( msg );
211         }
212         
213         return defineClass( name, classBytes, 0, classBytes.length );
214     }
215 }