View Javadoc

1   /*******************************************************************************
2    * Copyright (c) 2007, 2014 Massimiliano Ziccardi
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   *******************************************************************************/
16  package it.jnrpe.server.plugins;
17  
18  import java.io.File;
19  import java.io.IOException;
20  import java.net.URL;
21  import java.net.URLClassLoader;
22  import java.security.AccessController;
23  import java.security.PrivilegedAction;
24  import java.util.List;
25  import java.util.jar.JarFile;
26  
27  /**
28   * A parent-last classloader that will try the child classloader first and then
29   * the parent. This takes a fair bit of doing because java really prefers
30   * parent-first.
31   * 
32   * For those not familiar with class loading trickery, be wary
33   */
34  class JNRPEClassLoader extends ClassLoader {
35  	/**
36  	 * The list of classloader urls.
37  	 */
38  	private List<URL> classLoaderUrlList;
39  
40  	/**
41  	 * The child classloader.
42  	 */
43  	private ChildURLClassLoader childClassLoader;
44  
45  	/**
46  	 * This class allows me to call findClass on a classloader.
47  	 */
48  	private static class FindClassClassLoader extends ClassLoader {
49  
50  		/**
51  		 * Build the object specifing the parent classloader.
52  		 * 
53  		 * @param parent
54  		 *            the parent classloader
55  		 */
56  		public FindClassClassLoader(final ClassLoader parent) {
57  			super(parent);
58  		}
59  
60  		@Override
61  		public Class<?> findClass(final String name)
62  				throws ClassNotFoundException {
63  			return super.findClass(name);
64  		}
65  	}
66  
67  	/**
68  	 * This class delegates (child then parent) for the findClass method for a
69  	 * URLClassLoader. We need this because findClass is protected in
70  	 * URLClassLoader
71  	 */
72  	private static class ChildURLClassLoader extends URLClassLoader {
73  		/**
74  		 * The parent classLoader.
75  		 */
76  		private final FindClassClassLoader realParent;
77  
78  		/**
79  		 * Builds the childclassloader.
80  		 * 
81  		 * @param urls
82  		 *            The list of classloader urls
83  		 * @param parent
84  		 *            The classloader parent
85  		 */
86  		public ChildURLClassLoader(final URL[] urls,
87  				final FindClassClassLoader parent) {
88  			super(urls, null);
89  
90  			this.realParent = parent;
91  		}
92  
93  		@Override
94  		public Class<?> findClass(final String name)
95  				throws ClassNotFoundException {
96  			try {
97  				// first try to use the URLClassLoader findClass
98  				return super.findClass(name);
99  			} catch (ClassNotFoundException e) {
100 				// if that fails, we ask our real parent classloader to load the
101 				// class (we give up)
102 				return realParent.loadClass(name);
103 			}
104 		}
105 	}
106 
107 	/**
108 	 * Constructs the object with a list of classloader urls.
109 	 * 
110 	 * @param classpath
111 	 *            The list of urls
112 	 */
113 	public JNRPEClassLoader(final List<URL> classpath) {
114 		super(Thread.currentThread().getContextClassLoader());
115 		classLoaderUrlList = classpath;
116 
117 		final URL[] urls = classpath.toArray(new URL[classpath.size()]);
118 
119 		final ClassLoader parent = getParent();
120 
121 		childClassLoader = AccessController
122 				.doPrivileged(new PrivilegedAction<ChildURLClassLoader>() {
123 					public ChildURLClassLoader run() {
124 						return new ChildURLClassLoader(urls,
125 								new FindClassClassLoader(parent));
126 					}
127 
128 				});
129 		// childClassLoader = new ChildURLClassLoader(urls,
130 		// new FindClassClassLoader(this.getParent()));
131 	}
132 
133 	@Override
134 	protected synchronized Class<?> loadClass(final String name,
135 			final boolean resolve) throws ClassNotFoundException {
136 		try {
137 			// first we try to find a class inside the child classloader
138 			return childClassLoader.findClass(name);
139 		} catch (ClassNotFoundException e) {
140 			// didn't find it, try the parent
141 			return super.loadClass(name, resolve);
142 		}
143 	}
144 
145 	@Override
146 	protected URL findResource(final String name) {
147 
148 		JarFile jf = null;
149 		try {
150 			for (URL u : classLoaderUrlList) {
151 				jf = new JarFile(u.getFile());
152 				String resourceUrl = "jar:" + new File(u.getFile()).toURI()
153 						+ "!/" + name;
154 				if (jf.getEntry("jnrpe_plugins.xml") != null) {
155 					return new URL(resourceUrl);
156 				}
157 
158 				if (jf.getEntry("plugin.xml") != null) {
159 					return new URL(resourceUrl);
160 				}
161 			}
162 			return null;
163 		} catch (IOException e) {
164 			throw new RuntimeException(e.getMessage(), e);
165 		} finally {
166 			if (jf != null) {
167 				try {
168 					jf.close();
169 				} catch (IOException e) {
170 					e.printStackTrace();
171 				}
172 			}
173 		}
174 	}
175 }