View Javadoc

1   /**
2    *
3    * Copyright 2003-2004 The Apache Software Foundation
4    *
5    *  Licensed under the Apache License, Version 2.0 (the "License");
6    *  you may not use this file except in compliance with the License.
7    *  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  
18  package org.apache.geronimo.deployment;
19  
20  import java.io.ByteArrayInputStream;
21  import java.io.File;
22  import java.io.FileOutputStream;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.io.OutputStream;
26  import java.net.URI;
27  import java.net.URISyntaxException;
28  import java.net.URL;
29  import java.util.ArrayList;
30  import java.util.Collection;
31  import java.util.HashSet;
32  import java.util.Iterator;
33  import java.util.LinkedHashMap;
34  import java.util.LinkedHashSet;
35  import java.util.List;
36  import java.util.Map;
37  import java.util.Set;
38  import java.util.StringTokenizer;
39  import java.util.Collections;
40  import java.util.jar.Attributes;
41  import java.util.jar.JarFile;
42  import java.util.jar.Manifest;
43  import java.util.zip.ZipEntry;
44  import java.util.zip.ZipFile;
45  
46  import org.apache.geronimo.common.DeploymentException;
47  import org.apache.geronimo.deployment.util.DeploymentUtil;
48  import org.apache.geronimo.gbean.AbstractName;
49  import org.apache.geronimo.gbean.AbstractNameQuery;
50  import org.apache.geronimo.gbean.GBeanData;
51  import org.apache.geronimo.gbean.GBeanInfo;
52  import org.apache.geronimo.gbean.ReferencePatterns;
53  import org.apache.geronimo.gbean.GReferenceInfo;
54  import org.apache.geronimo.kernel.GBeanAlreadyExistsException;
55  import org.apache.geronimo.kernel.GBeanNotFoundException;
56  import org.apache.geronimo.kernel.Naming;
57  import org.apache.geronimo.kernel.config.Configuration;
58  import org.apache.geronimo.kernel.config.ConfigurationData;
59  import org.apache.geronimo.kernel.config.ConfigurationManager;
60  import org.apache.geronimo.kernel.config.ConfigurationModuleType;
61  import org.apache.geronimo.kernel.config.NoSuchConfigException;
62  import org.apache.geronimo.kernel.repository.Artifact;
63  import org.apache.geronimo.kernel.repository.Environment;
64  
65  /**
66   * @version $Rev:385232 $ $Date: 2006-11-06 02:28:54 -0800 (Mon, 06 Nov 2006) $
67   */
68  public class DeploymentContext {
69      private final File baseDir;
70      private final File inPlaceConfigurationDir;
71      private final ResourceContext resourceContext;
72      private final byte[] buffer = new byte[4096];
73      private final Map childConfigurationDatas = new LinkedHashMap();
74      private final ConfigurationManager configurationManager;
75      private final Configuration configuration;
76      private final Naming naming;
77      private final List additionalDeployment = new ArrayList();
78      protected final AbstractName moduleName;
79  
80      public DeploymentContext(File baseDir, File inPlaceConfigurationDir, Environment environment, AbstractName moduleName, ConfigurationModuleType moduleType, Naming naming, ConfigurationManager configurationManager, Collection repositories) throws DeploymentException {
81          this(baseDir, inPlaceConfigurationDir, environment, moduleName, moduleType, naming, createConfigurationManager(configurationManager, repositories));
82      }
83  
84      public DeploymentContext(File baseDir, File inPlaceConfigurationDir, Environment environment, AbstractName moduleName, ConfigurationModuleType moduleType, Naming naming, ConfigurationManager configurationManager) throws DeploymentException {
85          if (baseDir == null) throw new NullPointerException("baseDir is null");
86          if (environment == null) throw new NullPointerException("environment is null");
87          if (moduleType == null) throw new NullPointerException("type is null");
88          if (configurationManager == null) throw new NullPointerException("configurationManager is null");
89  
90          if (!baseDir.exists()) {
91              baseDir.mkdirs();
92          }
93          this.baseDir = baseDir;
94  
95          this.inPlaceConfigurationDir = inPlaceConfigurationDir;
96  
97          this.moduleName = moduleName;
98  
99          this.naming = naming;
100 
101         this.configuration = createTempConfiguration(environment, moduleType, baseDir, inPlaceConfigurationDir, configurationManager, naming);
102 
103         this.configurationManager = configurationManager;
104         
105         if (null == inPlaceConfigurationDir) {
106             resourceContext = new CopyResourceContext(configuration, baseDir);
107         } else {
108             resourceContext = new InPlaceResourceContext(configuration, inPlaceConfigurationDir);
109         }
110     }
111 
112     private static ConfigurationManager createConfigurationManager(ConfigurationManager configurationManager, Collection repositories) {
113         return new DeploymentConfigurationManager(configurationManager, repositories);
114     }
115 
116     private static Configuration createTempConfiguration(Environment environment, ConfigurationModuleType moduleType, File baseDir, File inPlaceConfigurationDir, ConfigurationManager configurationManager, Naming naming) throws DeploymentException {
117         try {
118             configurationManager.loadConfiguration(new ConfigurationData(moduleType, null, null, null, environment, baseDir, inPlaceConfigurationDir, naming));
119             return configurationManager.getConfiguration(environment.getConfigId());
120         } catch (Exception e) {
121             throw new DeploymentException("Unable to create configuration for deployment", e);
122         }
123     }
124 
125     public ConfigurationManager getConfigurationManager() {
126         return configurationManager;
127     }
128 
129     public Artifact getConfigID() {
130         return configuration.getId();
131     }
132 
133     public File getBaseDir() {
134         return baseDir;
135     }
136 
137     public File getInPlaceConfigurationDir() {
138         return inPlaceConfigurationDir;
139     }
140 
141     public Naming getNaming() {
142         return naming;
143     }
144 
145     public GBeanData addGBean(String name, GBeanInfo gbeanInfo) throws GBeanAlreadyExistsException {
146         if (name == null) throw new NullPointerException("name is null");
147         if (gbeanInfo == null) throw new NullPointerException("gbean is null");
148         GBeanData gbean = new GBeanData(gbeanInfo);
149         configuration.addGBean(name, gbean);
150         return gbean;
151     }
152 
153     public void addGBean(GBeanData gbean) throws GBeanAlreadyExistsException {
154         if (gbean == null) throw new NullPointerException("gbean is null");
155         if (gbean.getAbstractName() == null) throw new NullPointerException("gbean.getAbstractName() is null");
156         configuration.addGBean(gbean);
157     }
158 
159     public void removeGBean(AbstractName name) throws GBeanNotFoundException {
160         if (name == null) throw new NullPointerException("name is null");
161         configuration.removeGBean(name);
162     }
163 
164     public Set getGBeanNames() {
165         return new HashSet(configuration.getGBeans().keySet());
166     }
167 
168     /**
169      * @deprecated use findGBeans(pattern)
170      */
171     public Set listGBeans(AbstractNameQuery pattern) {
172         return findGBeans(pattern);
173     }
174 
175     public AbstractName findGBean(AbstractNameQuery pattern) throws GBeanNotFoundException {
176         return configuration.findGBean(pattern);
177     }
178 
179     public AbstractName findGBean(Set patterns) throws GBeanNotFoundException {
180         return configuration.findGBean(patterns);
181     }
182 
183     public LinkedHashSet findGBeans(AbstractNameQuery pattern) {
184         return configuration.findGBeans(pattern);
185     }
186 
187     public LinkedHashSet findGBeans(Set patterns) {
188         return configuration.findGBeans(patterns);
189     }
190 
191     public GBeanData getGBeanInstance(AbstractName name) throws GBeanNotFoundException {
192         Map gbeans = configuration.getGBeans();
193         GBeanData gbeanData = (GBeanData) gbeans.get(name);
194         if (gbeanData == null) {
195             throw new GBeanNotFoundException(name);
196         }
197         return gbeanData;
198     }
199 
200     /**
201      * Add a packed jar file into the deployment context and place it into the
202      * path specified in the target path.  The newly added packed jar is added
203      * to the classpath of the configuration.
204      *
205      * @param targetPath where the packed jar file should be placed
206      * @param jarFile the jar file to copy
207      * @throws IOException if there's a problem copying the jar file
208      */
209     public void addIncludeAsPackedJar(URI targetPath, JarFile jarFile) throws IOException {
210         resourceContext.addIncludeAsPackedJar(targetPath, jarFile);
211     }
212 
213     /**
214      * Add a ZIP file entry into the deployment context and place it into the
215      * path specified in the target path.  The newly added entry is added
216      * to the classpath of the configuration.
217      *
218      * @param targetPath where the ZIP file entry should be placed
219      * @param zipFile the ZIP file
220      * @param zipEntry the ZIP file entry
221      * @throws IOException if there's a problem copying the ZIP entry
222      */
223     public void addInclude(URI targetPath, ZipFile zipFile, ZipEntry zipEntry) throws IOException {
224         resourceContext.addInclude(targetPath, zipFile, zipEntry);
225     }
226 
227     /**
228      * Add a file into the deployment context and place it into the
229      * path specified in the target path.  The newly added file is added
230      * to the classpath of the configuration.
231      *
232      * @param targetPath where the file should be placed
233      * @param source     the URL of file to be copied
234      * @throws IOException if there's a problem copying the ZIP entry
235      */
236     public void addInclude(URI targetPath, URL source) throws IOException {
237         resourceContext.addInclude(targetPath, source);
238     }
239 
240     /**
241      * Add a file into the deployment context and place it into the
242      * path specified in the target path.  The newly added file is added
243      * to the classpath of the configuration.
244      *
245      * @param targetPath where the file should be placed
246      * @param source     the file to be copied
247      * @throws IOException if there's a problem copying the ZIP entry
248      */
249     public void addInclude(URI targetPath, File source) throws IOException {
250         resourceContext.addInclude(targetPath, source);
251     }
252 
253     /**
254      * Import the classpath from a jar file's manifest.  The imported classpath
255      * is crafted relative to <code>moduleBaseUri</code>.
256      *
257      * @param moduleFile    the jar file from which the manifest is obtained.
258      * @param moduleBaseUri the base for the imported classpath
259      * @throws DeploymentException if there is a problem with the classpath in
260      *                             the manifest
261      */
262     public void addManifestClassPath(JarFile moduleFile, URI moduleBaseUri) throws DeploymentException {
263         Manifest manifest;
264         try {
265             manifest = moduleFile.getManifest();
266         } catch (IOException e) {
267             throw new DeploymentException("Could not read manifest: " + moduleBaseUri);
268         }
269 
270         if (manifest == null) {
271             return;
272         }
273         String manifestClassPath = manifest.getMainAttributes().getValue(Attributes.Name.CLASS_PATH);
274         if (manifestClassPath == null) {
275             return;
276         }
277 
278         for (StringTokenizer tokenizer = new StringTokenizer(manifestClassPath, " "); tokenizer.hasMoreTokens();) {
279             String path = tokenizer.nextToken();
280 
281             URI pathUri;
282             try {
283                 pathUri = new URI(path);
284             } catch (URISyntaxException e) {
285                 throw new DeploymentException("Invalid manifest classpath entry: module=" + moduleBaseUri + ", path=" + path);
286             }
287 
288             if (!pathUri.getPath().endsWith(".jar")) {
289                 throw new DeploymentException("Manifest class path entries must end with the .jar extension (J2EE 1.4 Section 8.2): module=" + moduleBaseUri);
290             }
291             if (pathUri.isAbsolute()) {
292                 throw new DeploymentException("Manifest class path entries must be relative (J2EE 1.4 Section 8.2): moduel=" + moduleBaseUri);
293             }
294 
295             try {
296                 URI targetUri = moduleBaseUri.resolve(pathUri);
297                 if (targetUri.getPath().endsWith("/")) throw new IllegalStateException("target path must not end with a '/' character: " + targetUri);
298                 configuration.addToClassPath(targetUri.toString());
299             } catch (IOException e) {
300                 throw new DeploymentException(e);
301             }
302         }
303     }
304 
305     public void addClass(URI targetPath, String fqcn, byte[] bytes) throws IOException, URISyntaxException {
306         if (!targetPath.getPath().endsWith("/")) throw new IllegalStateException("target path must end with a '/' character: " + targetPath);
307 
308         String classFileName = fqcn.replace('.', '/') + ".class";
309 
310         File targetFile = getTargetFile(new URI(targetPath.toString() + classFileName));
311         addFile(targetFile, new ByteArrayInputStream(bytes));
312 
313         configuration.addToClassPath(targetPath.toString());
314     }
315 
316     public void addFile(URI targetPath, ZipFile zipFile, ZipEntry zipEntry) throws IOException {
317         resourceContext.addFile(targetPath, zipFile, zipEntry);
318     }
319 
320     public void addFile(URI targetPath, URL source) throws IOException {
321         resourceContext.addFile(targetPath, source);
322     }
323 
324     public void addFile(URI targetPath, File source) throws IOException {
325         resourceContext.addFile(targetPath, source);
326     }
327 
328     public void addFile(URI targetPath, String source) throws IOException {
329         resourceContext.addFile(targetPath, source);
330     }
331 
332     private void addFile(File targetFile, InputStream source) throws IOException {
333         targetFile.getParentFile().mkdirs();
334         OutputStream out = null;
335         try {
336             out = new FileOutputStream(targetFile);
337             int count;
338             while ((count = source.read(buffer)) > 0) {
339                 out.write(buffer, 0, count);
340             }
341         } finally {
342             DeploymentUtil.close(out);
343         }
344     }
345 
346     public File getTargetFile(URI targetPath) {
347         return resourceContext.getTargetFile(targetPath);
348     }
349 
350     public ClassLoader getClassLoader() throws DeploymentException {
351         return configuration.getConfigurationClassLoader();
352     }
353 
354     public Configuration getConfiguration() {
355         return configuration;
356     }
357 
358     public void flush() throws IOException{
359         resourceContext.flush();
360     }
361 
362     public void close() throws IOException, DeploymentException {
363         if (configurationManager != null) {
364             try {
365                 configurationManager.unloadConfiguration(configuration.getId());
366             } catch (NoSuchConfigException ignored) {
367                 //ignore
368             }
369         }
370     }
371 
372     public void addChildConfiguration(String moduleName, ConfigurationData configurationData) {
373         childConfigurationDatas.put(moduleName, configurationData);
374     }
375 
376     public ConfigurationData getConfigurationData() throws DeploymentException {
377         List failures = verify();
378         if (!failures.isEmpty()) {
379             StringBuffer message = new StringBuffer();
380             for (Iterator iterator = failures.iterator(); iterator.hasNext();) {
381                 String failure = (String) iterator.next();
382                 if (message.length() > 0) message.append("\n");
383                 message.append(failure);
384             }
385             throw new DeploymentException(message.toString());
386         }
387 
388         ArrayList gbeans = new ArrayList(configuration.getGBeans().values());
389         Collections.sort(gbeans, new GBeanData.PriorityComparator());
390         ConfigurationData configurationData = new ConfigurationData(configuration.getModuleType(),
391                 new LinkedHashSet(configuration.getClassPath()),
392                 gbeans,
393                 childConfigurationDatas,
394                 configuration.getEnvironment(),
395                 baseDir,
396                 inPlaceConfigurationDir,
397                 naming);
398 
399         for (Iterator iterator = additionalDeployment.iterator(); iterator.hasNext();) {
400             ConfigurationData ownedConfiguration = (ConfigurationData) iterator.next();
401             configurationData.addOwnedConfigurations(ownedConfiguration.getId());
402         }
403 
404         return configurationData;
405     }
406 
407     public void addAdditionalDeployment(ConfigurationData configurationData) {
408         additionalDeployment.add(configurationData);
409     }
410 
411     public List getAdditionalDeployment() {
412         return additionalDeployment;
413     }
414 
415     public AbstractName getModuleName() {
416         return moduleName;
417     }
418 
419     public List verify() throws DeploymentException {
420         List failures = new ArrayList();
421         for (Iterator iterator = configuration.getGBeans().entrySet().iterator(); iterator.hasNext();) {
422             Map.Entry entry = (Map.Entry) iterator.next();
423             AbstractName name = (AbstractName) entry.getKey();
424             GBeanData gbean = (GBeanData) entry.getValue();
425 
426             for (Iterator iterator1 = gbean.getReferences().entrySet().iterator(); iterator1.hasNext();) {
427                 Map.Entry referenceEntry = (Map.Entry) iterator1.next();
428                 String referenceName = (String) referenceEntry.getKey();
429                 ReferencePatterns referencePatterns = (ReferencePatterns) referenceEntry.getValue();
430 
431                 String failure = verifyReference(gbean, referenceName, referencePatterns);
432                 if (failure != null) {
433                     failures.add(failure);
434                 }
435             }
436 
437             for (Iterator iterator1 = gbean.getDependencies().iterator(); iterator1.hasNext();) {
438                 ReferencePatterns referencePatterns = (ReferencePatterns) iterator1.next();
439                 String failure = verifyDependency(name, referencePatterns);
440                 if (failure != null) {
441                     failures.add(failure);
442                 }
443             }
444         }
445         return failures;
446     }
447 
448     private String verifyReference(GBeanData gbean, String referenceName, ReferencePatterns referencePatterns) {
449         GReferenceInfo referenceInfo = gbean.getGBeanInfo().getReference(referenceName);
450 
451         // if there is no reference info we can't verify
452         if (referenceInfo == null) return null;
453 
454         // A collection valued reference doesn't need to be verified
455         if (referenceInfo.getProxyType().equals(Collection.class.getName())) return null;
456 
457         if (!isVerifyReference(referencePatterns)) {
458             return "Unable to resolve reference \"" + referenceName + "\" in gbean " +
459                     gbean.getAbstractName() + " to a gbean matching the pattern " + referencePatterns.getPatterns();
460         }
461         return null;
462     }
463 
464     private String verifyDependency(AbstractName name, ReferencePatterns referencePatterns) {
465         if (!isVerifyReference(referencePatterns)) {
466             return "Unable to resolve dependency in gbean " + name +
467                     " to a gbean matching the pattern " + referencePatterns.getPatterns();
468         }
469 
470         return null;
471     }
472 
473     private boolean isVerifyReference(ReferencePatterns referencePatterns) {
474         // we can't verify a resolved reference since it will have a specific artifact already set...
475         // hopefully the deployer won't generate bad resolved references
476         if (referencePatterns.isResolved()) return true;
477 
478         // Do not verify the reference if it has an explicit depenency on another artifact, because it it likely
479         // that the other artifact is not in the "environment" (if it were you wouldn't use the long form)
480         Set patterns = referencePatterns.getPatterns();
481         for (Iterator iterator = patterns.iterator(); iterator.hasNext();) {
482             AbstractNameQuery query = (AbstractNameQuery) iterator.next();
483             if (query.getArtifact() != null) return true;
484         }
485 
486         // attempt to find the bean
487         try {
488             findGBean(patterns);
489             return true;
490         } catch (GBeanNotFoundException e) {
491             return false;
492         }
493     }
494 }