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    }