001// Copyright 2011 The Apache Software Foundation 002// 003// Licensed under the Apache License, Version 2.0 (the "License"); 004// you may not use this file except in compliance with the License. 005// You may obtain a copy of the License at 006// 007// http://www.apache.org/licenses/LICENSE-2.0 008// 009// Unless required by applicable law or agreed to in writing, software 010// distributed under the License is distributed on an "AS IS" BASIS, 011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012// See the License for the specific language governing permissions and 013// limitations under the License. 014 015package org.apache.tapestry5.internal.plastic; 016 017import java.util.function.Function; 018import java.util.function.Predicate; 019 020import org.apache.tapestry5.plastic.PlasticUtils; 021 022public class PlasticClassLoader extends ClassLoader 023{ 024 static 025 { 026 // TAP5-2546 027 ClassLoader.registerAsParallelCapable(); 028 } 029 030 private final ClassLoaderDelegate delegate; 031 032 private Predicate<String> filter; 033 034 private Function<String, Class<?>> alternativeClassloading; 035 036 private String tag; 037 038 public PlasticClassLoader(ClassLoader parent, ClassLoaderDelegate delegate) 039 { 040 super(parent); 041 this.delegate = delegate; 042 } 043 044 @Override 045 protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException 046 { 047 synchronized(getClassLoadingLock(name)) 048 { 049 Class<?> loadedClass = findLoadedClass(name); 050 051 if (loadedClass != null) 052 return loadedClass; 053 054 if (shouldInterceptClassLoading(name)) 055 { 056 Class<?> c = null; 057 if ((filter != null && filter.test(name)) || (filter == null && delegate.shouldInterceptClassLoading(name))) 058 { 059 c = delegate.loadAndTransformClass(name); 060 } 061 else if (alternativeClassloading != null) 062 { 063 c = alternativeClassloading.apply(name); 064 } 065 066 if (c == null) 067 { 068 return super.loadClass(name, resolve); 069 } 070 071 if (resolve) 072 resolveClass(c); 073 074 return c; 075 } else 076 { 077 return super.loadClass(name, resolve); 078 } 079 } 080 } 081 082 private boolean shouldInterceptClassLoading(String name) { 083 return delegate.shouldInterceptClassLoading( 084 PlasticUtils.getEnclosingClassName(name)); 085 } 086 087 public synchronized Class<?> defineClassWithBytecode(String className, byte[] bytecode) 088 { 089 synchronized(getClassLoadingLock(className)) 090 { 091 return defineClass(className, bytecode, 0, bytecode.length); 092 } 093 } 094 095 /** 096 * When alternatingClassloader is set, this classloader delegates to it the 097 * call to {@linkplain ClassLoader#loadClass(String)}. If it returns a non-null object, 098 * it's returned by <code>loadClass(String)</code>. Otherwise, it returns 099 * <code>super.loadClass(name)</code>. 100 * @since 5.8.3 101 */ 102 public void setAlternativeClassloading(Function<String, Class<?>> alternateClassloading) 103 { 104 this.alternativeClassloading = alternateClassloading; 105 } 106 107 /** 108 * @since 5.8.3 109 */ 110 public void setTag(String tag) 111 { 112 this.tag = tag; 113 } 114 115 /** 116 * When a filter is set, only classes accepted by it will be loaded by this classloader. 117 * Instead, it will be delegated to alternate classloading first and the parent classloader 118 * in case the alternate doesn't handle it. 119 * @since 5.8.3 120 */ 121 public void setFilter(Predicate<String> filter) 122 { 123 this.filter = filter; 124 } 125 126 @Override 127 public String toString() 128 { 129 final String superToString = super.toString(); 130 final String id = superToString.substring(superToString.indexOf('@')).trim(); 131 return String.format("PlasticClassLoader[%s, tag=%s, parent=%s]", id, tag, getParent()); 132 } 133 134}