com.jaxio.celerio.output.FolderOutputResult Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of celerio-engine Show documentation
Show all versions of celerio-engine Show documentation
Celerio Core Generation Engine
/*
* Copyright 2015 JAXIO http://www.jaxio.com
*
* 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 com.jaxio.celerio.output;
import com.jaxio.celerio.template.pack.Template;
import com.jaxio.celerio.template.pack.TemplatePack;
import lombok.extern.slf4j.Slf4j;
import java.io.*;
import java.util.HashMap;
import static com.google.common.collect.Maps.newHashMap;
import static com.jaxio.celerio.convention.WellKnownFolder.JAVA;
import static com.jaxio.celerio.convention.WellKnownFolder.JAVA_TEST;
import static com.jaxio.celerio.output.FileUtil.getPathRelativeToBase;
import static org.apache.commons.io.FilenameUtils.normalize;
import static org.apache.commons.io.IOUtils.*;
@Slf4j
public class FolderOutputResult implements OutputResult {
private FileTracker fileTracker;
private SourceFile userSource;
private SourceFile generatedSource;
private boolean sameDirectory;
private boolean isOpen = false;
private boolean fileMetaDataSavedOk;
private HashMap generatedFiles = newHashMap();
private SCMStatus scmStatus = new SCMStatus(null);
public FolderOutputResult(SourceFile userSource, SourceFile generatedSource) {
this(userSource, generatedSource, null);
}
public FolderOutputResult(SourceFile userSource, SourceFile generatedSource, FileTracker fileTracker) {
this.userSource = userSource;
this.generatedSource = generatedSource;
this.fileTracker = fileTracker;
this.sameDirectory = userSource.getDirectory().equals(generatedSource.getDirectory());
}
@Override
public void open() throws IOException {
if (isOpen) {
return;
}
fileMetaDataSavedOk = false;
// NOTE: if we don't do this, we end up with inconsistent state
// For example if the user Ctrl-C while Celerio generates the files,
// we loose the generation information if we do not catch the shutdown.
// As a result, during the next generation, Celerio thinks the files have
// been manually modified... So with this hook, hopefully we record
// on disk whatever was generated before the interruption.
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
if (isFileTrackingEnabled() && !fileMetaDataSavedOk) {
try {
close();
} catch (IOException ioe) {
// well, too bad...
log.error("Could not write to disk last generation trace: " + ioe.getMessage());
}
}
}
});
if (isFileTrackingEnabled()) {
// it is important to keep existing data to handle cases where user generates
// several times in a raw before cleaning... indeed, the second time he generates
// the files is not written to disk, as it is identical...
generatedFiles = fileTracker.loadFromProjectDir(new File(userSource.getDirectory()));
scmStatus = fileTracker.getSCMStatus(new File(userSource.getDirectory()));
}
isOpen = true;
}
synchronized public void close() throws IOException {
if (isOpen) {
isOpen = false;
saveFileMetaData();
}
}
private void saveFileMetaData() throws IOException {
if (isFileTrackingEnabled() && !fileMetaDataSavedOk) {
// remember what was generated by dumping it in a special file.
fileTracker.saveToProjectDir(generatedFiles, new File(userSource.getDirectory()));
fileMetaDataSavedOk = true;
}
}
@Override
public void addContent(byte[] contentBytes, String targetFilename, TemplatePack pack, Template template) throws IOException {
String fullPath = generatedSource.getFullPath(targetFilename);
addContentWithFullPath(contentBytes, fullPath, pack, template);
}
@Override
public void addCollisionContent(byte[] contentBytes, String targetFilename, TemplatePack pack, Template template) throws IOException {
String fullPath = userSource.getFullPath(targetFilename);
addContentWithFullPath(contentBytes, fullPath, pack, template);
}
private void addContentWithFullPath(byte[] contentBytes, String fullPath, TemplatePack pack, Template template) throws IOException {
open();
File targetFile = new File(fullPath);
// create folder if needed
File parent = targetFile.getParentFile();
if (parent != null) {
parent.mkdirs();
} // else child was for example "."
FileOutputStream targetFileOutput = new FileOutputStream(targetFile);
write(contentBytes, targetFileOutput);
closeQuietly(targetFileOutput);
// keep track of generated file so we can easily delete it
if (isFileTrackingEnabled()) {
String filePathRelativeToUserSrc = getPathRelativeToBase(targetFile, userSource.getDirectory());
generatedFiles.put(filePathRelativeToUserSrc, new FileMetaData(pack, template, filePathRelativeToUserSrc, targetFile));
}
}
@Override
public void addContent(InputStream contentStream, String targetFilename, TemplatePack pack, Template template) throws IOException {
addContent(toByteArray(new InputStreamReader(contentStream), "UTF-8"), targetFilename, pack, template);
}
@Override
public SourceFile getUserSource() {
return userSource;
}
@Override
public SourceFile getGeneratedSource() {
return generatedSource;
}
@Override
public boolean sameDirectory() {
return sameDirectory;
}
/**
* @param pathToFile relative path to the file to check. Relative to the folder containing the ".celerio/generated.xml"
*/
private boolean manualModificationSinceLastGeneration(String pathToFile) {
if (!isFileTrackingEnabled()) {
return true; // no risk
}
FileMetaData oldFmd = generatedFiles.get(pathToFile);
if (oldFmd == null) {
boolean res = generatedSource.fileExists(pathToFile); // no risk
if (res && log.isInfoEnabled()) {
log.info("FILE ALREADY PRESENT and not tracked: " + pathToFile);
}
return res;
}
if (!generatedSource.fileExists(pathToFile)) {
return false;
}
FileMetaData recentFmd = new FileMetaData(null, null, pathToFile, new File(generatedSource.getFullPath(pathToFile)));
// not equals means there is a chance that the user manually edited the file between 2 generations.
boolean areNotEqual = !oldFmd.equals(recentFmd);
if (areNotEqual && log.isInfoEnabled()) {
log.info("MANUAL MODIFICATION detected for: " + pathToFile);
}
return areNotEqual;
}
/**
* @return true if the file is located under one of these directories: src/main/java or src/test/java
*/
@Override
public boolean hasCollision(String pathToFile) {
if (scmStatus.isUnderSCM(pathToFile)) {
log.info("FILE UNDER SCM: {}", pathToFile);
return true;
}
if (sameDirectory()) {
return isImportantUserFile(pathToFile) || manualModificationSinceLastGeneration(pathToFile);
} else {
return manualModificationSinceLastGeneration(pathToFile);
}
}
@Override
public String getCollisionName(String targetFilename) {
return normalize("target" + File.separatorChar + "celerio-maven-plugin" + File.separatorChar + "collisions" + File.separatorChar + targetFilename);
}
/**
* @return true if the file is located under one of these directories: src/main/java or src/test/java
*/
private boolean isImportantUserFile(String outputFile) {
return userSource.fileExists(outputFile) && (isInJavaUserOnlyFolder(outputFile) || isImportantFile(outputFile));
}
boolean isInJavaUserOnlyFolder(String outputFile) {
String normalizedOutputFile = normalize(outputFile);
boolean isInJava = normalizedOutputFile.contains(JAVA.getFolder());
boolean isInJavaTest = normalizedOutputFile.contains(JAVA_TEST.getFolder());
return isInJava || isInJavaTest;
}
boolean isImportantFile(String outputFile) {
return outputFile.contains("pom.xml") || outputFile.contains("celerio-maven-plugin.xml") || outputFile.contains("celerio-template-packs.xml")
|| outputFile.contains("01-create.sql");
}
private boolean isFileTrackingEnabled() {
return fileTracker != null;
}
}