001/*
002 * Copyright 2002-2012 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.util.Assert;
027
028/**
029 * Subclass of ShadowingClassLoader that overrides attempts to
030 * locate certain files.
031 *
032 * @author Rod Johnson
033 * @author Adrian Colyer
034 * @since 2.0
035 */
036public class ResourceOverridingShadowingClassLoader extends ShadowingClassLoader {
037
038        private static final Enumeration<URL> EMPTY_URL_ENUMERATION = new Enumeration<URL>() {
039                @Override
040                public boolean hasMoreElements() {
041                        return false;
042                }
043                @Override
044                public URL nextElement() {
045                        throw new UnsupportedOperationException("Should not be called. I am empty.");
046                }
047        };
048
049
050        /**
051         * Key is asked for value: value is actual value
052         */
053        private Map<String, String> overrides = new HashMap<String, String>();
054
055
056        /**
057         * Create a new ResourceOverridingShadowingClassLoader,
058         * decorating the given ClassLoader.
059         * @param enclosingClassLoader the ClassLoader to decorate
060         */
061        public ResourceOverridingShadowingClassLoader(ClassLoader enclosingClassLoader) {
062                super(enclosingClassLoader);
063        }
064
065
066        /**
067         * Return the resource (if any) at the new path
068         * on an attempt to locate a resource at the old path.
069         * @param oldPath the path requested
070         * @param newPath the actual path to be looked up
071         */
072        public void override(String oldPath, String newPath) {
073                this.overrides.put(oldPath, newPath);
074        }
075
076        /**
077         * Ensure that a resource with the given path is not found.
078         * @param oldPath the path of the resource to hide even if
079         * it exists in the parent ClassLoader
080         */
081        public void suppress(String oldPath) {
082                this.overrides.put(oldPath, null);
083        }
084
085        /**
086         * Copy all overrides from the given ClassLoader.
087         * @param other the other ClassLoader to copy from
088         */
089        public void copyOverrides(ResourceOverridingShadowingClassLoader other) {
090                Assert.notNull(other, "Other ClassLoader must not be null");
091                this.overrides.putAll(other.overrides);
092        }
093
094
095        @Override
096        public URL getResource(String requestedPath) {
097                if (this.overrides.containsKey(requestedPath)) {
098                        String overriddenPath = this.overrides.get(requestedPath);
099                        return (overriddenPath != null ? super.getResource(overriddenPath) : null);
100                }
101                else {
102                        return super.getResource(requestedPath);
103                }
104        }
105
106        @Override
107        public InputStream getResourceAsStream(String requestedPath) {
108                if (this.overrides.containsKey(requestedPath)) {
109                        String overriddenPath = this.overrides.get(requestedPath);
110                        return (overriddenPath != null ? super.getResourceAsStream(overriddenPath) : null);
111                }
112                else {
113                        return super.getResourceAsStream(requestedPath);
114                }
115        }
116
117        @Override
118        public Enumeration<URL> getResources(String requestedPath) throws IOException {
119                if (this.overrides.containsKey(requestedPath)) {
120                        String overriddenLocation = this.overrides.get(requestedPath);
121                        return (overriddenLocation != null ?
122                                        super.getResources(overriddenLocation) : EMPTY_URL_ENUMERATION);
123                }
124                else {
125                        return super.getResources(requestedPath);
126                }
127        }
128
129}