001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.instrument.classloading; 018 019import java.io.IOException; 020import java.io.InputStream; 021import java.net.URL; 022import java.util.Enumeration; 023import java.util.HashMap; 024import java.util.Map; 025 026import org.springframework.lang.Nullable; 027import org.springframework.util.Assert; 028 029/** 030 * Subclass of ShadowingClassLoader that overrides attempts to 031 * locate certain files. 032 * 033 * @author Rod Johnson 034 * @author Adrian Colyer 035 * @since 2.0 036 */ 037public class ResourceOverridingShadowingClassLoader extends ShadowingClassLoader { 038 039 private static final Enumeration<URL> EMPTY_URL_ENUMERATION = new Enumeration<URL>() { 040 @Override 041 public boolean hasMoreElements() { 042 return false; 043 } 044 @Override 045 public URL nextElement() { 046 throw new UnsupportedOperationException("Should not be called. I am empty."); 047 } 048 }; 049 050 051 /** 052 * Key is asked for value: value is actual value. 053 */ 054 private Map<String, String> overrides = new HashMap<>(); 055 056 057 /** 058 * Create a new ResourceOverridingShadowingClassLoader, 059 * decorating the given ClassLoader. 060 * @param enclosingClassLoader the ClassLoader to decorate 061 */ 062 public ResourceOverridingShadowingClassLoader(ClassLoader enclosingClassLoader) { 063 super(enclosingClassLoader); 064 } 065 066 067 /** 068 * Return the resource (if any) at the new path 069 * on an attempt to locate a resource at the old path. 070 * @param oldPath the path requested 071 * @param newPath the actual path to be looked up 072 */ 073 public void override(String oldPath, String newPath) { 074 this.overrides.put(oldPath, newPath); 075 } 076 077 /** 078 * Ensure that a resource with the given path is not found. 079 * @param oldPath the path of the resource to hide even if 080 * it exists in the parent ClassLoader 081 */ 082 public void suppress(String oldPath) { 083 this.overrides.put(oldPath, null); 084 } 085 086 /** 087 * Copy all overrides from the given ClassLoader. 088 * @param other the other ClassLoader to copy from 089 */ 090 public void copyOverrides(ResourceOverridingShadowingClassLoader other) { 091 Assert.notNull(other, "Other ClassLoader must not be null"); 092 this.overrides.putAll(other.overrides); 093 } 094 095 096 @Override 097 public URL getResource(String requestedPath) { 098 if (this.overrides.containsKey(requestedPath)) { 099 String overriddenPath = this.overrides.get(requestedPath); 100 return (overriddenPath != null ? super.getResource(overriddenPath) : null); 101 } 102 else { 103 return super.getResource(requestedPath); 104 } 105 } 106 107 @Override 108 @Nullable 109 public InputStream getResourceAsStream(String requestedPath) { 110 if (this.overrides.containsKey(requestedPath)) { 111 String overriddenPath = this.overrides.get(requestedPath); 112 return (overriddenPath != null ? super.getResourceAsStream(overriddenPath) : null); 113 } 114 else { 115 return super.getResourceAsStream(requestedPath); 116 } 117 } 118 119 @Override 120 public Enumeration<URL> getResources(String requestedPath) throws IOException { 121 if (this.overrides.containsKey(requestedPath)) { 122 String overriddenLocation = this.overrides.get(requestedPath); 123 return (overriddenLocation != null ? 124 super.getResources(overriddenLocation) : EMPTY_URL_ENUMERATION); 125 } 126 else { 127 return super.getResources(requestedPath); 128 } 129 } 130 131}