001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  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.service;
019    
020    import java.beans.PropertyEditorManager;
021    import java.io.File;
022    import java.io.FileNotFoundException;
023    import java.io.IOException;
024    import java.net.URI;
025    import java.net.URL;
026    import java.util.Collection;
027    import java.util.Collections;
028    import java.util.jar.JarFile;
029    
030    import javax.xml.namespace.QName;
031    
032    import org.apache.geronimo.common.DeploymentException;
033    import org.apache.geronimo.deployment.ConfigurationBuilder;
034    import org.apache.geronimo.deployment.DeploymentContext;
035    import org.apache.geronimo.deployment.ModuleIDBuilder;
036    import org.apache.geronimo.deployment.NamespaceDrivenBuilder;
037    import org.apache.geronimo.deployment.NamespaceDrivenBuilderCollection;
038    import org.apache.geronimo.deployment.util.DeploymentUtil;
039    import org.apache.geronimo.deployment.xbeans.ArtifactType;
040    import org.apache.geronimo.deployment.xbeans.EnvironmentType;
041    import org.apache.geronimo.deployment.xbeans.ModuleDocument;
042    import org.apache.geronimo.deployment.xbeans.ModuleType;
043    import org.apache.geronimo.deployment.xmlbeans.XmlBeansUtil;
044    import org.apache.geronimo.gbean.AbstractName;
045    import org.apache.geronimo.gbean.GBeanInfo;
046    import org.apache.geronimo.gbean.GBeanInfoBuilder;
047    import org.apache.geronimo.kernel.Kernel;
048    import org.apache.geronimo.kernel.Naming;
049    import org.apache.geronimo.kernel.config.ConfigurationAlreadyExistsException;
050    import org.apache.geronimo.kernel.config.ConfigurationManager;
051    import org.apache.geronimo.kernel.config.ConfigurationModuleType;
052    import org.apache.geronimo.kernel.config.ConfigurationStore;
053    import org.apache.geronimo.kernel.config.ConfigurationUtil;
054    import org.apache.geronimo.kernel.config.SimpleConfigurationManager;
055    import org.apache.geronimo.kernel.repository.Artifact;
056    import org.apache.geronimo.kernel.repository.ArtifactResolver;
057    import org.apache.geronimo.kernel.repository.Environment;
058    import org.apache.geronimo.kernel.repository.Repository;
059    import org.apache.xmlbeans.XmlCursor;
060    import org.apache.xmlbeans.XmlException;
061    import org.apache.xmlbeans.XmlObject;
062    
063    /**
064     * @version $Rev: 476049 $ $Date: 2006-11-16 23:35:17 -0500 (Thu, 16 Nov 2006) $
065     */
066    public class ServiceConfigBuilder implements ConfigurationBuilder {
067        private final Environment defaultEnvironment;
068        private final Collection repositories;
069    
070        private static final QName MODULE_QNAME = ModuleDocument.type.getDocumentElementName();
071        public static final String SERVICE_MODULE = "ServiceModule";
072        private final Naming naming;
073        private final ConfigurationManager configurationManager;
074        private final NamespaceDrivenBuilderCollection serviceBuilders;
075    
076        public ServiceConfigBuilder(Environment defaultEnvironment, Collection repositories, Naming naming) {
077            this(defaultEnvironment, repositories, Collections.EMPTY_LIST, naming, null);
078        }
079    
080        public ServiceConfigBuilder(Environment defaultEnvironment, Collection repositories, Collection serviceBuilders, Kernel kernel) {
081            this(defaultEnvironment, repositories, serviceBuilders, kernel.getNaming(), ConfigurationUtil.getConfigurationManager(kernel));
082        }
083    
084        public ServiceConfigBuilder(Environment defaultEnvironment, Collection repositories, Collection serviceBuilders, Naming naming) {
085            this(defaultEnvironment, repositories, serviceBuilders, naming, null);
086        }
087    
088        private ServiceConfigBuilder(Environment defaultEnvironment, Collection repositories, Collection serviceBuilders, Naming naming, ConfigurationManager configurationManager) {
089            this.naming = naming;
090            this.configurationManager = configurationManager;
091    
092            EnvironmentBuilder environmentBuilder = new EnvironmentBuilder();
093            this.defaultEnvironment = defaultEnvironment;
094    
095            this.repositories = repositories;
096            this.serviceBuilders = new NamespaceDrivenBuilderCollection(serviceBuilders, GBeanBuilder.SERVICE_QNAME);
097        }
098    
099        public Object getDeploymentPlan(File planFile, JarFile jarFile, ModuleIDBuilder idBuilder) throws DeploymentException {
100            if (planFile == null && jarFile == null) {
101                return null;
102            }
103    
104            try {
105                XmlObject xmlObject;
106                if (planFile != null) {
107                    xmlObject = XmlBeansUtil.parse(planFile.toURL(), getClass().getClassLoader());
108                } else {
109                    URL path = DeploymentUtil.createJarURL(jarFile, "META-INF/geronimo-service.xml");
110                    try {
111                        xmlObject = XmlBeansUtil.parse(path, getClass().getClassLoader());
112                    } catch (FileNotFoundException e) {
113                        // It has a JAR but no plan, and nothing at META-INF/geronimo-service.xml,
114                        // therefore it's not a service deployment
115                        return null;
116                    }
117                }
118                if(xmlObject == null) {
119                    return null;
120                }
121    
122                XmlCursor cursor = xmlObject.newCursor();
123                try {
124                    cursor.toFirstChild();
125                    if (!MODULE_QNAME.equals(cursor.getName())) {
126                        return null;
127                    }
128                } finally {
129                    cursor.dispose();
130                }
131                ModuleDocument moduleDoc;
132                if (xmlObject instanceof ModuleDocument) {
133                    moduleDoc = (ModuleDocument) xmlObject;
134                } else {
135                    moduleDoc = (ModuleDocument) xmlObject.changeType(ModuleDocument.type);
136                }
137                XmlBeansUtil.validateDD(moduleDoc);
138                // If there's no artifact ID and we won't be able to figure one out later, use the plan file name.  Bit of a hack.
139                if(jarFile == null && (moduleDoc.getModule().getEnvironment() == null ||
140                            moduleDoc.getModule().getEnvironment().getModuleId() == null ||
141                            moduleDoc.getModule().getEnvironment().getModuleId().getArtifactId() == null)) {
142                    if(moduleDoc.getModule().getEnvironment() == null) {
143                        moduleDoc.getModule().addNewEnvironment();
144                    }
145                    if(moduleDoc.getModule().getEnvironment().getModuleId() == null) {
146                        moduleDoc.getModule().getEnvironment().addNewModuleId();
147                    }
148                    String name = planFile.getName();
149                    int pos = name.lastIndexOf('.');
150                    if(pos > -1) {
151                        name = name.substring(0, pos);
152                    }
153                    moduleDoc.getModule().getEnvironment().getModuleId().setArtifactId(name);
154                }
155                return moduleDoc.getModule();
156            } catch (XmlException e) {
157                throw new DeploymentException("Could not parse xml in plan", e);
158            } catch (IOException e) {
159                throw new DeploymentException("no plan at " + planFile, e);
160            }
161        }
162    
163        public Artifact getConfigurationID(Object plan, JarFile module, ModuleIDBuilder idBuilder) throws IOException, DeploymentException {
164            ModuleType configType = (ModuleType) plan;
165            EnvironmentType environmentType = configType.getEnvironment();
166            Environment environment = EnvironmentBuilder.buildEnvironment(environmentType, defaultEnvironment);
167            idBuilder.resolve(environment, module == null ? "" : new File(module.getName()).getName(), "car");
168            if(!environment.getConfigId().isResolved()) {
169                throw new IllegalStateException("Service Module ID is not fully populated ("+environment.getConfigId()+")");
170            }
171            return environment.getConfigId();
172        }
173    
174        public DeploymentContext buildConfiguration(boolean inPlaceDeployment, Artifact configId, Object plan, JarFile jar, Collection configurationStores, ArtifactResolver artifactResolver, ConfigurationStore targetConfigurationStore) throws IOException, DeploymentException {
175            ModuleType configType = (ModuleType) plan;
176    
177            return buildConfiguration(inPlaceDeployment, configId, configType, jar, configurationStores, artifactResolver, targetConfigurationStore);
178        }
179    
180        public DeploymentContext buildConfiguration(boolean inPlaceDeployment, Artifact configId, ModuleType moduleType, JarFile jar, Collection configurationStores, ArtifactResolver artifactResolver, ConfigurationStore targetConfigurationStore) throws DeploymentException, IOException {
181            ArtifactType type = moduleType.getEnvironment().isSetModuleId() ? moduleType.getEnvironment().getModuleId() : moduleType.getEnvironment().addNewModuleId();
182            type.setArtifactId(configId.getArtifactId());
183            type.setGroupId(configId.getGroupId());
184            type.setType(configId.getType());
185            type.setVersion(configId.getVersion().toString());
186            Environment environment = EnvironmentBuilder.buildEnvironment(moduleType.getEnvironment(), defaultEnvironment);
187            if(!environment.getConfigId().isResolved()) {
188                throw new IllegalStateException("Module ID should be fully resolved by now (not "+environment.getConfigId()+")");
189            }
190            File outfile;
191            try {
192                outfile = targetConfigurationStore.createNewConfigurationDir(configId);
193            } catch (ConfigurationAlreadyExistsException e) {
194                throw new DeploymentException(e);
195            }
196    
197            DeploymentContext context = null;
198            try {
199                ConfigurationManager configurationManager = this.configurationManager;
200                if (configurationManager == null) {
201                    configurationManager = new SimpleConfigurationManager(configurationStores, artifactResolver, repositories);
202                }
203    
204                AbstractName moduleName = naming.createRootName(configId, configId.toString(), SERVICE_MODULE);
205                context = new DeploymentContext(outfile,
206                        inPlaceDeployment && null != jar ? DeploymentUtil.toFile(jar) : null,
207                        environment,
208                        moduleName,
209                        ConfigurationModuleType.SERVICE,
210                        naming,
211                        configurationManager,
212                        repositories);
213                if(jar != null) {
214                    File file = new File(jar.getName());
215                    context.addIncludeAsPackedJar(URI.create(file.getName()), jar);
216                }
217    
218                serviceBuilders.build(moduleType, context, context);
219                return context;
220            } catch (DeploymentException de) {
221                cleanupAfterFailedBuild(context, outfile);
222                throw de;
223            } catch (IOException ie) {
224                cleanupAfterFailedBuild(context, outfile);
225                throw ie;
226            } catch (RuntimeException re) {
227                cleanupAfterFailedBuild(context, outfile);
228                throw re;
229            } catch (Error e) {
230                cleanupAfterFailedBuild(context, outfile);
231                throw e;
232            }
233        }
234    
235        private void cleanupAfterFailedBuild(DeploymentContext context, File directory) {
236            try {
237                if (context !=null) {
238                    context.close();
239                }
240            } catch (DeploymentException de) {
241                // ignore error on cleanup
242            } catch (IOException ioe) {
243                // ignore error on cleanu
244            }
245            if (directory != null) {
246                DeploymentUtil.recursiveDelete(directory);
247            }
248        }
249    
250        public static final GBeanInfo GBEAN_INFO;
251    
252        static {
253            PropertyEditorManager.registerEditor(Environment.class, EnvironmentBuilder.class);
254    
255            GBeanInfoBuilder infoBuilder = GBeanInfoBuilder.createStatic(ServiceConfigBuilder.class, CONFIG_BUILDER);
256    
257            infoBuilder.addInterface(ConfigurationBuilder.class);
258    
259            infoBuilder.addAttribute("defaultEnvironment", Environment.class, true);
260            infoBuilder.addReference("Repository", Repository.class, "Repository");
261            infoBuilder.addReference("ServiceBuilders", NamespaceDrivenBuilder.class, "ModuleBuilder");
262            infoBuilder.addAttribute("kernel", Kernel.class, false, false);
263    
264            infoBuilder.setConstructor(new String[]{"defaultEnvironment", "Repository", "ServiceBuilders", "kernel"});
265    
266            GBEAN_INFO = infoBuilder.getBeanInfo();
267        }
268    
269        public static GBeanInfo getGBeanInfo() {
270            return GBEAN_INFO;
271        }
272    
273    }