Skip to content

Commit

Permalink
Improve the template and the pipeline error handling (#26)
Browse files Browse the repository at this point in the history
Signed-off-by: Clement Escoffier <[email protected]>
  • Loading branch information
cescoffier committed Jun 2, 2014
1 parent b4d6d07 commit b53ca3c
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 30 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* #%L
* Wisdom-Framework
* %%
* Copyright (C) 2013 - 2014 Wisdom Framework
* %%
* 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.
* #L%
*/
package org.wisdom.maven;

import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.project.MavenProject;

import java.io.File;

/**
* A specialization of the {@link org.wisdom.maven.Watcher} interface for instance being used in Maven.
*/
public interface MavenWatcher extends Watcher {

MavenSession session();

MavenProject project();
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,40 @@
package org.wisdom.maven.mojos;

import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.project.MavenProject;
import org.wisdom.maven.MavenWatcher;
import org.wisdom.maven.Watcher;
import org.wisdom.maven.pipeline.Watchers;

/**
* Common part.
*/
public abstract class AbstractWisdomWatcherMojo extends AbstractWisdomMojo implements Watcher {
public abstract class AbstractWisdomWatcherMojo extends AbstractWisdomMojo implements MavenWatcher {

/**
* Sets the Maven Session and registers the current mojo to the watcher list (stored in the session).
* @param session the maven session
*/
public void setSession(MavenSession session) {
this.session = session;
Watchers.add(session, this);
}

/**
* Removes the current mojo from the watcher list.
*/
public void removeFromWatching() {
Watchers.remove(session, this);
}

@Override
public MavenSession session() {
return session;
}

@Override
public MavenProject project() {
return project;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,8 @@ public Pipeline(Mojo mojo, File baseDir, List<? extends Watcher> list) {
for (Object o : list) {
watchers.add(new WatcherDelegate(o));
}
File pipelineDirectory = new File(baseDir, "target/pipeline");
mojo.getLog().debug("Creating the target/pipeline directory : " + pipelineDirectory.mkdirs());
error = new File(pipelineDirectory, "error.json");
error = new File(baseDir, "target/pipeline");
mojo.getLog().debug("Creating the target/pipeline directory : " + error.mkdirs());
}

/**
Expand Down Expand Up @@ -117,7 +116,6 @@ public Pipeline watch() {
* @param file the created file
*/
public void onFileCreate(File file) {
cleanupErrorFile();
mojo.getLog().info(EMPTY_STRING);
mojo.getLog().info("The watcher has detected a new file: " + file.getAbsolutePath());
mojo.getLog().info(EMPTY_STRING);
Expand All @@ -126,12 +124,13 @@ public void onFileCreate(File file) {
// This flag will be set to false if the processing must be interrupted.
boolean continueProcessing;
try {
cleanupErrorFile(watcher);
continueProcessing = watcher.fileCreated(file);
} catch (WatchingException e) { //NOSONAR
mojo.getLog().debug(watcher + " has thrown an exception while handling the " + file.getName() + EMPTY_STRING +
" creation", e);
mojo.getLog().error(String.format(WATCHING_EXCEPTION_MESSAGE, e.getMessage()));
createErrorFile(e);
createErrorFile(watcher, e);
continueProcessing = false;
}
if (!continueProcessing) {
Expand All @@ -147,14 +146,20 @@ public void onFileCreate(File file) {
* Creates the error file storing the information from the given exception in JSON. This file is consumed by the
* Wisdom server to generate an error page reporting the watching exception.
*
* @param e the exception
* @param watcher the watcher having thrown the exception
* @param e the exception
*/
@SuppressWarnings("unchecked")
private void createErrorFile(WatchingException e) {
private void createErrorFile(Watcher watcher, WatchingException e) {
mojo.getLog().debug("Creating error file for '" + e.getMessage() + "' happening at " + e.getLine() + ":" + e
.getCharacter() + " of " + e.getFile());
.getCharacter() + " of " + e.getFile() + ", created by watcher : " + watcher);
JSONObject obj = new JSONObject();
obj.put("message", e.getMessage());
if (watcher instanceof WatcherDelegate) {
obj.put("watcher", ((WatcherDelegate) watcher).getDelegate().getClass().getName());
} else {
obj.put("watcher", watcher.getClass().getName());
}
if (e.getFile() != null) {
obj.put("file", e.getFile().getAbsolutePath());
}
Expand All @@ -168,17 +173,28 @@ private void createErrorFile(WatchingException e) {
obj.put("cause", e.getCause().getMessage());
}
try {
FileUtils.writeStringToFile(error, obj.toJSONString(), false);
FileUtils.writeStringToFile(getErrorFileForWatcher(watcher), obj.toJSONString(), false);
} catch (IOException e1) {
mojo.getLog().error("Cannot write the error file", e1);
}
}

/**
* Method called on each event before the processing, deleting the error file is this file exists.
*
* @param watcher the watcher
*/
private void cleanupErrorFile() {
FileUtils.deleteQuietly(error);
private void cleanupErrorFile(Watcher watcher) {
File file = getErrorFileForWatcher(watcher);
FileUtils.deleteQuietly(file);
}

private File getErrorFileForWatcher(Watcher watcher) {
if (watcher instanceof WatcherDelegate) {
return new File(error, ((WatcherDelegate) watcher).getDelegate().toString() + ".json");
} else {
return new File(error, watcher + ".json");
}
}

/**
Expand All @@ -188,12 +204,12 @@ private void cleanupErrorFile() {
* @param file the updated file
*/
public void onFileChange(File file) {
cleanupErrorFile();
mojo.getLog().info(EMPTY_STRING);
mojo.getLog().info("The watcher has detected a change in " + file.getAbsolutePath());
mojo.getLog().info(EMPTY_STRING);
for (Watcher watcher : watchers) {
if (watcher.accept(file)) {
cleanupErrorFile(watcher);
// This flag will be set to false if the processing must be interrupted.
boolean continueProcessing;
try {
Expand All @@ -202,7 +218,7 @@ public void onFileChange(File file) {
mojo.getLog().debug(watcher + " has thrown an exception while handling the " + file.getName() + EMPTY_STRING +
" update", e);
mojo.getLog().error(String.format(WATCHING_EXCEPTION_MESSAGE, e.getMessage()));
createErrorFile(e);
createErrorFile(watcher, e);
continueProcessing = false;
}
if (!continueProcessing) {
Expand All @@ -221,12 +237,12 @@ public void onFileChange(File file) {
* @param file the deleted file
*/
public void onFileDelete(File file) {
cleanupErrorFile();
mojo.getLog().info(EMPTY_STRING);
mojo.getLog().info("The watcher has detected a deleted file: " + file.getAbsolutePath());
mojo.getLog().info(EMPTY_STRING);
for (Watcher watcher : watchers) {
if (watcher.accept(file)) {
cleanupErrorFile(watcher);
// This flag will be set to false if the processing must be interrupted.
boolean continueProcessing;
try {
Expand All @@ -235,7 +251,7 @@ public void onFileDelete(File file) {
mojo.getLog().debug(watcher + " has thrown an exception while handling the " + file.getName() + EMPTY_STRING +
" deletion", e);
mojo.getLog().error(String.format(WATCHING_EXCEPTION_MESSAGE, e.getMessage()));
createErrorFile(e);
createErrorFile(watcher, e);
continueProcessing = false;
}
if (!continueProcessing) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ public WatcherDelegate(Object delegate) {
}
}

/**
* @return the delegate.
*/
public Object getDelegate() {
return delegate;
}

@Override
public boolean accept(File file) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ public void testWatchingException() throws IOException {
waitPullPeriod();

// Check that the error file was created.
File error = new File(FAKE, "target/pipeline/error.json");
File error = new File(FAKE, "target/pipeline/" + bad + ".json");
assertThat(error).exists();
assertThat(FileUtils.readFileToString(error)).contains("10").contains("11").contains("touch.md").contains
("bad");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
* 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.
Expand Down Expand Up @@ -161,7 +161,7 @@ private MonitorExtension getExtensionByName(String name) {
return extension;
}
}
return null;
//return null;

This comment has been minimized.

Copy link
@barjo

barjo Jun 5, 2014

Member

The method is missing the return statement if there is no extension matching the name!

}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
import org.wisdom.api.DefaultController;
import org.wisdom.api.configuration.ApplicationConfiguration;
import org.wisdom.api.content.Json;
import org.wisdom.api.http.*;
import org.wisdom.api.http.Context;
import org.wisdom.api.http.*;
import org.wisdom.api.interception.Filter;
import org.wisdom.api.interception.RequestContext;
import org.wisdom.api.router.Route;
Expand Down Expand Up @@ -94,12 +94,28 @@ public class DefaultPageErrorHandler extends DefaultController implements Filter
@Requires
protected Json json;

private File error;
/**
* The directory where error report (created by watchers) are created.
*/
private File pipelineErrorDirectory;


@Validate
public void start() {
error = new File(configuration.getBaseDir().getParentFile(), "pipeline/error.json");
pipelineErrorDirectory = new File(configuration.getBaseDir().getParentFile(), "pipeline");
}

public File getFirstErrorFile() {
if (!pipelineErrorDirectory.isDirectory()) {
return null;
}
// We make the assumption that the directory only store error report and nothing else.
File[] files = pipelineErrorDirectory.listFiles();
if (files == null || files.length == 0) {
return null;
}
// Return the first error report.
return files[0];
}

/**
Expand Down Expand Up @@ -201,10 +217,11 @@ public Result call(Route route, RequestContext context) throws Exception {
// change.
if (configuration.isDev() && context.request().accepts(MimeTypes.HTML) && pipeline != null) {
// Check whether the error file is there
if (error.isFile()) {
File error = getFirstErrorFile();
if (error != null) {
logger().debug("Error file detected, preparing rendering");
try {
return renderPipelineError();
return renderPipelineError(error);
} catch (IOException e) {
LOGGER.error("An exception occurred while generating the error page for {} {}",
route.getHttpMethod(),
Expand All @@ -231,12 +248,13 @@ public Result call(Route route, RequestContext context) throws Exception {
}
}

private Result renderPipelineError() throws IOException {
private Result renderPipelineError(File error) throws IOException {
String content = FileUtils.readFileToString(error);
ObjectNode node = (ObjectNode) json.parse(content);

String message = node.get("message").asText();
String file = node.get("file").asText();
String file = node.get("file").asText();
String watcher = node.get("watcher").asText();
int line = -1;
int character = -1;

Expand All @@ -260,11 +278,11 @@ private Result renderPipelineError() throws IOException {

return internalServerError(render(pipeline,
"message", message,
"file", source,
"source", source,
"line", line,
"character", character,
"lines", lines,
"content", fileContent));
"watcher", watcher));
}

private Result renderNotFound(Route route, Result result) {
Expand All @@ -287,7 +305,7 @@ private Result switchToGet(Route route, RequestContext context) {
} else {
try {
Result result = getRoute.invoke();
// Replace the content with NO_CONTENT but we need to preserve the headers (CONTENT_TYPE and
// Replace the content with NO_CONTENT but we need to preserve the headers (CONTENT-TYPE and
// CONTENT-LENGTH). These headers may not have been set, so we searches values in the renderable
// objects too.
final Renderable renderable = result.getRenderable();
Expand Down

0 comments on commit b53ca3c

Please sign in to comment.