001    /*
002     * Copyright 2010 The Apache Software Foundation.
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     * 
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.vafer.jdeb.maven;
017    
018    import java.io.File;
019    import java.io.FileInputStream;
020    import java.text.ParseException;
021    import java.util.ArrayList;
022    import java.util.Collection;
023    import java.util.HashMap;
024    import java.util.Map;
025    
026    import org.apache.maven.plugin.MojoExecutionException;
027    import org.apache.maven.project.MavenProjectHelper;
028    import org.apache.tools.tar.TarEntry;
029    import org.vafer.jdeb.Console;
030    import org.vafer.jdeb.DataConsumer;
031    import org.vafer.jdeb.DataProducer;
032    import org.vafer.jdeb.PackagingException;
033    import org.vafer.jdeb.descriptors.AbstractDescriptor;
034    import org.vafer.jdeb.utils.MapVariableResolver;
035    import org.vafer.jdeb.utils.Utils;
036    import org.vafer.jdeb.utils.VariableResolver;
037    
038    /**
039     * Creates deb archive
040     * 
041     * @goal jdeb
042     */
043    public class DebMojo extends AbstractPluginMojo {
044    
045        /**
046         * @component
047         */
048        private MavenProjectHelper projectHelper;
049    
050        /**
051         * Defines the pattern of the name of final artifacts. Possible
052         * substitutions are [[artifactId]] [[version]] [[extension]] and
053         * [[groupId]].
054         * 
055         * @parameter expression="${namePattern}"
056         *            default-value="[[artifactId]]_[[version]].[[extension]]"
057         */
058        private String namePattern;
059    
060        /**
061         * Explicitly defines the final artifact name (without using the pattern)
062         * 
063         * @parameter expression="${deb}"
064         */
065        private File deb;
066        private String debName;
067    
068        /**
069         * Explicitly defines the path to the control directory. At least the
070         * control file is mandatory.
071         * 
072         * @parameter expression="${controlDir}"
073         */
074        private File controlDir;
075    
076        /**
077         * Explicitly define the file to read the changes from.
078         * 
079         * @parameter expression="${changesIn}"
080         */
081        private File changesIn = null;
082        private String changesName;
083    
084        /**
085         * Explicitly define the file where to write the changes to.
086         * 
087         * @parameter expression="${changesIn}"
088         */
089        private File changesOut = null;
090    
091    //  /**
092    //   * Explicitly define the file where to write the changes of the changes
093    //   * input to.
094    //   * 
095    //   * @parameter expression="${changesSave}"
096    //   */
097    //  private File changesSave = null;
098    
099    //  /**
100    //   * The keyring file. Usually some/path/secring.gpg
101    //   * 
102    //   * @parameter expression="${keyring}"
103    //   */
104    //  private File keyring = null;
105    
106    //  /**
107    //   * The hex key id to use for signing.
108    //   * 
109    //   * @parameter expression="${key}"
110    //   */
111    //  private String key = null;
112    
113    //  /**
114    //   * The passphrase for the key to sign the changes file.
115    //   * 
116    //   * @parameter expression="${passhrase}"
117    //   */
118    //  private String passphrase = null;
119    
120    //  /**
121    //   * The compression method used for the data file (none, gzip or bzip2)
122    //   * 
123    //   * @parameter expression="${compression}" default-value="gzip"
124    //   */
125    //  private String compression;
126    
127        /**
128         * The location where all package files will be installed. By default, all
129         * packages are installed in /opt (see the FHS here:
130         * http://www.pathname.com/
131         * fhs/pub/fhs-2.3.html#OPTADDONAPPLICATIONSOFTWAREPACKAGES)
132         * 
133         * @parameter expression="${installDir}"
134         *            default-value="/opt/${project.artifactId}"
135         */
136        private String installDir;
137        private String openReplaceToken = "[[";
138        private String closeReplaceToken = "]]";
139    
140        /**
141         * The type of attached artifact
142         *
143         * @parameter default-value="deb"
144         */
145        private String type;
146    
147        /**
148         * The classifier of attached artifact
149         *
150         * @parameter
151         */
152        private String classifier;
153    
154        /**
155         * "data" entries used to determine which files should be added to this deb.
156         * The "data" entries may specify a tarball (tar.gz, tar.bz2, tgz), a
157         * directory, or a normal file. An entry would look something like this in
158         * your pom.xml:
159         * 
160         * <pre>
161         *   <build>
162         *     <plugins>
163         *       <plugin>
164         *       <artifactId>jdeb</artifactId>
165         *       <groupId>org.vafer</groupId>
166         *       ...
167         *       <configuration>
168         *         ...
169         *         <dataSet>
170         *           <data>
171         *             <src>${project.basedir}/target/my_archive.tar.gz</src>
172         *             <include>...</include>
173         *             <exclude>...</exclude>
174         *             <mapper>
175         *               <type>perm</type>
176         *               <strip>1</strip>
177         *               <prefix>/somewhere/else</prefix>
178         *               <user>santbj</user>
179         *               <group>santbj</group>
180         *               <mode>600</mode>
181         *             </mapper>
182         *           </data>
183         *           <data>
184         *             <src>${project.build.directory}/data</src>
185         *             <include></include>
186         *             <exclude>**&#47;.svn</exclude>
187         *             <mapper>
188         *               <type>ls</type>
189         *               <src>mapping.txt</src>
190         *             </mapper>
191         *           </data>
192         *         <data>
193         *           <src>${project.basedir}/README.txt</src>
194         *         </data>
195         *         </dataSet>
196         *       </configuration>
197         *     </plugins>
198         *   </build>
199         * </pre>
200         * 
201         * @parameter expression="${dataSet}"
202         */
203        private Data[] dataSet;
204        private Collection dataProducers = new ArrayList();
205    
206        public void setData(Data[] pData) {
207            dataSet = pData;
208            dataProducers.clear();
209            if (pData != null) {
210                for (int i = 0; i < pData.length; i++) {
211                    dataProducers.add(pData[i]);
212                }
213            }
214        }
215    
216        public void setOpenReplaceToken(String openReplaceToken) {
217            this.openReplaceToken = openReplaceToken;
218            AbstractDescriptor.setOpenToken(openReplaceToken);
219        }
220    
221        public void setCloseReplaceToken(String closeReplaceToken) {
222            this.closeReplaceToken = closeReplaceToken;
223            AbstractDescriptor.setCloseToken(closeReplaceToken);
224        }
225    
226        protected VariableResolver initializeVariableResolver(Map variables) {
227            variables.put("name", getProject().getName());
228            variables.put("artifactId", getProject().getArtifactId());
229            variables.put("groupId", getProject().getGroupId());
230            variables.put("version", getProject().getVersion().replace('-', '+'));
231            variables.put("description", getProject().getDescription());
232            variables.put("extension", "deb");
233            return new MapVariableResolver(variables);
234        }
235    
236        protected File getDebFile() {
237            // if not specified try to the default
238            if (deb == null) {
239                deb = new File(buildDirectory, debName);
240            }
241            return deb;
242        }
243    
244        protected File getControlDir() {
245            // if not specified try to the default
246            if (controlDir == null) {
247                controlDir = new File(getProject().getBasedir(), "src/deb/control");
248                getLog().info(
249                        "Using default path to control directory " + controlDir);
250            }
251            return controlDir;
252        }
253    
254        protected File getControlFile() {
255            return new File(controlDir, "control");
256        }
257    
258        protected String getInstallDir() {
259            // if not specified try to the default
260            if (installDir == null) {
261                installDir = "/opt/" + getProject().getArtifactId();
262                getLog().info("Using default path to install directory " + installDir);
263            }
264            return installDir;
265        }
266    
267        protected File getChangesInFile() {
268            // if not specified try to the default
269            if (changesIn == null) {
270                final File f = new File(getProject().getBasedir(), "CHANGES.txt");
271                if (f.exists() && f.isFile() && f.canRead()) {
272                    changesIn = f;
273                }
274            }
275            return changesIn;
276        }
277    
278        protected File getChangesOutFile() {
279            // if not specified try to the default
280            if (changesOut == null) {
281                changesOut = new File(buildDirectory, changesName);
282            }
283            return changesOut;
284        }
285    
286        /**
287         * Main entry point
288         * 
289         * @throws MojoExecutionException on error
290         */
291        public void execute() throws MojoExecutionException {
292            Map variables = new HashMap();
293            final VariableResolver resolver = initializeVariableResolver(variables);
294            try {
295                // expand name pattern
296                debName = Utils.replaceVariables(resolver, namePattern, openReplaceToken, closeReplaceToken);
297                variables.put("extension", "changes");
298                changesName = Utils.replaceVariables(resolver, namePattern, openReplaceToken, closeReplaceToken);
299            } catch (ParseException e) {
300                throw new MojoExecutionException("Failed parsing artifact name pattern", e);
301            }
302    
303            deb = getDebFile();
304            changesIn = getChangesInFile();
305            changesOut = getChangesOutFile();
306            controlDir = getControlDir();
307    
308            setData(dataSet);
309    
310            // If there are no dataProducers, then we'll add a single producer that
311            // processes the
312            // maven artifact file (be it a jar, war, etc.)
313            if (dataProducers.isEmpty()) {
314                final File file = getProject().getArtifact().getFile();
315                dataProducers.add(new DataProducer() {
316                    public void produce(final DataConsumer receiver) {
317                        try {
318                            receiver.onEachFile(new FileInputStream(file),
319                                    new File(new File(getInstallDir()), file.getName()).getAbsolutePath(), "",
320                                    "root", 0, "root", 0,
321                                    TarEntry.DEFAULT_FILE_MODE, file.length());
322                        } catch (Exception e) {
323                            getLog().error(e);
324                        }
325                    }
326                });
327            }
328    
329            Console infoConsole = new Console() {
330                public void println(String s) {
331                    getLog().info(s);
332                }
333            };
334            try {
335                DebMaker debMaker = new DebMaker(infoConsole, deb, controlDir, dataProducers, resolver);
336                debMaker.makeDeb();
337                getLog().info("Attaching created debian archive " + deb);
338                projectHelper.attachArtifact(getProject(), type, classifier, deb);
339            } catch (PackagingException e) {
340                getLog().error("Failed to create debian package " + deb, e);
341                throw new MojoExecutionException("Failed to create debian package " + deb, e);
342            }
343        }
344    }