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.api.sp;
21  
22  
23  import java.util.ArrayList;
24  import java.util.List;
25  import java.util.Set;
26  
27  import org.apache.directory.api.ldap.model.constants.SchemaConstants;
28  import org.apache.directory.api.ldap.model.cursor.Cursor;
29  import org.apache.directory.api.ldap.model.entry.Attribute;
30  import org.apache.directory.api.ldap.model.entry.Entry;
31  import org.apache.directory.api.ldap.model.entry.Value;
32  import org.apache.directory.api.ldap.model.exception.LdapException;
33  import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
34  import org.apache.directory.api.ldap.model.filter.AndNode;
35  import org.apache.directory.api.ldap.model.filter.BranchNode;
36  import org.apache.directory.api.ldap.model.filter.EqualityNode;
37  import org.apache.directory.api.ldap.model.message.AliasDerefMode;
38  import org.apache.directory.api.ldap.model.message.SearchScope;
39  import org.apache.directory.api.ldap.model.name.Dn;
40  import org.apache.directory.api.ldap.model.schema.AttributeType;
41  import org.apache.directory.server.constants.ApacheSchemaConstants;
42  import org.apache.directory.server.core.api.DirectoryService;
43  import org.apache.directory.server.i18n.I18n;
44  import org.slf4j.Logger;
45  import org.slf4j.LoggerFactory;
46  
47  
48  /**
49   * A class loader that loads classes from an LDAP DIT.
50   * 
51   * <p>
52   * This loader looks for an configuration entry whose Dn is
53   * determined by defaultSearchContextsConfig variable. If there is such
54   * an entry it gets the search contexts from the entry and searches the 
55   * class to be loaded in those contexts.
56   * If there is no default search context configuration entry it searches
57   * the class in the whole DIT. 
58   * 
59   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
60   */
61  public class LdapClassLoader extends ClassLoader
62  {
63      private static final Logger LOG = LoggerFactory.getLogger( LdapClassLoader.class );
64      public static final String DEFAULT_SEARCH_CONTEXTS_CONFIG = "cn=classLoaderDefaultSearchContext,ou=configuration,ou=system";
65  
66      private Dn defaultSearchDn;
67      private DirectoryService directoryService;
68  
69      /** A storage for the ObjectClass attributeType */
70      private AttributeType objectClassAT;
71  
72  
73      public LdapClassLoader( DirectoryService directoryService ) throws LdapException
74      {
75          super( LdapClassLoader.class.getClassLoader() );
76          this.directoryService = directoryService;
77          defaultSearchDn = directoryService.getDnFactory().create( DEFAULT_SEARCH_CONTEXTS_CONFIG );
78  
79          objectClassAT = directoryService.getSchemaManager().getAttributeType( SchemaConstants.OBJECT_CLASS_AT );
80      }
81  
82  
83      private byte[] findClassInDIT( List<Dn> searchContexts, String name ) throws ClassNotFoundException, LdapInvalidAttributeValueException
84      {
85          // Set up the search filter
86          BranchNode filter = new AndNode();
87          AttributeType fqjcnAt = directoryService.getSchemaManager().getAttributeType( "fullyQualifiedJavaClassName" );
88          filter.addNode( new EqualityNode<String>( fqjcnAt, new Value( fqjcnAt, name ) ) );
89          filter.addNode( new EqualityNode<String>( objectClassAT,
90              new Value( objectClassAT, ApacheSchemaConstants.JAVA_CLASS_OC ) ) );
91  
92          try
93          {
94              for ( Dn base : searchContexts )
95              {
96                  Cursor<Entry> cursor = null;
97                  try
98                  {
99                      cursor = directoryService.getAdminSession()
100                         .search( base, SearchScope.SUBTREE, filter, AliasDerefMode.DEREF_ALWAYS );
101 
102                     cursor.beforeFirst();
103                     if ( cursor.next() ) // there should be only one!
104                     {
105                         LOG.debug( "Class {} found under {} search context.", name, base );
106                         Entry classEntry = cursor.get();
107 
108                         if ( cursor.next() )
109                         {
110                             Entry other = cursor.get();
111                             LOG.warn( "More than one class found on classpath at locations: {} \n\tand {}",
112                                 classEntry, other );
113                         }
114 
115                         return classEntry.get( "javaClassByteCode" ).getBytes();
116                     }
117                 }
118                 finally
119                 {
120                     if ( cursor != null )
121                     {
122                         cursor.close();
123                     }
124                 }
125             }
126         }
127         catch ( Exception e )
128         {
129             LOG.error( I18n.err( I18n.ERR_69, name ), e );
130         }
131 
132         throw new ClassNotFoundException();
133     }
134 
135 
136     /**
137      * {@inheritDoc}
138      */
139     @Override
140     public Class<?> findClass( String name ) throws ClassNotFoundException
141     {
142         byte[] classBytes = null;
143 
144         try
145         {
146             // TODO we should cache this information and register with the event
147             // service to get notified if this changes so we can update the cached
148             // copy - there's absolutely no reason why we should be performing this
149             // lookup every time!!!
150 
151             Entry configEntry = null;
152 
153             try
154             {
155                 configEntry = directoryService.getAdminSession().lookup( defaultSearchDn );
156             }
157             catch ( LdapException e )
158             {
159                 LOG.debug( "No configuration data found for class loader default search contexts." );
160             }
161 
162             if ( configEntry != null )
163             {
164                 List<Dn> searchContexts = new ArrayList<>();
165                 Attribute attr = configEntry.get( "classLoaderDefaultSearchContext" );
166 
167                 for ( Value val : attr )
168                 {
169                     Dn dn = directoryService.getDnFactory().create( val.getString() );
170                     searchContexts.add( dn );
171                 }
172 
173                 try
174                 {
175                     classBytes = findClassInDIT( searchContexts, name );
176 
177                     if ( LOG.isDebugEnabled() )
178                     { 
179                         LOG.debug( "Class {} found under default search contexts.", name );
180                     }
181                 }
182                 catch ( ClassNotFoundException e )
183                 {
184                     if ( LOG.isDebugEnabled() )
185                     { 
186                         LOG.debug( "Class {} could not be found under default search contexts.", name );
187                     }
188                 }
189             }
190 
191             if ( classBytes == null )
192             {
193                 List<Dn> namingContexts = new ArrayList<>();
194 
195                 Set<String> suffixes = directoryService.getPartitionNexus().listSuffixes();
196 
197                 for ( String suffix : suffixes )
198                 {
199                     Dn suffixDn = directoryService.getDnFactory().create( suffix );
200                     namingContexts.add( suffixDn );
201                 }
202 
203                 classBytes = findClassInDIT( namingContexts, name );
204             }
205         }
206         catch ( ClassNotFoundException e )
207         {
208             String msg = I18n.err( I18n.ERR_293, name );
209             LOG.debug( msg );
210             throw new ClassNotFoundException( msg );
211         }
212         catch ( Exception e )
213         {
214             String msg = I18n.err( I18n.ERR_70, name );
215             LOG.error( msg, e );
216             throw new ClassNotFoundException( msg );
217         }
218 
219         return defineClass( name, classBytes, 0, classBytes.length );
220     }
221 }