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