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 */
019package org.apache.maven.tools.plugin.extractor;
020
021import java.io.File;
022import java.io.IOException;
023import java.util.HashSet;
024import java.util.List;
025import java.util.Map;
026import java.util.Set;
027import java.util.TreeMap;
028
029import org.apache.maven.plugin.descriptor.InvalidPluginDescriptorException;
030import org.apache.maven.plugin.descriptor.MojoDescriptor;
031import org.apache.maven.project.MavenProject;
032import org.apache.maven.tools.plugin.PluginToolsRequest;
033import org.codehaus.plexus.logging.AbstractLogEnabled;
034import org.codehaus.plexus.util.DirectoryScanner;
035import org.codehaus.plexus.util.FileUtils;
036
037/**
038 * @deprecated Scripting support for Mojos is deprecated and is planned to be removed in Maven 4.0
039 * @author jdcasey
040 */
041@Deprecated
042public abstract class AbstractScriptedMojoDescriptorExtractor extends AbstractLogEnabled
043        implements MojoDescriptorExtractor {
044    @Override
045    public boolean isDeprecated() {
046        return true;
047    }
048
049    /** {@inheritDoc} */
050    @Override
051    public List<MojoDescriptor> execute(PluginToolsRequest request)
052            throws ExtractionException, InvalidPluginDescriptorException {
053        getLogger().debug("Running: " + getClass().getName());
054        String metadataExtension = getMetadataFileExtension(request);
055        String scriptExtension = getScriptFileExtension(request);
056
057        MavenProject project = request.getProject();
058
059        @SuppressWarnings("unchecked")
060        Map<String, Set<File>> scriptFilesKeyedByBasedir =
061                gatherFilesByBasedir(project.getBasedir(), project.getScriptSourceRoots(), scriptExtension, request);
062
063        List<MojoDescriptor> mojoDescriptors;
064        if (!(metadataExtension == null || metadataExtension.isEmpty())) {
065            @SuppressWarnings("unchecked")
066            Map<String, Set<File>> metadataFilesKeyedByBasedir = gatherFilesByBasedir(
067                    project.getBasedir(), project.getScriptSourceRoots(), metadataExtension, request);
068
069            mojoDescriptors = extractMojoDescriptorsFromMetadata(metadataFilesKeyedByBasedir, request);
070        } else {
071            mojoDescriptors = extractMojoDescriptors(scriptFilesKeyedByBasedir, request);
072        }
073
074        copyScriptsToOutputDirectory(
075                scriptFilesKeyedByBasedir, project.getBuild().getOutputDirectory(), request);
076
077        if (!mojoDescriptors.isEmpty()) {
078            getLogger().warn("Scripting support for mojos is deprecated and is planned to be removed in Maven 4.");
079            getLogger().warn("Found " + mojoDescriptors.size() + " scripted mojos.");
080        }
081
082        return mojoDescriptors;
083    }
084
085    /**
086     * @param scriptFilesKeyedByBasedir not null
087     * @param outputDirectory not null
088     * @param request the request
089     * @throws ExtractionException if any
090     */
091    protected void copyScriptsToOutputDirectory(
092            Map<String, Set<File>> scriptFilesKeyedByBasedir, String outputDirectory, PluginToolsRequest request)
093            throws ExtractionException {
094        File outputDir = new File(outputDirectory);
095
096        if (!outputDir.exists()) {
097            outputDir.mkdirs();
098        }
099
100        for (Map.Entry<String, Set<File>> entry : scriptFilesKeyedByBasedir.entrySet()) {
101            File sourceDir = new File(entry.getKey());
102
103            Set<File> scripts = entry.getValue();
104
105            for (File scriptFile : scripts) {
106                String relativePath =
107                        scriptFile.getPath().substring(sourceDir.getPath().length());
108
109                if (relativePath.charAt(0) == File.separatorChar) {
110                    relativePath = relativePath.substring(1);
111                }
112
113                File outputFile = new File(outputDir, relativePath).getAbsoluteFile();
114
115                if (!outputFile.getParentFile().exists()) {
116                    outputFile.getParentFile().mkdirs();
117                }
118
119                try {
120                    FileUtils.copyFile(scriptFile, outputFile);
121                } catch (IOException e) {
122                    throw new ExtractionException(
123                            "Cannot copy script file: " + scriptFile + " to output: " + outputFile, e);
124                }
125            }
126        }
127    }
128
129    /**
130     * @param basedir not null
131     * @param directories not null
132     * @param scriptFileExtension not null
133     * @param request the request
134     * @return map with subdirs paths as key
135     */
136    protected Map<String, Set<File>> gatherFilesByBasedir(
137            File basedir, List<String> directories, String scriptFileExtension, PluginToolsRequest request) {
138        Map<String, Set<File>> sourcesByBasedir = new TreeMap<>();
139
140        for (String resourceDir : directories) {
141            Set<File> sources = new HashSet<>();
142
143            getLogger()
144                    .debug("Scanning script dir: " + resourceDir + " with extractor: "
145                            + getClass().getName());
146            File dir = new File(resourceDir);
147            if (!dir.isAbsolute()) {
148                dir = new File(basedir, resourceDir).getAbsoluteFile();
149            }
150
151            resourceDir = dir.getPath();
152
153            if (dir.exists()) {
154                DirectoryScanner scanner = new DirectoryScanner();
155
156                scanner.setBasedir(dir);
157                scanner.addDefaultExcludes();
158                scanner.setIncludes(new String[] {"**/*" + scriptFileExtension});
159                scanner.scan();
160
161                String[] relativePaths = scanner.getIncludedFiles();
162
163                for (String relativePath : relativePaths) {
164                    File scriptFile = new File(dir, relativePath).getAbsoluteFile();
165
166                    if (scriptFile.isFile() && relativePath.endsWith(scriptFileExtension)) {
167                        sources.add(scriptFile);
168                    }
169                }
170
171                sourcesByBasedir.put(resourceDir, sources);
172            }
173        }
174
175        return sourcesByBasedir;
176    }
177
178    /**
179     * Should be implemented in the sub classes.
180     *
181     * @param metadataFilesByBasedir could be null
182     * @param request The plugin request, never <code>null</code>.
183     * @return always null
184     * @throws ExtractionException if any
185     * @throws InvalidPluginDescriptorException if any
186     */
187    protected List<MojoDescriptor> extractMojoDescriptorsFromMetadata(
188            Map<String, Set<File>> metadataFilesByBasedir, PluginToolsRequest request)
189            throws ExtractionException, InvalidPluginDescriptorException {
190        return null;
191    }
192
193    /**
194     * Should be implemented in the sub classes.
195     * @param request the request
196     * @return always null
197     */
198    protected String getMetadataFileExtension(PluginToolsRequest request) {
199        return null;
200    }
201
202    /**
203     * Should be implemented in the sub classes.
204     *
205     * @param scriptFilesKeyedByBasedir could be null
206     * @param request The plugin request, never <code>null</code>.
207     * @return always null
208     * @throws ExtractionException if any
209     * @throws InvalidPluginDescriptorException if any
210     */
211    protected List<MojoDescriptor> extractMojoDescriptors(
212            Map<String, Set<File>> scriptFilesKeyedByBasedir, PluginToolsRequest request)
213            throws ExtractionException, InvalidPluginDescriptorException {
214        return null;
215    }
216
217    /**
218     * @param request the request
219     * @return the file extension like <code>.bsh</code> for BeanShell.
220     */
221    protected abstract String getScriptFileExtension(PluginToolsRequest request);
222}