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;
036import org.codehaus.plexus.util.StringUtils;
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 extends AbstractLogEnabled
044        implements MojoDescriptorExtractor {
045    @Override
046    public boolean isDeprecated() {
047        return true;
048    }
049
050    /** {@inheritDoc} */
051    @Override
052    public List<MojoDescriptor> execute(PluginToolsRequest request)
053            throws ExtractionException, InvalidPluginDescriptorException {
054        getLogger().debug("Running: " + getClass().getName());
055        String metadataExtension = getMetadataFileExtension(request);
056        String scriptExtension = getScriptFileExtension(request);
057
058        MavenProject project = request.getProject();
059
060        @SuppressWarnings("unchecked")
061        Map<String, Set<File>> scriptFilesKeyedByBasedir =
062                gatherFilesByBasedir(project.getBasedir(), project.getScriptSourceRoots(), scriptExtension, request);
063
064        List<MojoDescriptor> mojoDescriptors;
065        if (!StringUtils.isEmpty(metadataExtension)) {
066            @SuppressWarnings("unchecked")
067            Map<String, Set<File>> metadataFilesKeyedByBasedir = gatherFilesByBasedir(
068                    project.getBasedir(), project.getScriptSourceRoots(), metadataExtension, request);
069
070            mojoDescriptors = extractMojoDescriptorsFromMetadata(metadataFilesKeyedByBasedir, request);
071        } else {
072            mojoDescriptors = extractMojoDescriptors(scriptFilesKeyedByBasedir, request);
073        }
074
075        copyScriptsToOutputDirectory(
076                scriptFilesKeyedByBasedir, project.getBuild().getOutputDirectory(), request);
077
078        if (!mojoDescriptors.isEmpty()) {
079            getLogger().warn("Scripting support for mojos is deprecated and is planned to be removed in Maven 4.");
080            getLogger().warn("Found " + mojoDescriptors.size() + " scripted mojos.");
081        }
082
083        return mojoDescriptors;
084    }
085
086    /**
087     * @param scriptFilesKeyedByBasedir not null
088     * @param outputDirectory not null
089     * @param request the request
090     * @throws ExtractionException if any
091     */
092    protected void copyScriptsToOutputDirectory(
093            Map<String, Set<File>> scriptFilesKeyedByBasedir, String outputDirectory, PluginToolsRequest request)
094            throws ExtractionException {
095        File outputDir = new File(outputDirectory);
096
097        if (!outputDir.exists()) {
098            outputDir.mkdirs();
099        }
100
101        for (Map.Entry<String, Set<File>> entry : scriptFilesKeyedByBasedir.entrySet()) {
102            File sourceDir = new File(entry.getKey());
103
104            Set<File> scripts = entry.getValue();
105
106            for (File scriptFile : scripts) {
107                String relativePath =
108                        scriptFile.getPath().substring(sourceDir.getPath().length());
109
110                if (relativePath.charAt(0) == File.separatorChar) {
111                    relativePath = relativePath.substring(1);
112                }
113
114                File outputFile = new File(outputDir, relativePath).getAbsoluteFile();
115
116                if (!outputFile.getParentFile().exists()) {
117                    outputFile.getParentFile().mkdirs();
118                }
119
120                try {
121                    FileUtils.copyFile(scriptFile, outputFile);
122                } catch (IOException e) {
123                    throw new ExtractionException(
124                            "Cannot copy script file: " + scriptFile + " to output: " + outputFile, e);
125                }
126            }
127        }
128    }
129
130    /**
131     * @param basedir not null
132     * @param directories not null
133     * @param scriptFileExtension not null
134     * @param request the request
135     * @return map with subdirs paths as key
136     */
137    protected Map<String, Set<File>> gatherFilesByBasedir(
138            File basedir, List<String> directories, String scriptFileExtension, PluginToolsRequest request) {
139        Map<String, Set<File>> sourcesByBasedir = new TreeMap<>();
140
141        for (String resourceDir : directories) {
142            Set<File> sources = new HashSet<>();
143
144            getLogger()
145                    .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}