1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.river.osgi;
17
18 import aQute.bnd.annotation.headers.ProvideCapability;
19 import aQute.bnd.annotation.headers.RequireCapability;
20 import java.io.ByteArrayInputStream;
21 import java.io.File;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.lang.reflect.InvocationHandler;
25 import java.lang.reflect.Proxy;
26 import java.net.JarURLConnection;
27 import java.net.MalformedURLException;
28 import java.net.URISyntaxException;
29 import java.net.URL;
30 import java.security.cert.CertPath;
31 import java.security.cert.Certificate;
32 import java.security.cert.CertificateException;
33 import java.security.cert.CertificateFactory;
34 import java.util.Arrays;
35 import java.util.Collection;
36 import java.util.HashSet;
37 import java.util.Iterator;
38 import java.util.StringTokenizer;
39 import java.util.concurrent.ConcurrentHashMap;
40 import java.util.concurrent.ConcurrentMap;
41 import net.jini.core.constraint.MethodConstraints;
42 import net.jini.core.constraint.RemoteMethodControl;
43 import net.jini.export.CodebaseAccessor;
44 import net.jini.io.MarshalledInstance;
45 import net.jini.io.context.IntegrityEnforcement;
46 import net.jini.loader.ProxyCodebaseSpi;
47 import net.jini.security.Security;
48 import org.apache.river.api.net.Uri;
49 import org.apache.river.concurrent.RC;
50 import org.apache.river.concurrent.Ref;
51 import org.apache.river.concurrent.Referrer;
52 import org.osgi.framework.Bundle;
53 import org.osgi.framework.BundleActivator;
54 import org.osgi.framework.BundleContext;
55 import org.osgi.framework.BundleException;
56
57
58
59
60
61 @RequireCapability(
62 ns="osgi.extender",
63 filter="(osgi.extender=osgi.serviceloader.registrar)")
64 @ProvideCapability(
65 ns="osgi.serviceloader",
66 name="net.jini.loader.ProxyCodebaseSpi")
67 public class ProxyBundleProvider implements ProxyCodebaseSpi, BundleActivator {
68 private static final ConcurrentMap<String,Uri[]> uriCache;
69 private static final ConcurrentMap<Key,ClassLoader> cache;
70
71 static {
72 cache = new ConcurrentHashMap<Key,ClassLoader>();
73 ConcurrentMap<Referrer<String>,Referrer<Uri[]>> intern1 =
74 new ConcurrentHashMap<Referrer<String>,Referrer<Uri[]>>();
75 uriCache = RC.concurrentMap(intern1, Ref.TIME, Ref.STRONG, 60000L, 60000L);
76 }
77
78 volatile BundleContext bc;
79
80
81 public ProxyBundleProvider(){
82 this.bc = null;
83 }
84
85
86
87
88 private static boolean isDirectory(URL url) {
89 String file = url.getFile();
90 return (file.length() > 0 && file.charAt(file.length() - 1) == File.separatorChar);
91 }
92
93
94
95
96
97
98
99
100
101
102
103
104 private Uri[] pathToURIs(String path) throws MalformedURLException {
105 if (path == null) {
106 return null;
107 }
108 Uri[] urls = uriCache.get(path);
109 if (urls != null) return urls;
110 StringTokenizer st = new StringTokenizer(path);
111 urls = new Uri[st.countTokens()];
112 for (int i = 0; st.hasMoreTokens(); i++) {
113 try {
114 String ur = st.nextToken();
115 ur = Uri.fixWindowsURI(ur);
116 urls[i] = Uri.parseAndCreate(ur);
117 } catch (URISyntaxException ex) {
118 throw new MalformedURLException("URL's must be RFC 3986 Compliant: "
119 + ex.getMessage());
120 }
121 }
122 Uri [] existed = uriCache.putIfAbsent(path, urls);
123 if (existed != null) urls = existed;
124 return urls;
125 }
126
127 @Override
128 public Object resolve(CodebaseAccessor bootstrapProxy,
129 MarshalledInstance smartProxy,
130 ClassLoader parent,
131 ClassLoader verifier,
132 Collection context)
133 throws IOException, ClassNotFoundException
134 {
135 if (bc == null) throw new NullPointerException("Bundle is not active");
136 if (context == null) throw new NullPointerException(
137 "stream context cannot be null");
138 if (!(bootstrapProxy instanceof RemoteMethodControl))
139 throw new IllegalArgumentException(
140 "bootstrap proxy must be instance of RemoteMethodControl");
141 Iterator it = context.iterator();
142 MethodConstraints mc = null;
143 IntegrityEnforcement integrityEnforcement = null;
144 while(it.hasNext()){
145 Object o = it.next();
146 if (o instanceof MethodConstraints){
147 mc = (MethodConstraints) o;
148 } else if (o instanceof IntegrityEnforcement){
149 integrityEnforcement = (IntegrityEnforcement) o;
150 }
151 }
152 bootstrapProxy = (CodebaseAccessor)
153 ((RemoteMethodControl) bootstrapProxy).setConstraints(mc);
154 String path = bootstrapProxy.getClassAnnotation();
155 String proxyBundlePath = null;
156 Uri [] codebases = pathToURIs(path);
157 Key loaderKey = new Key(Proxy.getInvocationHandler(bootstrapProxy), codebases[0]);
158 ClassLoader loader = cache.get(loaderKey);
159 if (loader == null){
160 byte [] encodedCerts = bootstrapProxy.getEncodedCerts();
161 Collection<? extends Certificate> certs = null;
162 if ((encodedCerts == null
163 || encodedCerts.length == 0 )
164 && integrityEnforcement != null
165 && integrityEnforcement.integrityEnforced())
166 {
167 Security.verifyCodebaseIntegrity(path, null);
168 } else if (encodedCerts != null && encodedCerts.length > 0) {
169
170
171 try {
172 String certFactoryType = bootstrapProxy.getCertFactoryType();
173 String certPathEncoding = bootstrapProxy.getCertPathEncoding();
174 CertificateFactory factory =
175 CertificateFactory.getInstance(certFactoryType);
176 CertPath certPath = factory.generateCertPath(
177 new ByteArrayInputStream(encodedCerts), certPathEncoding);
178 certs = certPath.getCertificates();
179 proxyBundlePath = codebases[0].toString();
180 URL searchURL = createSearchURL(new URL(proxyBundlePath));
181 URL jarURL = ((JarURLConnection) searchURL
182 .openConnection()).getJarFileURL();
183 JarURLConnection juc = (JarURLConnection) new URL(
184 "jar", "",
185 jarURL.toExternalForm() + "!/").openConnection();
186 juc.connect();
187 InputStream in = juc.getInputStream();
188 byte [] bytes = new byte[1024];
189 int bytesRead = 0;
190
191
192 do {
193 bytesRead = in.read(bytes);
194 } while (bytesRead == 1024);
195
196
197 Certificate [] certificates = juc.getCertificates();
198 if (certs == null){
199 throw new SecurityException("jar file invalid");
200 }
201
202 HashSet<Certificate> actualCerts
203 = new HashSet<Certificate>(Arrays.asList(certificates));
204 HashSet<Certificate> requiredCerts = new HashSet<Certificate>(certs);
205 if (!actualCerts.containsAll(requiredCerts)){
206 throw new SecurityException("certificates don't match");
207 }
208 } catch (CertificateException ex) {
209 throw new IOException("Problem creating signer certificates", ex);
210 }
211 }
212 if (proxyBundlePath == null) proxyBundlePath = codebases[0].toString();
213 try {
214 Bundle proxyBundle = bc.installBundle(proxyBundlePath);
215 loader = new BundleDelegatingClassLoader(proxyBundle);
216 ClassLoader existed = cache.putIfAbsent(loaderKey, loader);
217 if (existed != null){
218 loader = existed;
219 try {
220 proxyBundle.uninstall();
221 } catch (BundleException ex){}
222 }
223 } catch (BundleException ex) {
224 throw new IOException("Unable to resolve Bundle", ex);
225 }
226 }
227 Object sp = smartProxy.get(loader, true, null, context);
228
229
230 return sp;
231 }
232
233 public void start(BundleContext bc) throws Exception {
234 this.bc = bc;
235 }
236
237 public void stop(BundleContext bc) throws Exception {
238 this.bc = null;
239 }
240
241
242
243
244
245
246
247
248 private URL createSearchURL(URL url) throws MalformedURLException {
249 if (url == null) return url;
250 String protocol = url.getProtocol();
251 if (isDirectory(url) || protocol.equals("jar")) {
252 return url;
253 }
254 return new URL("jar", "",
255 -1, url.toString() + "!/");
256 }
257
258 public boolean substitute(Class serviceClass, ClassLoader streamLoader) {
259
260
261
262
263
264 String name = serviceClass.getName();
265 try {
266 Class found = streamLoader.loadClass(name);
267 return !found.equals(serviceClass);
268 } catch (ClassNotFoundException e){
269 return true;
270 }
271 }
272
273 private static class Key {
274 private final InvocationHandler handler;
275 private final Uri codebase;
276 private final int hashcode;
277
278 Key(InvocationHandler h, Uri codebase){
279 this.handler = h;
280 this.codebase = codebase;
281 int hash = 5;
282 hash = 73 * hash + (this.handler != null ? this.handler.hashCode() : 0);
283 hash = 73 * hash + (this.codebase != null ? this.codebase.hashCode() : 0);
284 this.hashcode = hash;
285 }
286
287 @Override
288 public int hashCode() {
289 return hashcode;
290 }
291
292 @Override
293 public boolean equals(Object o){
294 if (!(o instanceof Key)) return false;
295 if (!handler.equals(((Key)o).handler)) return false;
296 return codebase.equals(((Key)o).codebase);
297 }
298 }
299
300 }