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