001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020 package org.apache.directory.server.core.sp; 021 022 023 import java.util.ArrayList; 024 import java.util.List; 025 import java.util.Set; 026 027 028 import org.apache.directory.server.constants.ApacheSchemaConstants; 029 import org.apache.directory.server.core.DirectoryService; 030 import org.apache.directory.server.core.filtering.EntryFilteringCursor; 031 import org.apache.directory.server.core.interceptor.context.ListSuffixOperationContext; 032 import org.apache.directory.server.i18n.I18n; 033 import org.apache.directory.shared.ldap.constants.SchemaConstants; 034 import org.apache.directory.shared.ldap.entry.StringValue; 035 import org.apache.directory.shared.ldap.entry.EntryAttribute; 036 import org.apache.directory.shared.ldap.entry.ServerEntry; 037 import org.apache.directory.shared.ldap.entry.Value; 038 import org.apache.directory.shared.ldap.exception.LdapException; 039 import org.apache.directory.shared.ldap.filter.AndNode; 040 import org.apache.directory.shared.ldap.filter.BranchNode; 041 import org.apache.directory.shared.ldap.filter.EqualityNode; 042 import org.apache.directory.shared.ldap.filter.SearchScope; 043 import org.apache.directory.shared.ldap.message.AliasDerefMode; 044 import org.apache.directory.shared.ldap.name.DN; 045 import org.slf4j.Logger; 046 import org.slf4j.LoggerFactory; 047 048 049 /** 050 * A class loader that loads classes from an LDAP DIT. 051 * 052 * <p> 053 * This loader looks for an configuration entry whose DN is 054 * determined by defaultSearchContextsConfig variable. If there is such 055 * an entry it gets the search contexts from the entry and searches the 056 * class to be loaded in those contexts. 057 * If there is no default search context configuration entry it searches 058 * the class in the whole DIT. 059 * 060 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 061 * @version $Rev$ $Date$ 062 */ 063 public class LdapClassLoader extends ClassLoader 064 { 065 private static final Logger log = LoggerFactory.getLogger( LdapClassLoader.class ); 066 public static String defaultSearchContextsConfig = "cn=classLoaderDefaultSearchContext,ou=configuration,ou=system"; 067 068 private DN defaultSearchDn; 069 private DirectoryService directoryService; 070 071 072 public LdapClassLoader( DirectoryService directoryService ) throws LdapException 073 { 074 super( LdapClassLoader.class.getClassLoader() ); 075 this.directoryService = directoryService; 076 defaultSearchDn = new DN( defaultSearchContextsConfig ); 077 defaultSearchDn.normalize( directoryService.getSchemaManager().getNormalizerMapping() ); 078 } 079 080 081 private byte[] findClassInDIT( List<DN> searchContexts, String name ) throws ClassNotFoundException 082 { 083 // Set up the search filter 084 BranchNode filter = new AndNode( ); 085 filter.addNode( new EqualityNode<String>( "fullyQualifiedJavaClassName", 086 new StringValue( name ) ) ); 087 filter.addNode( new EqualityNode<String>( SchemaConstants.OBJECT_CLASS_AT, 088 new StringValue( ApacheSchemaConstants.JAVA_CLASS_OC ) ) ); 089 090 try 091 { 092 for ( DN base : searchContexts ) 093 { 094 EntryFilteringCursor cursor = null; 095 try 096 { 097 cursor = directoryService.getAdminSession() 098 .search( base, SearchScope.SUBTREE, filter, AliasDerefMode.DEREF_ALWAYS, null ); 099 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( I18n.err( I18n.ERR_69, 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 ( LdapException e ) 152 { 153 log.debug( "No configuration data found for class loader default search contexts." ); 154 } 155 156 if ( configEntry != null ) 157 { 158 List<DN> searchContexts = new ArrayList<DN>(); 159 EntryAttribute attr = configEntry.get( "classLoaderDefaultSearchContext" ); 160 161 for ( Value<?> val : attr ) 162 { 163 DN dn = new DN( val.getString() ); 164 dn.normalize( directoryService.getSchemaManager().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<DN> namingContexts = new ArrayList<DN>(); 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 Set<String> suffixes = 187 directoryService.getPartitionNexus().listSuffixes( 188 new ListSuffixOperationContext( directoryService.getAdminSession() ) ); 189 190 for ( String suffix:suffixes ) 191 { 192 DN dn = new DN( suffix ); 193 dn.normalize( directoryService.getSchemaManager().getNormalizerMapping() ); 194 namingContexts.add( dn ); 195 } 196 197 classBytes = findClassInDIT( namingContexts, name ); 198 } 199 } 200 catch ( ClassNotFoundException e ) 201 { 202 String msg = I18n.err( I18n.ERR_293, name ); 203 log.debug( msg ); 204 throw new ClassNotFoundException( msg ); 205 } 206 catch ( Exception e ) 207 { 208 String msg = I18n.err( I18n.ERR_70, name ); 209 log.error( msg, e ); 210 throw new ClassNotFoundException( msg ); 211 } 212 213 return defineClass( name, classBytes, 0, classBytes.length ); 214 } 215 }