/** * Copyright 2011-2017 Asakusa Framework Team. * * 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.asakusafw.runtime.core; import java.io.IOException; import java.text.MessageFormat; import com.asakusafw.runtime.core.api.ApiStub; import com.asakusafw.runtime.core.api.ReportApi; import com.asakusafw.runtime.core.legacy.LegacyReport; import com.asakusafw.runtime.core.legacy.RuntimeResource; /** * Report API entry class. * The Report API enables to notify some messages in operator methods, to the runtime reporting system * (e.g. logger, standard output, or etc.). * Generally, the Report API does not have any effect on the batch execution, for example, the batch execution will * continue even if {@link Report#error(String)} is invoked. * Clients should put <code>@Sticky</code> annotation for operator methods using this API, otherwise the Asakusa * DSL compiler optimization may remove the target operator. <pre><code> @Sticky @Update public void updateWithReport(Hoge hoge) { if (hoge.getValue() < 0) { Report.error("invalid value"); } else { hoge.setValue(0); } } </code></pre> * @since 0.1.0 * @version 0.9.0 */ public final class Report { /** * The Hadoop property name of the custom implementation class name of {@link Report.Delegate}. * To use a default implementation, clients should set {@code com.asakusafw.runtime.core.Report$Default} to it. */ public static final String K_DELEGATE_CLASS = "com.asakusafw.runtime.core.Report.Delegate"; //$NON-NLS-1$ private static final ApiStub<ReportApi> STUB = new ApiStub<>(LegacyReport.API); private Report() { return; } /** * Reports an <em>informative</em> message. * Clients should put <code>@Sticky</code> annotation to the operator method that using this. * @param message the message * @throws Report.FailedException if error was occurred while reporting tReportessage * @see Report */ public static void info(String message) { STUB.get().info(message); } /** * Reports an <em>informative</em> message. * Clients should put <code>@Sticky</code> annotation to the operator method that using this. * @param message the message * @param throwable the optional exception object (nullable) * @throws Report.FailedException if error was occurred while reporting the message * @see Report * @since 0.5.1 */ public static void info(String message, Throwable throwable) { STUB.get().info(message, throwable); } /** * Reports a <em>warning</em> message. * Clients should put <code>@Sticky</code> annotation to the operator method that using this. * @param message the message * @throws Report.FailedException if error was occurred while reporting the message * @see Report */ public static void warn(String message) { STUB.get().warn(message); } /** * Reports a <em>warning</em> message. * Clients should put <code>@Sticky</code> annotation to the operator method that using this. * @param message the message * @param throwable the optional exception object (nullable) * @throws Report.FailedException if error was occurred while reporting the message * @see Report * @since 0.5.1 */ public static void warn(String message, Throwable throwable) { STUB.get().warn(message, throwable); } /** * Reports an <em>error</em> message. * Clients should put <code>@Sticky</code> annotation to the operator method that using this. * Please be careful that this method will <em>NOT</em> shutdown the running batch. * To shutdown the batch, throw an exception ({@link RuntimeException}) in operator methods. * @param message the message * @throws Report.FailedException if error was occurred while reporting the message * @see Report */ public static void error(String message) { STUB.get().error(message); } /** * Reports an <em>error</em> message. * Clients should put <code>@Sticky</code> annotation to the operator method that using this. * Please be careful that this method will <em>NOT</em> shutdown the running batch. * To shutdown the batch, throw an exception ({@link RuntimeException}) in operator methods. * @param message the message * @param throwable the optional exception object (nullable) * @throws Report.FailedException if error was occurred while reporting the message * @see Report * @since 0.5.1 */ public static void error(String message, Throwable throwable) { STUB.get().error(message, throwable); } /** * Returns the API stub. * Application developer must not use this directly. * @return the API stub * @since 0.9.0 */ public static ApiStub<ReportApi> getStub() { return STUB; } /** * {@link FailedException} is thrown when an exception was occurred while processing messages in {@link Report}. */ public static class FailedException extends RuntimeException { private static final long serialVersionUID = 1L; /** * Creates a new instance. */ public FailedException() { super(); } /** * Creates a new instance. * @param message the exception message (nullable) * @param cause the original cause (nullable) */ public FailedException(String message, Throwable cause) { super(message, cause); } /** * Creates a new instance. * @param message the exception message (nullable) */ public FailedException(String message) { super(message); } /** * Creates a new instance. * @param cause the original cause (nullable) */ public FailedException(Throwable cause) { super(cause); } } /** * An abstract super class of delegation objects for {@link Report}. * Application developers can inherit this class, and set the fully qualified name to the property * {@link Report#K_DELEGATE_CLASS} to use the custom implementation for the Report API. * @since 0.1.0 * @version 0.7.4 */ public abstract static class Delegate implements RuntimeResource { /** * Notifies a report. * @param level report level * @param message report message * @throws IOException if failed to notify this report by I/O error */ public abstract void report(Level level, String message) throws IOException; /** * Notifies a report. * @param level report level * @param message report message * @param throwable optional exception info (nullable) * @throws IOException if failed to notify this report by I/O error * @since 0.5.1 */ public void report(Level level, String message, Throwable throwable) throws IOException { report(level, message); } } /** * Represents levels of reporting. */ public enum Level { /** * Informative level. */ INFO, /** * Warning level. */ WARN, /** * Erroneous level. */ ERROR, } /** * A basic implementation of {@link Delegate}. * @since 0.1.0 * @version 0.5.1 */ public static class Default extends Delegate { @Override public void report(Level level, String message) { switch (level) { case INFO: System.out.println(message); break; case WARN: System.err.println(message); new Exception("Warning").printStackTrace(); break; case ERROR: System.err.println(message); new Exception("Error").printStackTrace(); break; default: throw new AssertionError(MessageFormat.format( "[{0}] {1}", //$NON-NLS-1$ level, message)); } } @Override public void report(Level level, String message, Throwable throwable) { switch (level) { case INFO: System.out.println(message); if (throwable != null) { throwable.printStackTrace(System.out); } break; case WARN: case ERROR: System.err.println(message); if (throwable != null) { throwable.printStackTrace(System.err); } break; default: throw new AssertionError(MessageFormat.format( "[{0}] {1}", //$NON-NLS-1$ level, message)); } } } }