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