001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one
003     *  or more contributor license agreements.  See the NOTICE file
004     *  distributed with this work for additional information
005     *  regarding copyright ownership.  The ASF licenses this file
006     *  to you under the Apache License, Version 2.0 (the
007     *  "License"); you may not use this file except in compliance
008     *  with the License.  You may obtain a copy of the License at
009     *
010     *    http://www.apache.org/licenses/LICENSE-2.0
011     *
012     *  Unless required by applicable law or agreed to in writing,
013     *  software distributed under the License is distributed on an
014     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *  KIND, either express or implied.  See the License for the
016     *  specific language governing permissions and limitations
017     *  under the License.
018     */
019    
020    package org.apache.geronimo.mavenplugins.geronimo.server;
021    
022    import java.io.File;
023    import java.io.IOException;
024    import java.util.Map;
025    import java.util.HashMap;
026    import java.util.Enumeration;
027    import java.util.zip.ZipFile;
028    import java.util.zip.ZipEntry;
029    
030    import org.apache.maven.artifact.Artifact;
031    
032    import org.apache.maven.plugin.MojoExecutionException;
033    import org.apache.maven.plugin.MojoFailureException;
034    
035    import org.codehaus.plexus.util.FileUtils;
036    
037    import org.apache.tools.ant.taskdefs.Expand;
038    import org.apache.tools.ant.taskdefs.Chmod;
039    
040    import org.apache.geronimo.mavenplugins.geronimo.AssemblyConfig;
041    import org.apache.geronimo.mavenplugins.geronimo.reporting.ReportingMojoSupport;
042    
043    /**
044     * Common assembly install support.
045     *
046     * @version $Rev: 547776 $ $Date: 2007-06-15 16:20:31 -0400 (Fri, 15 Jun 2007) $
047     */
048    public abstract class InstallerMojoSupport
049        extends ReportingMojoSupport
050    {
051        /**
052         * Enable forced install refresh.
053         *
054         * @parameter expression="${refresh}" default-value="false"
055         */
056        protected boolean refresh = false;
057    
058        /**
059         * List of assembly artifact configurations.  Artifacts need to point to ZIP archives.
060         *
061         * @parameter
062         */
063        protected AssemblyConfig[] assemblies = null;
064    
065        /**
066         * Identifer of the assembly configuration to use.
067         *
068         * @parameter expression="${assemblyId}"
069         */
070        protected String assemblyId = null;
071    
072        /**
073         * The default assemblyId to use when no assemblyId configured.
074         *
075         * @parameter
076         */
077        protected String defaultAssemblyId = null;
078    
079        /**
080         * A file which points to a specific assembly ZIP archive.
081         * If this parameter is set, then it will be used instead of from the
082         * assemblies configuration.
083         *
084         * @parameter expression="${assemblyArchive}"
085         */
086        protected File assemblyArchive = null;
087    
088        /**
089         * Directory to install the assembly into.
090         *
091         * @parameter expression="${installDirectory}" default-value="${project.build.directory}"
092         * @required
093         */
094        protected File installDirectory = null;
095    
096        /**
097         * The directory where the assembly has been installed to.
098         *
099         * Normally this value is detected,
100         * but if it is set, then it is assumed to be the location where a pre-installed assembly exists
101         * and no installation will be done.
102         *
103         * @parameter expression="${geronimoHome}"
104         */
105        protected File geronimoHome;
106        
107        protected static enum InstallType {
108            FROM_ARTIFACT,
109            FROM_FILE,
110            ALREADY_EXISTS
111        }
112        
113        protected InstallType installType;
114    
115        private File discoverGeronimoHome(final File archive) throws IOException, MojoExecutionException {
116            log.debug("Attempting to discover geronimoHome...");
117    
118            File dir = null;
119    
120            try {
121                ZipFile zipFile = new ZipFile(archive);
122                
123                Enumeration n = zipFile.entries();
124                while (n.hasMoreElements()) {
125                    ZipEntry entry = (ZipEntry)n.nextElement();
126                    if (entry.getName().endsWith("bin/server.jar")) {
127                        File file = new File(installDirectory, entry.getName());
128                        dir = file.getParentFile().getParentFile();
129                        break;
130                    }
131                }
132    
133                zipFile.close();
134            }
135            catch (IOException e) {
136                throw new MojoExecutionException("Failed to determine geronimoHome while scanning archive for 'bin/server.jar'", e);
137            }
138    
139            if (dir == null) {
140                throw new MojoExecutionException("Archive does not contain a Geronimo assembly: " + archive);
141            }
142    
143            return dir.getCanonicalFile();
144        }
145    
146        protected void init() throws MojoExecutionException, MojoFailureException {
147            super.init();
148            
149            try {
150                // First check if geronimoHome is set, if it is, then we can skip this
151                if (geronimoHome != null) {
152                    geronimoHome = geronimoHome.getCanonicalFile();
153                    
154                    // Quick sanity check
155                    File file = new File(geronimoHome, "bin/server.jar");
156                    if (!file.exists()) {
157                        throw new MojoExecutionException("When geronimoHome is set, it must point to a directory that contains 'bin/server.jar'");
158                    }
159                    log.info("Using pre-installed assembly: " + geronimoHome);
160    
161                    installType = InstallType.ALREADY_EXISTS;
162                }
163                else {
164                    if (assemblyArchive != null) {
165                        assemblyArchive = assemblyArchive.getCanonicalFile();
166                        
167                        log.info("Using non-artifact based assembly archive: " + assemblyArchive);
168    
169                        installType = InstallType.FROM_FILE;
170                    }
171                    else {
172                        Artifact artifact = getAssemblyArtifact();
173    
174                        if (!"zip".equals(artifact.getType())) {
175                            throw new MojoExecutionException("Assembly file does not look like a ZIP archive");
176                        }
177    
178                        log.info("Using assembly artifact: " + artifact);
179    
180                        assemblyArchive = artifact.getFile();
181    
182                        installType = InstallType.FROM_ARTIFACT;
183                    }
184    
185                    geronimoHome = discoverGeronimoHome(assemblyArchive);
186                    log.info("Using geronimoHome: " + geronimoHome);
187                }
188            }
189            catch (java.io.IOException e) {
190                throw new MojoExecutionException(e.getMessage(), e);
191            }
192        }
193    
194        /**
195         * Selects the assembly artifact tp be used for installation.
196         *
197         * @return The assembly artifact selected to be installed.
198         *
199         * @throws MojoExecutionException   Failed to select assembly artifact
200         */
201        protected Artifact getAssemblyArtifact() throws MojoExecutionException {
202            AssemblyConfig config;
203    
204            if (assemblies == null || assemblies.length == 0) {
205                throw new MojoExecutionException("At least one assembly configuration must be specified");
206            }
207            else if (assemblies.length > 1 && assemblyId == null && defaultAssemblyId == null) {
208                throw new MojoExecutionException("Must specify assemblyId (or defaultAssemblyId) when more than on assembly configuration is given");
209            }
210            else if (assemblies.length == 1) {
211                config = assemblies[0];
212            }
213            else {
214                if (assemblyId == null) {
215                    assemblyId = defaultAssemblyId;
216                }
217    
218                log.debug("Searching for assembly config for id: " + assemblyId);
219    
220                // Make sure there are no duplicate ids
221                Map idMap = new HashMap();
222    
223                for (int i=0; i < assemblies.length; i++) {
224                    String id = assemblies[i].getId();
225    
226                    if (id == null) {
227                        throw new MojoExecutionException("Missing id for assembly configuration: " + assemblies[i]);
228                    }
229    
230                    if (idMap.containsKey(id)) {
231                        throw new MojoExecutionException("Duplicate assembly id: " + id);
232                    }
233    
234                    idMap.put(id, assemblies[i]);
235                }
236    
237                config = (AssemblyConfig) idMap.get(assemblyId);
238                if (config == null) {
239                    throw new MojoExecutionException("Missing assembly configuration for id: " + assemblyId);
240                }
241            }
242    
243            log.info("Using assembly configuration: " + config.getId());
244            Artifact artifact = getArtifact(config);
245    
246            if (artifact.getFile() == null) {
247                throw new MojoExecutionException("Assembly artifact does not have an attached file: " + artifact);
248            }
249    
250            return artifact;
251        }
252    
253        /**
254         * Performs assembly installation unless the install type is pre-existing.
255         *
256         * @throws Exception
257         */
258        protected void installAssembly() throws Exception {
259            if (installType == InstallType.ALREADY_EXISTS) {
260                log.info("Installation type is pre-existing; skipping installation");
261                return;
262            }
263    
264            // Check if there is a newer archive or missing marker to trigger assembly install
265            File installMarker = new File(geronimoHome, ".installed");
266    
267            if (!refresh) {
268                if (!installMarker.exists()) {
269                    refresh = true;
270                }
271                else if (assemblyArchive.lastModified() > installMarker.lastModified()) {
272                    log.debug("Detected new assembly archive");
273                    refresh = true;
274                }
275            }
276            else {
277                log.debug("User requested installation refresh");
278            }
279    
280            if (refresh) {
281                if (geronimoHome.exists()) {
282                    log.info("Uninstalling: " + geronimoHome);
283                    FileUtils.forceDelete(geronimoHome);
284                }
285            }
286    
287            // Install the assembly
288            if (!installMarker.exists()) {
289                log.info("Installing assembly...");
290    
291                FileUtils.forceMkdir(geronimoHome);
292                
293                Expand unzip = (Expand)createTask("unzip");
294                unzip.setSrc(assemblyArchive);
295                unzip.setDest(installDirectory.getCanonicalFile());
296                unzip.execute();
297    
298                // Make scripts executable, since Java unzip ignores perms
299                Chmod chmod = (Chmod)createTask("chmod");
300                chmod.setPerm("ugo+rx");
301                chmod.setDir(geronimoHome);
302                chmod.setIncludes("bin/*.sh");
303                chmod.execute();
304    
305                installMarker.createNewFile();
306            }
307            else {
308                log.info("Re-using previously installed assembly");
309            }
310        }
311    }