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 package org.apache.servicemix.geronimo;
018
019 import java.io.File;
020 import java.io.IOException;
021 import java.net.URI;
022 import java.net.URL;
023 import java.util.Collection;
024 import java.util.Enumeration;
025 import java.util.HashMap;
026 import java.util.Map;
027 import java.util.Properties;
028 import java.util.jar.JarFile;
029 import java.util.zip.ZipEntry;
030
031 import javax.jbi.component.Component;
032 import javax.jbi.component.ServiceUnitManager;
033 import javax.management.MalformedObjectNameException;
034
035 import org.apache.commons.logging.Log;
036 import org.apache.commons.logging.LogFactory;
037 import org.apache.geronimo.common.DeploymentException;
038 import org.apache.geronimo.deployment.ConfigurationBuilder;
039 import org.apache.geronimo.deployment.DeploymentContext;
040 import org.apache.geronimo.deployment.ModuleIDBuilder;
041 import org.apache.geronimo.deployment.service.EnvironmentBuilder;
042 import org.apache.geronimo.deployment.util.DeploymentUtil;
043 import org.apache.geronimo.deployment.xbeans.EnvironmentType;
044 import org.apache.geronimo.gbean.AbstractName;
045 import org.apache.geronimo.gbean.AbstractNameQuery;
046 import org.apache.geronimo.gbean.GBeanData;
047 import org.apache.geronimo.gbean.GBeanInfo;
048 import org.apache.geronimo.gbean.GBeanInfoBuilder;
049 import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
050 import org.apache.geronimo.kernel.GBeanNotFoundException;
051 import org.apache.geronimo.kernel.Kernel;
052 import org.apache.geronimo.kernel.config.ConfigurationAlreadyExistsException;
053 import org.apache.geronimo.kernel.config.ConfigurationModuleType;
054 import org.apache.geronimo.kernel.config.ConfigurationStore;
055 import org.apache.geronimo.kernel.config.ConfigurationUtil;
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.ImportType;
060 import org.apache.geronimo.kernel.repository.Repository;
061 import org.apache.servicemix.geronimo.deployment.SMJbiDocument;
062 import org.apache.servicemix.jbi.deployment.Descriptor;
063 import org.apache.servicemix.jbi.deployment.DescriptorFactory;
064 import org.apache.servicemix.jbi.deployment.ServiceUnit;
065 import org.apache.servicemix.jbi.deployment.SharedLibraryList;
066 import org.apache.xmlbeans.XmlObject;
067
068 public class ServiceMixConfigBuilder implements ConfigurationBuilder {
069
070 private static final Log log = LogFactory.getLog(ServiceMixConfigBuilder.class);
071
072 private final Environment defaultEnvironment;
073
074 private final Collection repositories;
075
076 private final AbstractNameQuery containerName;
077
078 private final Kernel kernel;
079
080 public static final GBeanInfo GBEAN_INFO;
081
082 static {
083 GBeanInfoBuilder infoFactory = new GBeanInfoBuilder(ServiceMixConfigBuilder.class, NameFactory.CONFIG_BUILDER);
084 infoFactory.addInterface(ConfigurationBuilder.class);
085 infoFactory.addAttribute("defaultEnvironment", Environment.class, true, true);
086 infoFactory.addAttribute("kernel", Kernel.class, false);
087 infoFactory.addAttribute("containerName", AbstractNameQuery.class, true, true);
088 infoFactory.addReference("Repositories", Repository.class, "Repository");
089 infoFactory.setConstructor(new String[] { "defaultEnvironment", "containerName", "Repositories", "kernel" });
090 GBEAN_INFO = infoFactory.getBeanInfo();
091 }
092
093 public static GBeanInfo getGBeanInfo() {
094 return GBEAN_INFO;
095 }
096
097 public ServiceMixConfigBuilder(Environment defaultEnvironment, AbstractNameQuery containerName, Collection repositories, Kernel kernel) {
098 this.defaultEnvironment = defaultEnvironment;
099 this.repositories = repositories;
100 this.kernel = kernel;
101 this.containerName = containerName;
102 }
103
104 /**
105 * Builds a deployment plan specific to this builder from a planFile and/or
106 * module if this builder can process it.
107 *
108 * @param planFile
109 * the deployment plan to examine; can be null
110 * @param module
111 * the URL of the module to examine; can be null
112 * @return the deployment plan, or null if this builder can not handle the
113 * module
114 * @throws org.apache.geronimo.common.DeploymentException
115 * if there was a problem with the configuration
116 */
117 public Object getDeploymentPlan(File planFile, JarFile module, ModuleIDBuilder idBuilder)
118 throws DeploymentException {
119 log.debug("Checking for ServiceMix deployment.");
120 if (module == null) {
121 return null;
122 }
123
124 // Check that the jbi descriptor is present
125 try {
126 URL url = DeploymentUtil.createJarURL(module, "META-INF/jbi.xml");
127 Descriptor descriptor = DescriptorFactory.buildDescriptor(url);
128 if (descriptor == null) {
129 return null;
130 }
131 DescriptorFactory.checkDescriptor(descriptor);
132
133 XmlObject object = null;
134 SMJbiDocument geronimoPlan = null;
135
136 try {
137 if (planFile != null) {
138 object = XmlObject.Factory.parse(planFile);
139 }
140 } catch (Exception e) {
141 log.info("error " + e);
142 }
143
144 if (object != null) {
145 try {
146
147 if (object instanceof SMJbiDocument) {
148 geronimoPlan = (SMJbiDocument) object;
149 } else {
150 geronimoPlan = (SMJbiDocument) object.changeType(SMJbiDocument.type);
151 }
152
153 } catch (Exception e) {
154 throw new DeploymentException("Geronimo Plan found but wrong format!" + e.getMessage());
155 }
156 }
157
158 return new DeploymentPlanWrapper(descriptor, geronimoPlan);
159 } catch (Exception e) {
160 log.debug("Not a ServiceMix deployment: no jbi.xml found.", e);
161 // no jbi.xml, not for us
162 return null;
163 }
164 }
165
166 /**
167 * Checks what configuration URL will be used for the provided module.
168 *
169 * @param plan
170 * the deployment plan
171 * @param module
172 * the module to build
173 * @return the ID that will be used for the Configuration
174 * @throws IOException
175 * if there was a problem reading or writing the files
176 * @throws org.apache.geronimo.common.DeploymentException
177 * if there was a problem with the configuration
178 */
179 public Artifact getConfigurationID(Object plan, JarFile module, ModuleIDBuilder idBuilder) throws IOException,
180 DeploymentException {
181 DeploymentPlanWrapper wrapper = (DeploymentPlanWrapper) plan;
182 Descriptor descriptor = wrapper.getServicemixDescriptor();
183 if (descriptor.getComponent() != null) {
184 return new Artifact("servicemix-components", descriptor.getComponent().getIdentification().getName(),
185 "0.0", "car");
186 } else if (descriptor.getServiceAssembly() != null) {
187 return new Artifact("servicemix-assemblies", descriptor.getServiceAssembly().getIdentification().getName(),
188 "0.0", "car");
189 } else if (descriptor.getSharedLibrary() != null) {
190 return new Artifact("servicemix-libraries", descriptor.getSharedLibrary().getIdentification().getName(),
191 descriptor.getSharedLibrary().getVersion(), "car");
192 } else {
193 throw new DeploymentException("Unable to construct configuration ID " + module.getName()
194 + ": unrecognized jbi package. Should be a component, assembly or library.");
195 }
196 }
197
198 /**
199 * Build a configuration from a local file
200 *
201 * @param inPlaceDeployment
202 * @param configId
203 * @param plan
204 * @param earFile
205 * @param configurationStores
206 * @param artifactResolver
207 * @param targetConfigurationStore
208 * @return the DeploymentContext information
209 * @throws IOException
210 * if there was a problem reading or writing the files
211 * @throws org.apache.geronimo.common.DeploymentException
212 * if there was a problem with the configuration
213 */
214 public DeploymentContext buildConfiguration(boolean inPlaceDeployment, Artifact configId, Object plan,
215 JarFile jarFile, Collection configurationStores, ArtifactResolver artifactResolver,
216 ConfigurationStore targetConfigurationStore) throws IOException, DeploymentException {
217 if (plan == null) {
218 log.warn("Expected a Descriptor but received null");
219 return null;
220 }
221 if (plan instanceof DeploymentPlanWrapper == false) {
222 log.warn("Expected a Descriptor but received a " + plan.getClass().getName());
223 return null;
224 }
225 if (((DeploymentPlanWrapper)plan).getServicemixDescriptor() == null) {
226 log.warn("Expected a SM Descriptor but received null");
227 return null;
228 }
229 File configurationDir;
230 try {
231 configurationDir = targetConfigurationStore.createNewConfigurationDir(configId);
232 } catch (ConfigurationAlreadyExistsException e) {
233 throw new DeploymentException(e);
234 }
235
236 Environment environment = new Environment();
237 environment.setConfigId(configId);
238 EnvironmentBuilder.mergeEnvironments(environment, defaultEnvironment);
239
240 DeploymentPlanWrapper wrapper = (DeploymentPlanWrapper) plan;
241 if (wrapper.getGeronimoPlan() != null) {
242
243 if (wrapper.getGeronimoPlan().getJbi() != null) {
244 EnvironmentType environmentType = wrapper.getGeronimoPlan().getJbi().getEnvironment();
245 if (environmentType != null) {
246 log.debug("Environment found in Geronimo Plan for Servicemix " + environmentType);
247 Environment geronimoPlanEnvironment = EnvironmentBuilder.buildEnvironment(environmentType);
248 EnvironmentBuilder.mergeEnvironments(environment, geronimoPlanEnvironment);
249 } else {
250 log.debug("no additional environment entry found in deployment plan for JBI component");
251 }
252 }
253 }
254
255 DeploymentContext context = null;
256 try {
257 Descriptor descriptor = wrapper.getServicemixDescriptor();
258 Map name = new HashMap();
259 name.put("Config", configId.toString());
260 context = new DeploymentContext(configurationDir,
261 inPlaceDeployment ? DeploymentUtil.toFile(jarFile) : null, environment,
262 new AbstractName(configId, name),
263 ConfigurationModuleType.SERVICE, kernel.getNaming(), ConfigurationUtil
264 .getConfigurationManager(kernel), repositories);
265 if (descriptor.getComponent() != null) {
266 buildComponent(descriptor, context, jarFile);
267 } else if (descriptor.getServiceAssembly() != null) {
268 buildServiceAssembly(descriptor, context, jarFile);
269 } else if (descriptor.getSharedLibrary() != null) {
270 buildSharedLibrary(descriptor, context, jarFile);
271 } else {
272 throw new IllegalStateException("Invalid jbi descriptor");
273 }
274 } catch (Exception e) {
275 if (context != null) {
276 context.close();
277 }
278 DeploymentUtil.recursiveDelete(configurationDir);
279 throw new DeploymentException("Unable to deploy", e);
280 }
281
282 return context;
283 }
284
285 protected void buildComponent(Descriptor descriptor, DeploymentContext context, JarFile module) throws Exception {
286 Environment environment = context.getConfiguration().getEnvironment();
287 // Unzip the component
288 File targetDir = new File(context.getBaseDir(), "install");
289 targetDir.mkdirs();
290 unzip(context, module, new URI("install/"));
291 // Create workspace dir
292 File workDir = new File(context.getBaseDir(), "workspace");
293 workDir.mkdirs();
294 // Create the bootstrap and perform installation
295 // TODO: Create the bootstrap and perform installation
296 // Add classpath entries
297 if ("self-first".equals(descriptor.getComponent().getComponentClassLoaderDelegation())) {
298 context.getConfiguration().getEnvironment().setInverseClassLoading(true);
299 }
300 SharedLibraryList[] slList = descriptor.getComponent().getSharedLibraries();
301 if (slList != null) {
302 for (int i = 0; i < slList.length; i++) {
303 Artifact sl = new Artifact("servicemix-libraries", slList[i].getName(), slList[i].getVersion(), "car");
304 environment.addDependency(sl, ImportType.CLASSES);
305 }
306 }
307 if (descriptor.getComponent().getComponentClassPath() != null) {
308 String[] pathElements = descriptor.getComponent().getComponentClassPath().getPathElements();
309 if (pathElements != null) {
310 for (int i = 0; i < pathElements.length; i++) {
311 context.getConfiguration().addToClassPath(new URI("install/").resolve(pathElements[i]).toString());
312 }
313 }
314 }
315 // Create the JBI deployment managed object
316 Properties props = new Properties();
317 props.put("jbiType", "JBIComponent");
318 props.put("name", descriptor.getComponent().getIdentification().getName());
319 AbstractName name = new AbstractName(environment.getConfigId(), props);
320 GBeanData gbeanData = new GBeanData(name, org.apache.servicemix.geronimo.Component.GBEAN_INFO);
321 gbeanData.setAttribute("name", descriptor.getComponent().getIdentification().getName());
322 gbeanData.setAttribute("description", descriptor.getComponent().getIdentification().getDescription());
323 gbeanData.setAttribute("type", descriptor.getComponent().getType());
324 gbeanData.setAttribute("className", descriptor.getComponent().getComponentClassName());
325 gbeanData.setReferencePattern("container", containerName);
326 context.addGBean(gbeanData);
327 }
328
329 protected void buildServiceAssembly(Descriptor descriptor, DeploymentContext context, JarFile module)
330 throws Exception {
331 Environment environment = context.getConfiguration().getEnvironment();
332 // Unzip the component
333 File targetDir = new File(context.getBaseDir(), "install");
334 targetDir.mkdirs();
335 unzip(context, module, new URI("install/"));
336 // Unzip SUs
337 ServiceUnit[] sus = descriptor.getServiceAssembly().getServiceUnits();
338 for (int i = 0; i < sus.length; i++) {
339 String name = sus[i].getIdentification().getName();
340 String zip = sus[i].getTarget().getArtifactsZip();
341 String comp = sus[i].getTarget().getComponentName();
342 URI installUri = new URI("sus/" + comp + "/" + name + "/");
343 unzip(context, new JarFile(new File(targetDir, zip)), installUri);
344 // Add component config as a dependency
345 Artifact sl = new Artifact("servicemix-components", comp, "0.0", "car");
346 environment.addDependency(sl, ImportType.ALL);
347 // Deploy the SU on the component
348 Component jbiServiceUnit = null;
349 try {
350 jbiServiceUnit = getAssociatedJbiServiceUnit(comp, sl);
351 } catch (GBeanNotFoundException e) {
352 throw new DeploymentException("Can not find the associated service unit for this service assembly. "
353 + "Check if it's deployed and started.", e);
354 }
355 ServiceUnitManager serviceUnitManager = jbiServiceUnit.getServiceUnitManager();
356 File installDir = new File(context.getBaseDir(), installUri.toString());
357 String deploy = serviceUnitManager.deploy(name, installDir.getAbsolutePath());
358 log.debug(deploy);
359 }
360 // Create the JBI deployment managed object
361 Properties props = new Properties();
362 props.put("jbiType", "JBIServiceAssembly");
363 props.put("name", descriptor.getServiceAssembly().getIdentification().getName());
364 AbstractName name = new AbstractName(environment.getConfigId(), props);
365 GBeanData gbeanData = new GBeanData(name, ServiceAssembly.GBEAN_INFO);
366 gbeanData.setAttribute("name", descriptor.getServiceAssembly().getIdentification().getName());
367 gbeanData.setReferencePattern("container", containerName);
368 for (int i = 0; i < sus.length; i++) {
369 String comp = sus[i].getTarget().getComponentName();
370 gbeanData.addDependency(getComponentName(comp));
371 }
372 context.addGBean(gbeanData);
373 }
374
375 /**
376 * Returns the JBI ServiceUnit with the given 'ArtifactName' and
377 * the given componentName.
378 *
379 * @param compName Name
380 * @param artifactName Name
381 * @return Component instance
382 * @throws GBeanNotFoundException if the ServiceUnit cannot be found
383 */
384 private Component getAssociatedJbiServiceUnit(String compName, Artifact artifactName) throws GBeanNotFoundException {
385 Properties props = new Properties();
386 props.put("jbiType", "JBIComponent");
387 props.put("name", compName);
388 org.apache.servicemix.geronimo.Component serviceUnit =
389 (org.apache.servicemix.geronimo.Component) kernel.getGBean(new AbstractName(artifactName, props));
390 Component jbiServiceUnit = serviceUnit.getComponent();
391 return jbiServiceUnit;
392 }
393
394 protected void buildSharedLibrary(Descriptor descriptor, DeploymentContext context, JarFile module)
395 throws Exception {
396 Environment environment = context.getConfiguration().getEnvironment();
397 // Unzip the SL
398 File targetDir = new File(context.getBaseDir(), "install");
399 targetDir.mkdirs();
400 unzip(context, module, new URI("install/"));
401 // Create workspace dir
402 File workDir = new File(context.getBaseDir(), "workspace");
403 workDir.mkdirs();
404 // Add classpath entries
405 if ("self-first".equals(descriptor.getSharedLibrary().getClassLoaderDelegation())) {
406 context.getConfiguration().getEnvironment().setInverseClassLoading(true);
407 }
408 if (descriptor.getSharedLibrary().getSharedLibraryClassPath() != null) {
409 String[] pathElements = descriptor.getSharedLibrary().getSharedLibraryClassPath().getPathElements();
410 if (pathElements != null) {
411 for (int i = 0; i < pathElements.length; i++) {
412 log.debug("Processing pathElements[" + i + "]: " + pathElements[i]);
413 // We can not add includes directly, so move the file and
414 // include it
415 File include = new File(targetDir, pathElements[i]);
416 File temp = new File(workDir, pathElements[i]);
417 if (!include.isFile()) {
418 throw new Exception("Classpath element '" + pathElements[i] + "' not found");
419 }
420 temp.getParentFile().mkdirs();
421 include.renameTo(temp);
422 context.addInclude(new URI("install/").resolve(pathElements[i]), temp);
423 temp.delete();
424 }
425 } else {
426 log.debug("SharedLibrary().getSharedLibraryClassPath().getPathElements() is null");
427 }
428 } else {
429 log.debug("SharedLibrary().getSharedLibraryClassPath() is null");
430 }
431 // Create the JBI deployment managed object
432 Properties props = new Properties();
433 props.put("jbiType", "JBISharedLibrary");
434 props.put("name", descriptor.getSharedLibrary().getIdentification().getName());
435 AbstractName name = new AbstractName(environment.getConfigId(), props);
436 GBeanData gbeanData = new GBeanData(name, SharedLibrary.GBEAN_INFO);
437 gbeanData.setAttribute("name", descriptor.getSharedLibrary().getIdentification().getName());
438 gbeanData.setAttribute("description", descriptor.getSharedLibrary().getIdentification().getDescription());
439 gbeanData.setReferencePattern("container", containerName);
440 context.addGBean(gbeanData);
441 }
442
443 protected void unzip(DeploymentContext context, JarFile module, URI targetUri) throws IOException {
444 Enumeration entries = module.entries();
445 while (entries.hasMoreElements()) {
446 ZipEntry entry = (ZipEntry) entries.nextElement();
447 URI target = targetUri.resolve(entry.getName());
448 context.addFile(target, module, entry);
449 }
450 }
451
452 protected AbstractNameQuery getComponentName(String name) throws MalformedObjectNameException {
453 URI uri = URI.create("servicemix-components/" + name + "//car?jbiType=JBIComponent");
454 return new AbstractNameQuery(uri);
455 }
456
457 }