diff --git a/core/wisdom-maven-plugin/src/main/java/org/wisdom/maven/MavenWatcher.java b/core/wisdom-maven-plugin/src/main/java/org/wisdom/maven/MavenWatcher.java new file mode 100644 index 000000000..b5cf11c5a --- /dev/null +++ b/core/wisdom-maven-plugin/src/main/java/org/wisdom/maven/MavenWatcher.java @@ -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(); +} diff --git a/core/wisdom-maven-plugin/src/main/java/org/wisdom/maven/mojos/AbstractWisdomWatcherMojo.java b/core/wisdom-maven-plugin/src/main/java/org/wisdom/maven/mojos/AbstractWisdomWatcherMojo.java index 667758cd1..c5ce2de5e 100644 --- a/core/wisdom-maven-plugin/src/main/java/org/wisdom/maven/mojos/AbstractWisdomWatcherMojo.java +++ b/core/wisdom-maven-plugin/src/main/java/org/wisdom/maven/mojos/AbstractWisdomWatcherMojo.java @@ -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; + } } diff --git a/core/wisdom-maven-plugin/src/main/java/org/wisdom/maven/pipeline/Pipeline.java b/core/wisdom-maven-plugin/src/main/java/org/wisdom/maven/pipeline/Pipeline.java index cb9b334b3..9e42c901f 100644 --- a/core/wisdom-maven-plugin/src/main/java/org/wisdom/maven/pipeline/Pipeline.java +++ b/core/wisdom-maven-plugin/src/main/java/org/wisdom/maven/pipeline/Pipeline.java @@ -73,9 +73,8 @@ public Pipeline(Mojo mojo, File baseDir, List 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()); } /** @@ -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); @@ -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) { @@ -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()); } @@ -168,7 +173,7 @@ 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); } @@ -176,9 +181,20 @@ private void createErrorFile(WatchingException e) { /** * 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"); + } } /** @@ -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 { @@ -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) { @@ -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 { @@ -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) { diff --git a/core/wisdom-maven-plugin/src/main/java/org/wisdom/maven/pipeline/WatcherDelegate.java b/core/wisdom-maven-plugin/src/main/java/org/wisdom/maven/pipeline/WatcherDelegate.java index 61eb37302..22aeea5b5 100644 --- a/core/wisdom-maven-plugin/src/main/java/org/wisdom/maven/pipeline/WatcherDelegate.java +++ b/core/wisdom-maven-plugin/src/main/java/org/wisdom/maven/pipeline/WatcherDelegate.java @@ -51,6 +51,13 @@ public WatcherDelegate(Object delegate) { } } + /** + * @return the delegate. + */ + public Object getDelegate() { + return delegate; + } + @Override public boolean accept(File file) { try { diff --git a/core/wisdom-maven-plugin/src/test/java/org/wisdom/maven/pipeline/PipelineTest.java b/core/wisdom-maven-plugin/src/test/java/org/wisdom/maven/pipeline/PipelineTest.java index 5473d6e65..de78f13c4 100644 --- a/core/wisdom-maven-plugin/src/test/java/org/wisdom/maven/pipeline/PipelineTest.java +++ b/core/wisdom-maven-plugin/src/test/java/org/wisdom/maven/pipeline/PipelineTest.java @@ -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"); diff --git a/extensions/wisdom-monitor/src/main/java/org/wisdom/monitor/MonitorCenter.java b/extensions/wisdom-monitor/src/main/java/org/wisdom/monitor/MonitorCenter.java index 263c4bb9c..fc0ede59a 100644 --- a/extensions/wisdom-monitor/src/main/java/org/wisdom/monitor/MonitorCenter.java +++ b/extensions/wisdom-monitor/src/main/java/org/wisdom/monitor/MonitorCenter.java @@ -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. @@ -161,7 +161,7 @@ private MonitorExtension getExtensionByName(String name) { return extension; } } - return null; + //return null; } /** diff --git a/framework/default-error-handler/src/main/java/org/wisdom/error/DefaultPageErrorHandler.java b/framework/default-error-handler/src/main/java/org/wisdom/error/DefaultPageErrorHandler.java index 48458e75d..674d93347 100644 --- a/framework/default-error-handler/src/main/java/org/wisdom/error/DefaultPageErrorHandler.java +++ b/framework/default-error-handler/src/main/java/org/wisdom/error/DefaultPageErrorHandler.java @@ -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; @@ -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]; } /** @@ -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(), @@ -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; @@ -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) { @@ -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();