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    }