001    /**
002     * Copyright 2010-2012 The Kuali Foundation
003     *
004     * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
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.codehaus.mojo.license;
017    
018    import java.io.File;
019    import java.util.Arrays;
020    
021    import org.apache.commons.lang.StringUtils;
022    import org.apache.maven.execution.MavenSession;
023    import org.apache.maven.plugin.AbstractMojo;
024    import org.apache.maven.plugin.MojoExecutionException;
025    import org.apache.maven.plugin.MojoFailureException;
026    import org.apache.maven.project.MavenProject;
027    import org.codehaus.plexus.util.ReaderFactory;
028    
029    /**
030     * Abstract license mojo.
031     *
032     * @author tchemit <chemit@codelutin.com>
033     * @since 1.0
034     */
035    public abstract class AbstractLicenseMojo extends AbstractMojo {
036    
037        /**
038         * Current maven session. (used to launch certain mojo once by build).
039         *
040         * @parameter expression="${session}"
041         * @required
042         * @readonly
043         * @since 1.0
044         */
045        private MavenSession session;
046    
047        /**
048         * The reacted project.
049         *
050         * @parameter default-value="${project}"
051         * @required
052         * @since 1.0
053         */
054        private MavenProject project;
055    
056        /**
057         * Flag to activate verbose mode.
058         * <p/>
059         * <b>Note:</b> Verbose mode is always on if you starts a debug maven instance (says via {@code -X}).
060         *
061         * @parameter expression="${license.verbose}" default-value="${maven.verbose}"
062         * @since 1.0
063         */
064        private boolean verbose;
065    
066        /**
067         * Encoding used to read and writes files.
068         * <p/>
069         * <b>Note:</b> If nothing is filled here, we will use the system property {@code file.encoding}.
070         *
071         * @parameter expression="${license.encoding}" default-value="${project.build.sourceEncoding}"
072         * @since 1.0
073         */
074        private String encoding;
075    
076        public final String getEncoding() {
077            return encoding;
078        }
079    
080        public final void setEncoding(String encoding) {
081            this.encoding = encoding;
082        }
083    
084        /**
085         * Method to initialize the mojo before doing any concrete actions.
086         * <p/>
087         * <b>Note:</b> The method is invoked before the {@link #doAction()} method.
088         *
089         * @throws Exception
090         *             if any
091         */
092        protected abstract void init() throws Exception;
093    
094        /**
095         * Do plugin action.
096         * <p/>
097         * The method {@link #execute()} invoke this method only and only if :
098         * <ul>
099         * <li>{@link #checkPackaging()} returns {@code true}.</li>
100         * <li>method {@link #init()} returns {@code true}.</li>
101         * </ul>
102         *
103         * @throws Exception
104         *             if any
105         */
106        protected abstract void doAction() throws Exception;
107    
108        @Override
109        public final void execute() throws MojoExecutionException, MojoFailureException {
110            try {
111                if (getLog().isDebugEnabled()) {
112    
113                    // always be verbose in debug mode
114                    setVerbose(true);
115                }
116    
117                // check if project packaging is compatible with the mojo
118    
119                boolean canContinue = checkPackaging();
120                if (!canContinue) {
121                    getLog().debug("Skip for packaging '" + getProject().getPackaging() + "'");
122                    return;
123                }
124    
125                // init the mojo
126    
127                try {
128    
129                    checkEncoding();
130    
131                    init();
132    
133                } catch (MojoFailureException e) {
134                    throw e;
135                } catch (MojoExecutionException e) {
136                    throw e;
137                } catch (Exception e) {
138                    throw new MojoExecutionException("could not init goal " + getClass().getSimpleName() + " for reason : "
139                            + e.getMessage(), e);
140                }
141    
142                // check if mojo can be skipped
143    
144                canContinue = checkSkip();
145                if (!canContinue) {
146                    if (isVerbose()) {
147                        getLog().info("Goal will not be executed.");
148                    }
149                    return;
150                }
151    
152                // can really execute the mojo
153    
154                try {
155    
156                    doAction();
157    
158                } catch (MojoFailureException e) {
159                    throw e;
160                } catch (MojoExecutionException e) {
161                    throw e;
162                } catch (Exception e) {
163                    throw new MojoExecutionException("could not execute goal " + getClass().getSimpleName()
164                            + " for reason : " + e.getMessage(), e);
165                }
166            } finally {
167                afterExecute();
168            }
169        }
170    
171        /**
172         * A call back to execute after the {@link #execute()} is done
173         */
174        protected void afterExecute() {
175            // by default do nothing
176        }
177    
178        /**
179         * Check if the project packaging is acceptable for the mojo.
180         * <p/>
181         * By default, accept all packaging types.
182         * <p/>
183         * <b>Note:</b> This method is the first instruction to be executed in the {@link #execute()}.
184         * <p/>
185         * <b>Tip:</b> There is two method to simplify the packaging check :
186         * <p/>
187         * {@link #acceptPackaging(String...)}
188         * <p/>
189         * and
190         * <p/>
191         * {@link #rejectPackaging(String...)}
192         *
193         * @return {@code true} if can execute the goal for the packaging of the project, {@code false} otherwise.
194         */
195        protected boolean checkPackaging() {
196            // by default, accept every type of packaging
197            return true;
198        }
199    
200        /**
201         * Checks if the mojo execution should be skipped.
202         *
203         * @return {@code false} if the mojo should not be executed.
204         */
205        protected boolean checkSkip() {
206            // by default, never skip goal
207            return true;
208        }
209    
210        /**
211         * Accept the project's packaging between some given.
212         *
213         * @param packages
214         *            the accepted packaging
215         * @return {@code true} if the project's packaging is one of the given ones.
216         */
217        protected boolean acceptPackaging(String... packages) {
218            String projectPackaging = getProject().getPackaging();
219    
220            for (String p : packages) {
221                if (p.equals(projectPackaging)) {
222                    // accept packaging
223                    return true;
224                }
225            }
226            // reject packaging
227            return false;
228        }
229    
230        /**
231         * Accept the project's packaging if not in given one.
232         *
233         * @param packages
234         *            the rejecting packagings
235         * @return {@code true} if the project's packaging is not in the given ones.
236         */
237        protected boolean rejectPackaging(String... packages) {
238            String projectPackaging = getProject().getPackaging();
239    
240            for (String p : packages) {
241                if (p.equals(projectPackaging)) {
242                    // reject this packaging
243                    return false;
244                }
245            }
246            // accept packaging
247            return true;
248        }
249    
250        /**
251         * Method to be invoked in init phase to check sanity of {@link #getEncoding()}.
252         * <p/>
253         * If no encoding was filled, then use the default for system (via {@code file.encoding} environement property).
254         */
255        protected void checkEncoding() {
256    
257            if (isVerbose()) {
258                getLog().info("Will check encoding : " + getEncoding());
259            }
260            if (StringUtils.isEmpty(getEncoding())) {
261                getLog().warn(
262                        "File encoding has not been set, using platform encoding " + ReaderFactory.FILE_ENCODING
263                                + ", i.e. build is platform dependent!");
264                setEncoding(ReaderFactory.FILE_ENCODING);
265            }
266        }
267    
268        public final MavenProject getProject() {
269            return project;
270        }
271    
272        public final void setProject(MavenProject project) {
273            this.project = project;
274        }
275    
276        public final boolean isVerbose() {
277            return verbose;
278        }
279    
280        public final void setVerbose(boolean verbose) {
281            this.verbose = verbose;
282        }
283    
284        public final MavenSession getSession() {
285            return session;
286        }
287    
288        public final void setSession(MavenSession session) {
289            this.session = session;
290        }
291    
292        public final long getBuildTimestamp() {
293            return session.getStartTime().getTime();
294        }
295    
296        /**
297         * Add a new resource location to the maven project (in not already present).
298         *
299         * @param dir
300         *            the new resource location to add
301         * @param includes
302         *            files to include
303         */
304        protected void addResourceDir(File dir, String... includes) {
305            boolean added = MojoHelper.addResourceDir(dir, getProject(), includes);
306            if (added && isVerbose()) {
307                getLog().info("add resource " + dir + " with includes " + Arrays.toString(includes));
308            }
309        }
310    
311        /**
312         * @return {@code true} if project is not a pom, {@code false} otherwise.
313         */
314        protected boolean hasClassPath() {
315            return rejectPackaging("pom");
316        }
317    
318    }