7930bf27d0b2a6399dba43599bfd0f272d1494d5
[policy/drools-pdp.git] / feature-server-pool / src / test / java / org / onap / policy / drools / serverpooltest / BlockingClassLoader.java
1 /*
2  * ============LICENSE_START=======================================================
3  * feature-server-pool
4  * ================================================================================
5  * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.policy.drools.serverpooltest;
22
23 import java.net.URL;
24 import java.util.ArrayList;
25 import java.util.Enumeration;
26 import java.util.HashSet;
27 import java.util.NoSuchElementException;
28
29 /**
30  * Ordinarily, a 'ClassLoader' first attempts to load a class via the
31  * parent 'ClassLoader'. If that fails, it attempts to load it "locally"
32  * by whatever mechanism the class loader supports.
33  * This 'ClassLoader' instance blocks attempts to load specific classes,
34  * throwing a 'ClassNotFoundException'. This doesn't seem useful on the
35  * surface, but it forces all child 'ClassLoader' instances to do the lookup
36  * themselves. In addition, each child 'ClassLoader' will have their own
37  * copy of the classes they load, providing a way to have multiple copies of
38  * the same class running within the same JVM. Each child 'ClassLoader' can
39  * be viewed as having a separate name space.
40  */
41 public class BlockingClassLoader extends ClassLoader {
42     // these are the set of packages to block
43     private HashSet<String> packages;
44
45     // these are the prefixes of class names to block
46     private ArrayList<String> prefixes;
47
48     // these specific classes will not be blocked, even if they are in one
49     // of the packages indicated by 'packages'
50     private HashSet<String> excludes = new HashSet<String>();
51
52     // these are the prefixes of class names to exclude
53     private ArrayList<String> excludePrefixes = new ArrayList<>();
54
55     /**
56      * Constructor -- initialize the 'ClassLoader' and 'packages' variable.
57      *
58      * @param parent the parent ClassLoader
59      * @param packages variable number of packages to block
60      */
61     public BlockingClassLoader(ClassLoader parent, String... packages) {
62         super(parent);
63         this.packages = new HashSet<>();
64         this.prefixes = new ArrayList<>();
65         for (String pkg : packages) {
66             if (pkg.endsWith("*")) {
67                 prefixes.add(pkg.substring(0, pkg.length() - 1));
68             } else {
69                 this.packages.add(pkg);
70             }
71         }
72     }
73
74     /**
75      * {@inheritDoc}
76      */
77     @Override
78     protected Class<?> findClass(String name) throws ClassNotFoundException {
79         // throws a 'ClassNotFoundException' if we are blocking this one
80         testClass(name);
81
82         // not blocking this one -- pass it on to the superclass
83         return super.findClass(name);
84     }
85
86     /**
87      * {@inheritDoc}
88      */
89     @Override
90     public Enumeration<URL> getResources(String name) {
91         // in order to avoid replicated resources, we return an empty set
92         return new Enumeration<URL>() {
93             public boolean hasMoreElements() {
94                 return false;
95             }
96
97             public URL nextElement() {
98                 throw new NoSuchElementException("'BlockingClassLoader' blocks duplicate resources");
99             }
100         };
101     }
102
103     /**
104      * {@inheritDoc}
105      */
106     @Override
107     public Class<?> loadClass(String name) throws ClassNotFoundException {
108         // throws a 'ClassNotFoundException' if we are blocking this one
109         testClass(name);
110
111         // not blocking this one -- pass it on to the superclass
112         return super.loadClass(name);
113     }
114
115     /**
116      * {@inheritDoc}
117      */
118     @Override
119     protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
120         // throws a 'ClassNotFoundException' if we are blocking this one
121         testClass(name);
122
123         // not blocking this one -- pass it on to the superclass
124         return super.loadClass(name, resolve);
125     }
126
127     /**
128      * Add an entry to the list of classes that should NOT be blocked.
129      *
130      * @param name the full name of a class that shouldn't be blocked
131      */
132     public void addExclude(String name) {
133         if (name.endsWith("*")) {
134             excludePrefixes.add(name.substring(0, name.length() - 1));
135         } else {
136             excludes.add(name);
137         }
138     }
139
140     /**
141      * This method looks at a class name -- if it should be blocked, a
142      * 'ClassNotFoundException' is thrown. Otherwise, it does nothing.
143      *
144      * @param name the name of the class to be tested
145      * @throws ClassNotFoundException if this class should be blocked
146      */
147     private void testClass(String name) throws ClassNotFoundException {
148         if (excludes.contains(name)) {
149             // allow this one
150             return;
151         }
152
153         for (String prefix : excludePrefixes) {
154             if (name.startsWith(prefix)) {
155                 // allow this one
156                 return;
157             }
158         }
159
160         // extract the package from the class name -- throw a
161         // 'ClassNotFoundException' if the package is in the list
162         // being blocked
163         int index = name.lastIndexOf('.');
164         if (index >= 0) {
165             if (packages.contains(name.substring(0,index))) {
166                 throw(new ClassNotFoundException(name));
167             }
168
169             for (String prefix : prefixes) {
170                 if (name.startsWith(prefix)) {
171                     throw(new ClassNotFoundException(name));
172                 }
173             }
174         }
175     }
176 }