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