1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2006, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */
22
23 package org.jboss.embedded;
24
25 import java.io.File;
26 import java.io.IOException;
27 import java.net.MalformedURLException;
28 import java.net.URL;
29 import java.util.ArrayList;
30 import java.util.Enumeration;
31 import java.util.List;
32 import java.util.StringTokenizer;
33
34 import org.jboss.deployers.client.spi.main.MainDeployer;
35 import org.jboss.deployers.spi.DeploymentException;
36 import org.jboss.deployers.structure.spi.DeploymentUnit;
37 import org.jboss.deployers.structure.spi.main.MainDeployerStructure;
38 import org.jboss.deployers.vfs.spi.client.VFSDeployment;
39 import org.jboss.deployers.vfs.spi.client.VFSDeploymentFactory;
40 import org.jboss.kernel.Kernel;
41 import org.jboss.virtual.VFS;
42 import org.jboss.virtual.VirtualFile;
43 import org.jboss.virtual.VirtualFileFilter;
44
45 /**
46 * comment
47 *
48 * @author <a href="bill@jboss.com">Bill Burke</a>
49 * @author adrian@jboss.org
50 * @version $Revision: 1.1 $
51 */
52 public class DeploymentGroup
53 {
54 private ArrayList<VFSDeployment> deployments = new ArrayList<VFSDeployment>();
55 protected MainDeployer mainDeployer;
56 protected Kernel kernel;
57 private VirtualFileFilter filter;
58 private ClassLoader classLoader;
59
60 public void setMainDeployer(MainDeployer mainDeployer)
61 {
62 this.mainDeployer = mainDeployer;
63 }
64
65 public void setKernel(Kernel kernel)
66 {
67 this.kernel = kernel;
68 }
69
70 /**
71 *
72 * File filter that will be used when scanning a directory
73 *
74 * @param filter
75 */
76 public void setFilter(VirtualFileFilter filter)
77 {
78 this.filter = filter;
79 }
80
81 public void setClassLoader(ClassLoader classLoader)
82 {
83 this.classLoader = classLoader;
84 }
85
86 /**
87 * A helper to find all deployments under a directory vf
88 * and add them to the supplied list.
89 *
90 * We may recurse.
91 */
92 private static void addDeployments(VirtualFileFilter filter, List<VirtualFile> list, VirtualFile root, boolean recurse)
93 throws IOException
94 {
95 List<VirtualFile> components = root.getChildren();
96
97 for (VirtualFile component : components)
98 {
99 // Filter the component regardless of its type
100 if( filter != null && filter.accepts(component) == false)
101 continue;
102 if (component.isLeaf())
103 {
104 list.add(component);
105 }
106 // TODO replace . in the name with isArchive() == false?
107 else if (component.getName().indexOf('.') == -1 && recurse)
108 {
109 // recurse if not '.' in name and recursive search is enabled
110 addDeployments(filter, list, component, true);
111 }
112 else
113 {
114 list.add(component);
115 }
116 }
117 }
118
119 /**
120 * Ask the mainDeployer to process the set of files you added to the group
121 * this will also check the processing
122 *
123 * @throws org.jboss.deployers.spi.DeploymentException
124 */
125 public void process() throws DeploymentException
126 {
127 mainDeployer.process();
128 mainDeployer.checkComplete();
129 }
130
131 /**
132 * Ask the mainDeployer to undeploy the set of files in the group
133 *
134 * @throws org.jboss.deployers.spi.DeploymentException
135 */
136 public void undeploy() throws DeploymentException
137 {
138 for (VFSDeployment ctx : deployments)
139 mainDeployer.removeDeployment(ctx);
140 process();
141 }
142
143 /**
144 * Schedule a VirtualFile to be deployed
145 *
146 * @param vf
147 * @throws DeploymentException
148 */
149 public void add(VirtualFile vf) throws DeploymentException
150 {
151 VFSDeploymentFactory factory = VFSDeploymentFactory.getInstance();
152 VFSDeployment deployment = factory.createVFSDeployment(vf);
153 mainDeployer.addDeployment(deployment);
154 deployments.add(deployment);
155 }
156
157 /**
158 * schedules a URL to be deployed
159 *
160 * @param url
161 * @throws DeploymentException
162 */
163 public void add(URL url) throws DeploymentException
164 {
165 VirtualFile file = getVirtualFile(url);
166 add(file);
167 }
168
169 public static VirtualFile getVirtualFile(URL url)
170 throws DeploymentException
171 {
172 VirtualFile file = null;
173 try
174 {
175 file = VFS.getRoot(url);
176 }
177 catch (IOException e)
178 {
179 throw new DeploymentException("Unable to get VirtualFile for url: " + url, e);
180 }
181 return file;
182 }
183
184 /**
185 * schedules a list of virtual files to be deployed
186 *
187 * @param vfs
188 * @throws DeploymentException
189 */
190 public void addVirtualFiles(List<VirtualFile> vfs) throws DeploymentException
191 {
192 for (VirtualFile vf : vfs)
193 {
194 add(vf);
195 }
196 }
197
198 /**
199 * schedules a list of urls to be deployed
200 *
201 * @param urls
202 * @throws DeploymentException
203 */
204 public void addUrls(List<URL> urls) throws DeploymentException
205 {
206 for (URL url : urls)
207 {
208 add(url);
209 }
210 }
211
212 /**
213 * Scan all paths/jars in Java CLasspath (found with java.class.path System Property)
214 * schedule these jars to be deployed
215 *
216 * @throws org.jboss.deployers.spi.DeploymentException
217 */
218 public void addClasspath() throws DeploymentException
219 {
220 addUrls(getClassPaths());
221 }
222
223 public static List<URL> getClassPaths() throws DeploymentException
224 {
225 List<URL> list = new ArrayList<URL>();
226 String classpath = System.getProperty("java.class.path");
227 StringTokenizer tokenizer = new StringTokenizer(classpath, File.pathSeparator);
228
229 while (tokenizer.hasMoreTokens())
230 {
231 String path = tokenizer.nextToken();
232 File fp = new File(path);
233 if (!fp.exists()) throw new DeploymentException("File in java.class.path does not exist: " + fp);
234 try
235 {
236 list.add(fp.toURL());
237 }
238 catch (MalformedURLException e)
239 {
240 throw new DeploymentException(e);
241 }
242 }
243 return list;
244 }
245
246 /**
247 * Scan Java Classpath (found with java.class.path)
248 * for a specified list of files you want to deploy
249 *
250 * The files listed should be only the filename. Do not put relative or absolute paths in filenames.
251 * i.e. "myejbs.jar, my-beans.xml"
252 *
253 * @param paths comma delimited list of files
254 * @throws org.jboss.deployers.spi.DeploymentException
255 */
256 public void addClasspath(String paths) throws DeploymentException
257 {
258 List<URL> urls = getClassPaths(paths);
259 addUrls(urls);
260 }
261
262 public static List<URL> getClassPaths(String paths) throws DeploymentException
263 {
264 ArrayList<URL> list = new ArrayList<URL>();
265
266 String classpath = System.getProperty("java.class.path");
267 StringTokenizer tokenizer = new StringTokenizer(classpath, File.pathSeparator);
268 String[] split = paths.split(",");
269 for (int i = 0; i < split.length; i++)
270 {
271 split[i] = split[i].trim();
272 }
273
274 while (tokenizer.hasMoreTokens())
275 {
276 String path = tokenizer.nextToken().trim();
277 boolean found = false;
278 for (String wantedPath : split)
279 {
280 if (path.endsWith(File.separator + wantedPath))
281 {
282 found = true;
283 break;
284 }
285 }
286 if (!found)
287 continue;
288
289 File fp = new File(path);
290 if (!fp.exists())
291 throw new DeploymentException("File in java.class.path does not exists: " + fp);
292
293 try
294 {
295 list.add(fp.toURL());
296 }
297 catch (MalformedURLException e)
298 {
299 throw new DeploymentException(e);
300 }
301 }
302 return list;
303 }
304
305 /**
306 * Search for the resource using the group's configured classloader
307 * if no classloader, then Thread.currentThread().getContextClassLoader() is used.
308 * classLoader.getResource(String resource)
309 *
310 * Schedule the resource to be deployed.
311 *
312 * @param resource
313 * @throws DeploymentException
314 * @throws NullPointerException
315 */
316 public void addResource(String resource) throws DeploymentException, NullPointerException
317 {
318 ClassLoader loader = Thread.currentThread().getContextClassLoader();
319 if (classLoader != null)
320 loader = classLoader;
321
322 URL url = loader.getResource(resource);
323 if (url == null)
324 throw new NullPointerException("Resource was null: " + resource);
325
326 add(url);
327 }
328
329 /**
330 * Deploy the classpath directory or .jar file a classloader resource is located in.
331 *
332 * i.e.
333 *
334 * classpath is "/home/wburke/lib/tutorial.jar"
335 * tutorial.jar has "META-INF/persistence.xml" resource within it.
336 *
337 * addResourceBase("META-INF/persistence.xml") will try and deploy tutorial.jar
338 *
339 *
340 * @param baseResource
341 * @throws DeploymentException
342 */
343 public void addResourceBase(String baseResource) throws DeploymentException
344 {
345 ClassLoader loader = Thread.currentThread().getContextClassLoader();
346 if (classLoader != null) loader = classLoader;
347 URL url = loader.getResource(baseResource);
348 if (url == null) throw new RuntimeException("Could not find baseResource: " + baseResource);
349 addBaseResource(url, baseResource);
350
351
352 }
353
354 protected void addBaseResource(URL url, String baseResource)
355 throws DeploymentException
356 {
357 String urlString = url.toString();
358 int idx = urlString.lastIndexOf(baseResource);
359 urlString = urlString.substring(0, idx);
360 URL deployUrl = null;
361 try
362 {
363 deployUrl = new URL(urlString);
364 }
365 catch (MalformedURLException e)
366 {
367 throw new RuntimeException(e);
368 }
369 add(deployUrl);
370 }
371
372 /**
373 * Deploy the classpath directories or .jar files a classloader resource is located in.
374 * ClassLoader.getResources() is used to find the base resources.
375 *
376 * i.e.
377 *
378 * classpath is "/home/wburke/lib/tutorial.jar:/home/wburke/lib/pu.jar"
379 * tutorial.jar and pu.jar has "META-INF/persistence.xml" resource within it.
380 *
381 * addResourceBases("META-INF/persistence.xml") will try and deploy tutorial.jar and pu.jar because
382 * the both have the META-INF/persistence.xml resource within them.
383 *
384 *
385 * @param baseResource
386 * @throws DeploymentException
387 */
388 public void addResourceBases(String baseResource) throws DeploymentException
389 {
390 ClassLoader loader = Thread.currentThread().getContextClassLoader();
391 if (classLoader != null) loader = classLoader;
392 try
393 {
394 Enumeration<URL> urls = loader.getResources(baseResource);
395 while (urls.hasMoreElements())
396 {
397 URL url = urls.nextElement();
398 addBaseResource(url, baseResource);
399 }
400 }
401 catch (IOException e)
402 {
403 throw new RuntimeException(e);
404 }
405 }
406
407 /**
408 * Find the .class file resource of provided class
409 * Return a URL pointing to the classpath the resource is located in.
410 *
411 * i.e.
412 *
413 * classpath is "/home/wburke/lib/tutorial.jar"
414 * tutorial.jar has "META-INF/persistence.xml" resource within it.
415 *
416 * addResourceBase("META-INF/persistence.xml") will try and deploy tutorial.jar
417 *
418 *
419 * @param baseResource
420 * @throws DeploymentException
421 */
422 public void addResourceBase(Class baseResource) throws DeploymentException
423 {
424 String resource = baseResource.getName().replace('.', '/') + ".class";
425 addResourceBase(resource);
426 }
427
428 /**
429 * Search for resources using the group's configured classloader
430 * if no classloader, then Thread.currentThread().getContextClassLoader() is used.
431 * classLoader.getResources(String resource)
432 *
433 * Schedule the resource to be deployed.
434 *
435 * @param resource
436 * @throws DeploymentException
437 * @throws IOException
438 */
439 public void addMultipleResources(String resource) throws DeploymentException, IOException
440 {
441 ClassLoader loader = Thread.currentThread().getContextClassLoader();
442 if (classLoader != null) loader = classLoader;
443 Enumeration<URL> urls = loader.getResources(resource);
444 while (urls.hasMoreElements())
445 {
446 add(urls.nextElement());
447 }
448 }
449
450 /**
451 * Searches for a directory as described in the getDirFromResource() method of this class.
452 *
453 * schedules all possible files in directory to be deployed
454 *
455 *
456 * @param resource
457 * @param recurse whether or not to recurse child directories
458 * @throws DeploymentException
459 * @throws IOException
460 */
461 public void addDirectoryByResource(String resource, boolean recurse) throws DeploymentException, IOException
462 {
463 ClassLoader loader = Thread.currentThread().getContextClassLoader();
464 if (classLoader != null) loader = classLoader;
465 List<VirtualFile> files = getDeployerDirUrlsFromResource(filter, loader, resource, recurse);
466 addVirtualFiles(files);
467 }
468
469 /**
470 * Searches for a file based on the location of an existing classloader resource
471 * as described in the getDirFromResource() method of this class.
472 *
473 * schedules this particular file for deployment
474 *
475 *
476 * @param resource
477 * @throws DeploymentException
478 * @throws IOException
479 */
480 public void addFileByResource(String resource) throws DeploymentException, IOException
481 {
482 ClassLoader loader = Thread.currentThread().getContextClassLoader();
483 if (classLoader != null) loader = classLoader;
484 URL url = getDirFromResource(loader, resource);
485 if (url == null)
486 {
487 throw new DeploymentException("Unable to find file from resource: " + resource);
488 }
489 String urlStr = url.toString();
490 if (urlStr.endsWith("/"))
491 {
492 urlStr = urlStr.substring(0, urlStr.length() -1);
493 }
494 url = new URL(urlStr);
495 add(url);
496 }
497
498 public void addDirectory(URL directory, boolean recurse) throws DeploymentException, IOException
499 {
500 addVirtualFiles(getDeployerDirUrls(filter, directory, recurse));
501 }
502
503 /**
504 * Get the deployment units
505 *
506 * @return the deployment units
507 * @throws IllegalStateException when the units cannot be located
508 */
509 public List<DeploymentUnit> getDeploymentUnits()
510 {
511 ArrayList<DeploymentUnit> result = new ArrayList<DeploymentUnit>();
512 MainDeployerStructure structure = (MainDeployerStructure) mainDeployer;
513 for (VFSDeployment deployment : deployments)
514 {
515 DeploymentUnit unit = structure.getDeploymentUnit(deployment.getName());
516 if (unit == null)
517 throw new IllegalStateException("DeploymentUnit not found " + deployment.getName());
518 result.add(unit);
519 }
520 return result;
521 }
522
523 public List<VFSDeployment> getDeployments()
524 {
525 return deployments;
526 }
527
528 public static List<VirtualFile> getDeployerDirUrlsFromResource(VirtualFileFilter filter, ClassLoader loader, String resource, boolean recurse)
529 throws DeploymentException, IOException
530 {
531 URL url = getDirFromResource(loader, resource);
532 if (url == null)
533 {
534 throw new DeploymentException("Unable to find deployDir from resource: " + resource);
535 }
536 List<VirtualFile> files = getDeployerDirUrls(filter, url, recurse);
537 return files;
538 }
539
540 public static List<VirtualFile> getDeployerDirUrls(VirtualFileFilter filter, URL url, boolean recurse)
541 throws DeploymentException, IOException
542 {
543 VirtualFile file = null;
544 try
545 {
546 file = VFS.getRoot(url);
547 }
548 catch (Exception e)
549 {
550 throw new DeploymentException("Unable to find deployDir from url: " + url, e);
551 }
552 List<VirtualFile> files = new ArrayList<VirtualFile>();
553 addDeployments(filter, files, file, recurse);
554 return files;
555 }
556
557 /**
558 * Find the directory that contains a given resource.
559 * <p/>
560 * The '.' character can be used to specify the current directory.
561 * The '..' string can be used to specify a higher relative path
562 * <p/>
563 * i.e.
564 * <p/>
565 * getDirFromResource(loader, "org/jboss/Test.class")
566 * file:/<root>/org/jboss/
567 * <p/>
568 * getDirFromResource(loader, "org/jboss/Test.class/..")
569 * file:/<root>/org/
570 * <p/>
571 * getDirFromResource(loader, "org/jboss/Test.class/../acme")
572 * file:/<root>/org/acme/
573 * <p/>
574 * getDirFromResource(loader, "org/jboss/Test.class/./embedded")
575 * file:/<root>/org/jboss/embedded/
576 *
577 * @param loader
578 * @param resource
579 * @return the url
580 */
581 public static URL getDirFromResource(ClassLoader loader, String resource)
582 {
583 int idx = resource.indexOf("/.");
584 String base = resource;
585 String relative = null;
586 if (idx != -1)
587 {
588 base = resource.substring(0, idx);
589 relative = resource.substring(idx + 1);
590 }
591 URL url = loader.getResource(base);
592 if (url == null) return null;
593 String urlAsString = url.toString();
594 String[] paths = urlAsString.split("/");
595 int last = paths.length - 2;
596 if (relative != null)
597 {
598 String[] relativePaths = relative.split("/");
599 int relativeStart = 0;
600 for (String relativePath : relativePaths)
601 {
602 if (relativePath.equals(".."))
603 {
604 last--;
605 relativeStart++;
606 }
607 else if (relativePath.equals("."))
608 {
609 relativeStart++;
610 }
611 else
612 {
613 break;
614 }
615 }
616 urlAsString = "";
617 for (int i = 0; i <= last; i++)
618 {
619 urlAsString += paths[i] + "/";
620 }
621 for (int i = relativeStart; i < relativePaths.length; i++)
622 {
623 urlAsString += relativePaths[i] + "/";
624 }
625 }
626 else
627 {
628 urlAsString = "";
629 for (int i = 0; i <= last; i++)
630 {
631 urlAsString += paths[i] + "/";
632 }
633 }
634 try
635 {
636 url = new URL(urlAsString);
637 }
638 catch (MalformedURLException e)
639 {
640 throw new RuntimeException(e);
641 }
642 return url;
643 }
644 }