001    /**
002     *
003     * Copyright 2003-2004 The Apache Software Foundation
004     *
005     *  Licensed under the Apache License, Version 2.0 (the "License");
006     *  you may not use this file except in compliance with the License.
007     *  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    
018    package org.apache.geronimo.deployment;
019    
020    import java.io.ByteArrayInputStream;
021    import java.io.File;
022    import java.io.FileOutputStream;
023    import java.io.IOException;
024    import java.io.InputStream;
025    import java.io.OutputStream;
026    import java.net.URI;
027    import java.net.URISyntaxException;
028    import java.net.URL;
029    import java.util.ArrayList;
030    import java.util.Collection;
031    import java.util.HashSet;
032    import java.util.Iterator;
033    import java.util.LinkedHashMap;
034    import java.util.LinkedHashSet;
035    import java.util.List;
036    import java.util.Map;
037    import java.util.Set;
038    import java.util.StringTokenizer;
039    import java.util.Collections;
040    import java.util.jar.Attributes;
041    import java.util.jar.JarFile;
042    import java.util.jar.Manifest;
043    import java.util.zip.ZipEntry;
044    import java.util.zip.ZipFile;
045    
046    import org.apache.geronimo.common.DeploymentException;
047    import org.apache.geronimo.deployment.util.DeploymentUtil;
048    import org.apache.geronimo.gbean.AbstractName;
049    import org.apache.geronimo.gbean.AbstractNameQuery;
050    import org.apache.geronimo.gbean.GBeanData;
051    import org.apache.geronimo.gbean.GBeanInfo;
052    import org.apache.geronimo.gbean.ReferencePatterns;
053    import org.apache.geronimo.gbean.GReferenceInfo;
054    import org.apache.geronimo.kernel.GBeanAlreadyExistsException;
055    import org.apache.geronimo.kernel.GBeanNotFoundException;
056    import org.apache.geronimo.kernel.Naming;
057    import org.apache.geronimo.kernel.config.Configuration;
058    import org.apache.geronimo.kernel.config.ConfigurationData;
059    import org.apache.geronimo.kernel.config.ConfigurationManager;
060    import org.apache.geronimo.kernel.config.ConfigurationModuleType;
061    import org.apache.geronimo.kernel.config.NoSuchConfigException;
062    import org.apache.geronimo.kernel.repository.Artifact;
063    import org.apache.geronimo.kernel.repository.Environment;
064    
065    /**
066     * @version $Rev:385232 $ $Date: 2006-11-06 02:28:54 -0800 (Mon, 06 Nov 2006) $
067     */
068    public class DeploymentContext {
069        private final File baseDir;
070        private final File inPlaceConfigurationDir;
071        private final ResourceContext resourceContext;
072        private final byte[] buffer = new byte[4096];
073        private final Map childConfigurationDatas = new LinkedHashMap();
074        private final ConfigurationManager configurationManager;
075        private final Configuration configuration;
076        private final Naming naming;
077        private final List additionalDeployment = new ArrayList();
078        protected final AbstractName moduleName;
079    
080        public DeploymentContext(File baseDir, File inPlaceConfigurationDir, Environment environment, AbstractName moduleName, ConfigurationModuleType moduleType, Naming naming, ConfigurationManager configurationManager, Collection repositories) throws DeploymentException {
081            this(baseDir, inPlaceConfigurationDir, environment, moduleName, moduleType, naming, createConfigurationManager(configurationManager, repositories));
082        }
083    
084        public DeploymentContext(File baseDir, File inPlaceConfigurationDir, Environment environment, AbstractName moduleName, ConfigurationModuleType moduleType, Naming naming, ConfigurationManager configurationManager) throws DeploymentException {
085            if (baseDir == null) throw new NullPointerException("baseDir is null");
086            if (environment == null) throw new NullPointerException("environment is null");
087            if (moduleType == null) throw new NullPointerException("type is null");
088            if (configurationManager == null) throw new NullPointerException("configurationManager is null");
089    
090            if (!baseDir.exists()) {
091                baseDir.mkdirs();
092            }
093            this.baseDir = baseDir;
094    
095            this.inPlaceConfigurationDir = inPlaceConfigurationDir;
096    
097            this.moduleName = moduleName;
098    
099            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    }