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 }