/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package at.nonblocking.maven.nonsnapshot.impl;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import at.nonblocking.maven.nonsnapshot.model.UpdatedUpstreamMavenArtifact;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.InputLocation;
import org.apache.maven.model.InputLocationTracker;
import org.apache.maven.model.InputSource;
import org.apache.maven.model.Model;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.Profile;
import org.apache.maven.model.io.xpp3.MavenXpp3ReaderEx;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.ReaderFactory;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import at.nonblocking.maven.nonsnapshot.MavenPomHandler;
import at.nonblocking.maven.nonsnapshot.exception.NonSnapshotPluginException;
import at.nonblocking.maven.nonsnapshot.model.MavenArtifact;
import at.nonblocking.maven.nonsnapshot.model.MavenModule;
import at.nonblocking.maven.nonsnapshot.model.MavenModuleDependency;
/**
* Default implementation of {@link MavenPomHandler}
*
* @author Juergen Kofler
*/
@Component(role = MavenPomHandler.class, hint = "default")
public class MavenPomHandlerDefaultImpl implements MavenPomHandler {
private static final Logger LOG = LoggerFactory.getLogger(MavenPomHandlerDefaultImpl.class);
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
@Override
public MavenModule readArtifact(File pomFile) {
LOG.debug("Loading POM file: {}", pomFile.getAbsolutePath());
InputSource is = new InputSource();
MavenXpp3ReaderEx reader = new MavenXpp3ReaderEx();
try {
Model model = reader.read(ReaderFactory.newXmlReader(pomFile), false, is);
model.setPomFile(pomFile);
return readArtifact(model);
} catch (IOException | XmlPullParserException e) {
throw new NonSnapshotPluginException("Failed to load POM: " + pomFile.getAbsolutePath(), e);
}
}
@Override
public MavenModule readArtifact(Model model) {
File pomFile = model.getPomFile();
String groupId = model.getGroupId();
if (groupId == null) {
if (model.getParent() != null) {
groupId = model.getParent().getGroupId();
} else {
throw new NonSnapshotPluginException("Invalid POM file: groupId is not set and no parent either: " + pomFile.getAbsolutePath());
}
}
boolean insertVersionTag = false;
String version = model.getVersion();
if (version == null) {
if (model.getParent() != null) {
version = model.getParent().getVersion();
insertVersionTag = true;
} else {
throw new NonSnapshotPluginException("Invalid POM file: Version is not set and no parent either: " + pomFile.getAbsolutePath());
}
}
MavenModule mavenModule = new MavenModule(pomFile, groupId, model.getArtifactId(), version);
mavenModule.setInsertVersionTag(insertVersionTag);
mavenModule.setVersionLocation(getVersionLocation(model));
// Parent
if (model.getParent() != null) {
mavenModule.setParent(new MavenArtifact(model.getParent().getGroupId(),
model.getParent().getArtifactId(), model.getParent().getVersion()));
mavenModule.setParentVersionLocation(getVersionLocation(model.getParent()));
}
// Dependencies
for (Dependency dependency : model.getDependencies()) {
mavenModule.getDependencies().add(new MavenModuleDependency(
getVersionLocation(dependency),
new MavenArtifact(dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion())));
}
// Plugins
if (model.getBuild() != null) {
for (Plugin plugin : model.getBuild().getPlugins()) {
mavenModule.getDependencies().add(new MavenModuleDependency(
getVersionLocation(plugin),
new MavenArtifact(plugin.getGroupId(), plugin.getArtifactId(), plugin.getVersion())));
for (Dependency dependency : plugin.getDependencies()) {
mavenModule.getDependencies().add(new MavenModuleDependency(
getVersionLocation(dependency),
new MavenArtifact(dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion())));
}
}
}
// Profile Dependencies
for (Profile profile : model.getProfiles()) {
for (Dependency dependency : profile.getDependencies()) {
mavenModule.getDependencies().add(new MavenModuleDependency(
getVersionLocation(dependency),
new MavenArtifact(dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion())));
}
}
// Profile Plugin with Dependecies
for (Profile profile : model.getProfiles()) {
if (profile.getBuild() != null) {
for (Plugin plugin : profile.getBuild().getPlugins()) {
mavenModule.getDependencies().add(new MavenModuleDependency(
getVersionLocation(plugin),
new MavenArtifact(plugin.getGroupId(), plugin.getArtifactId(), plugin.getVersion())));
for (Dependency dependency : plugin.getDependencies()) {
mavenModule.getDependencies().add(new MavenModuleDependency(
getVersionLocation(dependency),
new MavenArtifact(dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion())));
}
}
}
}
return mavenModule;
}
private int getVersionLocation(InputLocationTracker tracker) {
InputLocation location = tracker.getLocation("version");
if (location == null) {
location = tracker.getLocation("artifactId");
}
return location.getLineNumber();
}
@Override
public void updateArtifact(MavenModule mavenModule) {
if (!mavenModule.isDirty()) {
return;
}
List<PomUpdateCommand> commands = new ArrayList<>();
addUpdateCommand(mavenModule, mavenModule.getVersionLocation(), false, commands);
if (mavenModule.getParent() != null) {
if (mavenModule.getParent() instanceof MavenModule) {
addUpdateCommand((MavenModule) mavenModule.getParent(), mavenModule.getParentVersionLocation(), true, commands);
} else if (mavenModule.getParent() instanceof UpdatedUpstreamMavenArtifact) {
addUpdateCommand((UpdatedUpstreamMavenArtifact) mavenModule.getParent(), mavenModule.getParentVersionLocation(), commands);
}
}
for (MavenModuleDependency dependency : mavenModule.getDependencies()) {
if (dependency.getArtifact() instanceof MavenModule) {
addUpdateCommand((MavenModule) dependency.getArtifact(), dependency.getVersionLocation(), true, commands);
} else if (dependency.getArtifact() instanceof UpdatedUpstreamMavenArtifact) {
addUpdateCommand((UpdatedUpstreamMavenArtifact) dependency.getArtifact(), dependency.getVersionLocation(), commands);
}
}
executeUpdateCommands(commands, mavenModule.getPomFile());
}
private void addUpdateCommand(MavenModule mavenModule, Integer lineNumber, boolean dependency, List<PomUpdateCommand> commands) {
if (!mavenModule.isDirty()) {
return;
}
if (mavenModule.getNewVersion() == null) {
LOG.warn("No new version set for module {}:{}. Cannot update version!", mavenModule.getGroupId(), mavenModule.getArtifactId());
return;
}
if (!dependency && mavenModule.isInsertVersionTag()) {
commands.add(new PomUpdateCommand(lineNumber, UPDATE_COMMAND_TYPE.INSERT, "<version>" + mavenModule.getNewVersion() + "</version>", null));
} else {
commands.add(new PomUpdateCommand(lineNumber, UPDATE_COMMAND_TYPE.REPLACE, "<version>.*?</version>", "<version>" + mavenModule.getNewVersion() + "</version>"));
}
}
private void addUpdateCommand(UpdatedUpstreamMavenArtifact updatedUpstreamMavenArtifact, Integer lineNumber, List<PomUpdateCommand> commands) {
commands.add(new PomUpdateCommand(lineNumber, UPDATE_COMMAND_TYPE.REPLACE, "<version>.*?</version>", "<version>" + updatedUpstreamMavenArtifact.getNewVersion() + "</version>"));
}
private void executeUpdateCommands(List<PomUpdateCommand> commands, File pomFile) {
Map<Integer, PomUpdateCommand> commandMap = new HashMap<>();
for (PomUpdateCommand command : commands) {
commandMap.put(command.lineNumber, command);
}
try {
LineNumberReader reader = new LineNumberReader(new FileReader(pomFile));
File tempTarget = File.createTempFile("pom", ".xml");
PrintWriter writer = new PrintWriter(tempTarget);
LOG.debug("Writing temporary POM file to: {}", tempTarget.getAbsoluteFile());
String line;
while ((line = reader.readLine()) != null) {
PomUpdateCommand command = commandMap.get(reader.getLineNumber());
if (command != null) {
line = executeCommand(line, command);
}
writer.write(line + LINE_SEPARATOR);
}
reader.close();
writer.close();
LOG.debug("Copy temporary POM file to: {}", pomFile.getAbsoluteFile());
IOUtil.copy(new FileReader(tempTarget), new FileOutputStream(pomFile));
tempTarget.delete();
} catch (IOException e) {
throw new NonSnapshotPluginException("Failed to updated POM file: " + pomFile.getAbsolutePath(), e);
}
}
private String executeCommand(String line, PomUpdateCommand command) {
switch (command.commandType) {
case REPLACE:
LOG.debug("Replacing '{}' with '{}' in line number: {}", new Object[]{command.text1, command.text2, command.lineNumber});
return line.replaceAll(command.text1, command.text2);
case INSERT:
LOG.debug("Inserting '{}' in line number: {}", new Object[]{command.text1, command.lineNumber});
return line + LINE_SEPARATOR + command.text1;
}
return line;
}
private enum UPDATE_COMMAND_TYPE {
INSERT, REPLACE
}
private static class PomUpdateCommand {
int lineNumber;
UPDATE_COMMAND_TYPE commandType;
String text1;
String text2;
public PomUpdateCommand(int lineNumber, UPDATE_COMMAND_TYPE commandType, String text1, String text2) {
this.lineNumber = lineNumber;
this.commandType = commandType;
this.text1 = text1;
this.text2 = text2;
}
}
}