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.Map;
029    import java.util.HashMap;
030    import java.util.jar.JarFile;
031    
032    import javax.xml.namespace.QName;
033    
034    import org.apache.geronimo.common.DeploymentException;
035    import org.apache.geronimo.deployment.ConfigurationBuilder;
036    import org.apache.geronimo.deployment.DeploymentContext;
037    import org.apache.geronimo.deployment.ModuleIDBuilder;
038    import org.apache.geronimo.deployment.NamespaceDrivenBuilder;
039    import org.apache.geronimo.deployment.NamespaceDrivenBuilderCollection;
040    import org.apache.geronimo.deployment.util.DeploymentUtil;
041    import org.apache.geronimo.deployment.xbeans.ArtifactType;
042    import org.apache.geronimo.deployment.xbeans.EnvironmentType;
043    import org.apache.geronimo.deployment.xbeans.ModuleDocument;
044    import org.apache.geronimo.deployment.xbeans.ModuleType;
045    import org.apache.geronimo.deployment.xmlbeans.XmlBeansUtil;
046    import org.apache.geronimo.gbean.AbstractName;
047    import org.apache.geronimo.gbean.GBeanInfo;
048    import org.apache.geronimo.gbean.GBeanInfoBuilder;
049    import org.apache.geronimo.gbean.GBeanLifecycle;
050    import org.apache.geronimo.kernel.Kernel;
051    import org.apache.geronimo.kernel.Naming;
052    import org.apache.geronimo.kernel.config.ConfigurationAlreadyExistsException;
053    import org.apache.geronimo.kernel.config.ConfigurationManager;
054    import org.apache.geronimo.kernel.config.ConfigurationModuleType;
055    import org.apache.geronimo.kernel.config.ConfigurationStore;
056    import org.apache.geronimo.kernel.config.ConfigurationUtil;
057    import org.apache.geronimo.kernel.config.SimpleConfigurationManager;
058    import org.apache.geronimo.kernel.repository.Artifact;
059    import org.apache.geronimo.kernel.repository.ArtifactResolver;
060    import org.apache.geronimo.kernel.repository.Environment;
061    import org.apache.geronimo.kernel.repository.Repository;
062    import org.apache.xmlbeans.XmlCursor;
063    import org.apache.xmlbeans.XmlException;
064    import org.apache.xmlbeans.XmlObject;
065    
066    /**
067     * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
068     */
069    public class ServiceConfigBuilder implements ConfigurationBuilder, GBeanLifecycle {
070        private final Environment defaultEnvironment;
071        private final Collection repositories;
072    
073        private static final QName MODULE_QNAME = ModuleDocument.type.getDocumentElementName();
074        public static final String SERVICE_MODULE = "ServiceModule";
075        private static final Map<String, String> NAMESPACE_UPDATES = new HashMap<String, String>();
076        static {
077            NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/deployment", "http://geronimo.apache.org/xml/ns/deployment-1.2");
078            NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/deployment-1.1", "http://geronimo.apache.org/xml/ns/deployment-1.2");
079            NAMESPACE_UPDATES.put("http://geronimo.apache.org/xml/ns/deployment/javabean", "http://geronimo.apache.org/xml/ns/deployment/javabean-1.0");
080        }
081    
082        private final Naming naming;
083        private final ConfigurationManager configurationManager;
084        private final NamespaceDrivenBuilderCollection serviceBuilders;
085    
086        public ServiceConfigBuilder(Environment defaultEnvironment, Collection repositories, Naming naming) {
087            this(defaultEnvironment, repositories, Collections.EMPTY_LIST, naming, null);
088        }
089    
090        public ServiceConfigBuilder(Environment defaultEnvironment, Collection repositories, Collection serviceBuilders, Kernel kernel) {
091            this(defaultEnvironment, repositories, serviceBuilders, kernel.getNaming(), ConfigurationUtil.getConfigurationManager(kernel));
092        }
093    
094        public ServiceConfigBuilder(Environment defaultEnvironment, Collection repositories, Collection serviceBuilders, Naming naming) {
095            this(defaultEnvironment, repositories, serviceBuilders, naming, null);
096        }
097    
098        public void doStart() throws Exception {
099            XmlBeansUtil.registerNamespaceUpdates(NAMESPACE_UPDATES);
100        }
101    
102        public void doStop() {
103            XmlBeansUtil.unregisterNamespaceUpdates(NAMESPACE_UPDATES);
104        }
105    
106        public void doFail() {
107            doStop();
108        }
109    
110        private ServiceConfigBuilder(Environment defaultEnvironment, Collection repositories, Collection serviceBuilders, Naming naming, ConfigurationManager configurationManager) {
111            this.naming = naming;
112            this.configurationManager = configurationManager;
113    
114            EnvironmentBuilder environmentBuilder = new EnvironmentBuilder();
115            this.defaultEnvironment = defaultEnvironment;
116    
117            this.repositories = repositories;
118            this.serviceBuilders = new NamespaceDrivenBuilderCollection(serviceBuilders, GBeanBuilder.SERVICE_QNAME);
119        }
120    
121        public Object getDeploymentPlan(File planFile, JarFile jarFile, ModuleIDBuilder idBuilder) throws DeploymentException {
122            if (planFile == null && jarFile == null) {
123                return null;
124            }
125    
126            try {
127                XmlObject xmlObject;
128                if (planFile != null) {
129                    xmlObject = XmlBeansUtil.parse(planFile.toURL(), getClass().getClassLoader());
130                } else {
131                    URL path = DeploymentUtil.createJarURL(jarFile, "META-INF/geronimo-service.xml");
132                    try {
133                        xmlObject = XmlBeansUtil.parse(path, getClass().getClassLoader());
134                    } catch (FileNotFoundException e) {
135                        // It has a JAR but no plan, and nothing at META-INF/geronimo-service.xml,
136                        // therefore it's not a service deployment
137                        return null;
138                    }
139                }
140                if(xmlObject == null) {
141                    return null;
142                }
143    
144                XmlCursor cursor = xmlObject.newCursor();
145                try {
146                    cursor.toFirstChild();
147                    if (!MODULE_QNAME.equals(cursor.getName())) {
148                        return null;
149                    }
150                } finally {
151                    cursor.dispose();
152                }
153                ModuleDocument moduleDoc;
154                if (xmlObject instanceof ModuleDocument) {
155                    moduleDoc = (ModuleDocument) xmlObject;
156                } else {
157                    moduleDoc = (ModuleDocument) xmlObject.changeType(ModuleDocument.type);
158                }
159                XmlBeansUtil.validateDD(moduleDoc);
160                // 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.
161                if(jarFile == null && (moduleDoc.getModule().getEnvironment() == null ||
162                            moduleDoc.getModule().getEnvironment().getModuleId() == null ||
163                            moduleDoc.getModule().getEnvironment().getModuleId().getArtifactId() == null)) {
164                    if(moduleDoc.getModule().getEnvironment() == null) {
165                        moduleDoc.getModule().addNewEnvironment();
166                    }
167                    if(moduleDoc.getModule().getEnvironment().getModuleId() == null) {
168                        moduleDoc.getModule().getEnvironment().addNewModuleId();
169                    }
170                    String name = planFile.getName();
171                    int pos = name.lastIndexOf('.');
172                    if(pos > -1) {
173                        name = name.substring(0, pos);
174                    }
175                    moduleDoc.getModule().getEnvironment().getModuleId().setArtifactId(name);
176                }
177                return moduleDoc.getModule();
178            } catch (XmlException e) {
179                throw new DeploymentException("Could not parse xml in plan", e);
180            } catch (IOException e) {
181                throw new DeploymentException("no plan at " + planFile, e);
182            }
183        }
184    
185        public Artifact getConfigurationID(Object plan, JarFile module, ModuleIDBuilder idBuilder) throws IOException, DeploymentException {
186            ModuleType configType = (ModuleType) plan;
187            EnvironmentType environmentType = configType.getEnvironment();
188            Environment environment = EnvironmentBuilder.buildEnvironment(environmentType, defaultEnvironment);
189            idBuilder.resolve(environment, module == null ? "" : new File(module.getName()).getName(), "car");
190            if(!environment.getConfigId().isResolved()) {
191                throw new IllegalStateException("Service Module ID is not fully populated ("+environment.getConfigId()+")");
192            }
193            return environment.getConfigId();
194        }
195    
196        public DeploymentContext buildConfiguration(boolean inPlaceDeployment, Artifact configId, Object plan, JarFile jar, Collection configurationStores, ArtifactResolver artifactResolver, ConfigurationStore targetConfigurationStore) throws IOException, DeploymentException {
197            ModuleType configType = (ModuleType) plan;
198    
199            return buildConfiguration(inPlaceDeployment, configId, configType, jar, configurationStores, artifactResolver, targetConfigurationStore);
200        }
201    
202        public DeploymentContext buildConfiguration(boolean inPlaceDeployment, Artifact configId, ModuleType moduleType, JarFile jar, Collection configurationStores, ArtifactResolver artifactResolver, ConfigurationStore targetConfigurationStore) throws DeploymentException, IOException {
203            ArtifactType type = moduleType.getEnvironment().isSetModuleId() ? moduleType.getEnvironment().getModuleId() : moduleType.getEnvironment().addNewModuleId();
204            type.setArtifactId(configId.getArtifactId());
205            type.setGroupId(configId.getGroupId());
206            type.setType(configId.getType());
207            type.setVersion(configId.getVersion().toString());
208            Environment environment = EnvironmentBuilder.buildEnvironment(moduleType.getEnvironment(), defaultEnvironment);
209            if(!environment.getConfigId().isResolved()) {
210                throw new IllegalStateException("Module ID should be fully resolved by now (not "+environment.getConfigId()+")");
211            }
212            File outfile;
213            try {
214                outfile = targetConfigurationStore.createNewConfigurationDir(configId);
215            } catch (ConfigurationAlreadyExistsException e) {
216                throw new DeploymentException(e);
217            }
218    
219            DeploymentContext context = null;
220            try {
221                ConfigurationManager configurationManager = this.configurationManager;
222                if (configurationManager == null) {
223                    configurationManager = new SimpleConfigurationManager(configurationStores, artifactResolver, repositories);
224                }
225    
226                AbstractName moduleName = naming.createRootName(configId, configId.toString(), SERVICE_MODULE);
227                context = new DeploymentContext(outfile,
228                        inPlaceDeployment && null != jar ? DeploymentUtil.toFile(jar) : null,
229                        environment,
230                        moduleName,
231                        ConfigurationModuleType.SERVICE,
232                        naming,
233                        configurationManager,
234                        repositories);
235                if(jar != null) {
236                    File file = new File(jar.getName());
237                    context.addIncludeAsPackedJar(URI.create(file.getName()), jar);
238                }
239    
240                serviceBuilders.build(moduleType, context, context);
241                return context;
242            } catch (DeploymentException de) {
243                cleanupAfterFailedBuild(context, outfile);
244                throw de;
245            } catch (IOException ie) {
246                cleanupAfterFailedBuild(context, outfile);
247                throw ie;
248            } catch (RuntimeException re) {
249                cleanupAfterFailedBuild(context, outfile);
250                throw re;
251            } catch (Error e) {
252                cleanupAfterFailedBuild(context, outfile);
253                throw e;
254            }
255        }
256    
257        private void cleanupAfterFailedBuild(DeploymentContext context, File directory) {
258            try {
259                if (context !=null) {
260                    context.close();
261                }
262            } catch (DeploymentException de) {
263                // ignore error on cleanup
264            } catch (IOException ioe) {
265                // ignore error on cleanu
266            }
267            if (directory != null) {
268                DeploymentUtil.recursiveDelete(directory);
269            }
270        }
271    
272        public static final GBeanInfo GBEAN_INFO;
273    
274        static {
275            PropertyEditorManager.registerEditor(Environment.class, EnvironmentBuilder.class);
276    
277            GBeanInfoBuilder infoBuilder = GBeanInfoBuilder.createStatic(ServiceConfigBuilder.class, CONFIG_BUILDER);
278    
279            infoBuilder.addInterface(ConfigurationBuilder.class);
280    
281            infoBuilder.addAttribute("defaultEnvironment", Environment.class, true);
282            infoBuilder.addReference("Repository", Repository.class, "Repository");
283            infoBuilder.addReference("ServiceBuilders", NamespaceDrivenBuilder.class, "ModuleBuilder");
284            infoBuilder.addAttribute("kernel", Kernel.class, false, false);
285    
286            infoBuilder.setConstructor(new String[]{"defaultEnvironment", "Repository", "ServiceBuilders", "kernel"});
287    
288            GBEAN_INFO = infoBuilder.getBeanInfo();
289        }
290    
291        public static GBeanInfo getGBeanInfo() {
292            return GBEAN_INFO;
293        }
294    
295    }