org.glassfish.jersey.internal.Errors Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ehcache Show documentation
Show all versions of ehcache Show documentation
Ehcache is an open source, standards-based cache used to boost performance,
offload the database and simplify scalability. Ehcache is robust, proven and full-featured and
this has made it the most widely-used Java-based cache.
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2012-2013 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* http://glassfish.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package org.glassfish.jersey.internal;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.logging.Logger;
import org.glassfish.jersey.Severity;
import org.glassfish.jersey.internal.util.Producer;
/**
* Errors utility used to file processing messages (e.g. validation, provider, resource building errors, hint).
*
* Error filing methods ({@code #warning}, {@code #error}, {@code #fatal}) can be invoked only in the "error scope" which is
* created by {@link #process(Producer)} or
* {@link #processWithException(Producer)} methods. Filed error messages are present also in this
* scope.
*
* TODO do not use static thread local?
*
* @author Michal Gajdos (michal.gajdos at oracle.com)
*/
public class Errors {
private static final Logger LOGGER = Logger.getLogger(Errors.class.getName());
private static final ThreadLocal errors = new ThreadLocal();
/**
* Add an error to the list of messages.
*
* @param message message of the error.
* @param severity indicates severity of added error.
*/
public static void error(final String message, Severity severity) {
error(null, message, severity);
}
/**
* Add an error to the list of messages.
*
* @param source source of the error.
* @param message message of the error.
* @param severity indicates severity of added error.
*/
public static void error(final Object source, final String message, final Severity severity) {
getInstance().issues.add(new ErrorMessage(source, message, severity));
}
/**
* Add a fatal error to the list of messages.
*
* @param source source of the error.
* @param message message of the error.
*/
public static void fatal(final Object source, final String message) {
error(source, message, Severity.FATAL);
}
/**
* Add a warning to the list of messages.
*
* @param source source of the error.
* @param message message of the error.
*/
public static void warning(final Object source, final String message) {
error(source, message, Severity.WARNING);
}
/**
* Add a hint to the list of messages.
*
* @param source source of the error.
* @param message message of the error.
*/
public static void hint(final Object source, final String message) {
getInstance().issues.add(new ErrorMessage(source, message, Severity.HINT));
}
/**
* Log errors and throw an exception if there are any fatal issues detected and
* the {@code throwException} flag has been set to {@code true}.
*
* @param throwException if set to {@code true}, any fatal issues will cause a {@link ErrorMessagesException}
* to be thrown.
*/
private static void processErrors(final boolean throwException) {
final List errors = new ArrayList(Errors.errors.get().issues);
boolean isFatal = logErrors(errors);
if (throwException && isFatal) {
throw new ErrorMessagesException(errors);
}
}
/**
* Log errors and return a status flag indicating whether a fatal issue has been found
* in the error collection.
*
* The {@code afterMark} flag indicates whether only those issues should be logged that were
* added after a {@link #mark() mark has been set}.
*
*
* @param afterMark if {@code true}, only issues added after a mark has been set are returned,
* if {@code false} all issues are returned.
* @return {@code true} if there are any fatal issues present in the collection, {@code false}
* otherwise.
*/
public static boolean logErrors(final boolean afterMark) {
return logErrors(getInstance()._getErrorMessages(afterMark));
}
/**
* Log supplied errors and return a status flag indicating whether a fatal issue has been found
* in the error collection.
*
* @param errors a collection of errors to be logged.
* @return {@code true} if there are any fatal issues present in the collection, {@code false}
* otherwise.
*/
private static boolean logErrors(final Collection errors) {
boolean isFatal = false;
if (!errors.isEmpty()) {
StringBuilder fatals = new StringBuilder("\n");
StringBuilder warnings = new StringBuilder();
StringBuilder hints = new StringBuilder();
for (final ErrorMessage error : errors) {
switch (error.getSeverity()) {
case FATAL:
isFatal = true;
fatals.append(LocalizationMessages.ERROR_MSG(error.getMessage())).append('\n');
break;
case WARNING:
warnings.append(LocalizationMessages.WARNING_MSG(error.getMessage())).append('\n');
break;
case HINT:
warnings.append(LocalizationMessages.HINT_MSG(error.getMessage())).append('\n');
break;
}
}
if (isFatal) {
LOGGER.severe(LocalizationMessages.ERRORS_AND_WARNINGS_DETECTED(fatals.append(warnings)
.append(hints).toString()));
} else {
if (warnings.length() > 0) {
LOGGER.warning(LocalizationMessages.WARNINGS_DETECTED(warnings.toString()));
}
if (hints.length() > 0) {
LOGGER.config(LocalizationMessages.HINTS_DETECTED(hints.toString()));
}
}
}
return isFatal;
}
/**
* Check whether a fatal error is present in the list of all messages.
*
* @return {@code true} if there are any fatal issues in this error context, {@code false} otherwise.
*/
public static boolean fatalIssuesFound() {
for (final ErrorMessage message : getInstance().issues) {
if (message.getSeverity() == Severity.FATAL) {
return true;
}
}
return false;
}
/**
* Invoke given producer task and gather errors.
*
* After the task is complete all gathered errors are logged. No exception is thrown
* even if there is a fatal error present in the list of errors.
*
* @param producer producer task to be invoked.
* @return the result produced by the task.
*/
public static T process(final Producer producer) {
return process(producer, false);
}
/**
* Invoke given callable task and gather messages.
*
* After the task is complete all gathered errors are logged. Any exception thrown
* by the throwable is re-thrown.
*
* @param task callable task to be invoked.
* @return the result produced by the task.
* @throws Exception exception thrown by the task.
*/
public static T process(final Callable task) throws Exception {
return process(task, true);
}
/**
* Invoke given producer task and gather messages.
*
* After the task is complete all gathered errors are logged. If there is a fatal error
* present in the list of errors an {@link ErrorMessagesException exception} is thrown.
*
* @param producer producer task to be invoked.
* @return the result produced by the task.
*/
public static T processWithException(final Producer producer) {
return process(producer, true);
}
/**
* Invoke given task and gather messages.
*
* After the task is complete all gathered errors are logged. No exception is thrown
* even if there is a fatal error present in the list of errors.
*
* @param task task to be invoked.
*/
public static void process(final Runnable task) {
process(new Producer() {
@Override
public Void call() {
task.run();
return null;
}
}, false);
}
/**
* Invoke given task and gather messages.
*
* After the task is complete all gathered errors are logged. If there is a fatal error
* present in the list of errors an {@link ErrorMessagesException exception} is thrown.
*
* @param task task to be invoked.
*/
public static void processWithException(final Runnable task) {
process(new Producer() {
@Override
public Void call() {
task.run();
return null;
}
}, true);
}
private static T process(final Producer task, final boolean throwException) {
try {
return process((Callable) task, throwException);
} catch (RuntimeException ex) {
throw ex;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
private static T process(final Callable task, final boolean throwException) throws Exception {
Errors instance = errors.get();
if (instance == null) {
instance = new Errors();
errors.set(instance);
}
instance.preProcess();
Exception caught = null;
try {
return task.call();
} catch (Exception re) {
// If a runtime exception is caught then report errors and rethrow.
caught = re;
} finally {
instance.postProcess(throwException && caught == null);
}
throw caught;
}
private static Errors getInstance() {
final Errors instance = errors.get();
// No error processing in scope
if (instance == null) {
throw new IllegalStateException(LocalizationMessages.NO_ERROR_PROCESSING_IN_SCOPE());
}
// The following should not be necessary but given the fragile nature of
// static thread local probably best to add it in case some internals of
// this class change
if (instance.stack == 0) {
errors.remove();
throw new IllegalStateException(LocalizationMessages.NO_ERROR_PROCESSING_IN_SCOPE());
}
return instance;
}
/**
* Get the list of all error messages.
*
* @return non-null error message list.
*/
public static List getErrorMessages() {
return getErrorMessages(false);
}
/**
* Get the list of error messages.
*
* The {@code afterMark} flag indicates whether only those issues should be returned that were
* added after a {@link #mark() mark has been set}.
*
*
* @param afterMark if {@code true}, only issues added after a mark has been set are returned,
* if {@code false} all issues are returned.
* @return non-null error list.
*/
public static List getErrorMessages(final boolean afterMark) {
return getInstance()._getErrorMessages(afterMark);
}
/**
* Set a mark at a current position in the errors messages list.
*/
public static void mark() {
getInstance()._mark();
}
/**
* Remove a previously set mark, if any.
*/
public static void unmark() {
getInstance()._unmark();
}
/**
* Removes all issues that have been added since the last marked position as well as
* removes the last mark.
*/
public static void reset() {
getInstance()._reset();
}
private final ArrayList issues = new ArrayList(0);
private Errors() {
}
private Deque mark = new ArrayDeque(4);
private int stack = 0;
private void _mark() {
mark.addLast(issues.size());
}
private void _unmark() {
mark.pollLast();
}
private void _reset() {
final Integer _pos = mark.pollLast(); // also performs "unmark" functionality
final int markedPos = (_pos == null) ? -1 : _pos;
if (markedPos >= 0 && markedPos < issues.size()) {
issues.subList(markedPos, issues.size()).clear();
}
}
private void preProcess() {
stack++;
}
private void postProcess(boolean throwException) {
stack--;
if (stack == 0) {
try {
if (!issues.isEmpty()) {
processErrors(throwException);
}
} finally {
errors.remove();
}
}
}
private List _getErrorMessages(final boolean afterMark) {
if (afterMark) {
final Integer _pos = mark.peekLast();
final int markedPos = (_pos == null) ? -1 : _pos;
if (markedPos >= 0 && markedPos < issues.size()) {
return Collections.unmodifiableList(new ArrayList(issues.subList(markedPos, issues.size())));
} // else return all errors
}
return Collections.unmodifiableList(new ArrayList(issues));
}
/**
* Error message exception.
*/
public static class ErrorMessagesException extends RuntimeException {
private final List messages;
private ErrorMessagesException(final List messages) {
this.messages = messages;
}
/**
* Get encountered error messages.
*
* @return encountered error messages.
*/
public List getMessages() {
return messages;
}
}
/**
* Generic error message.
*/
public static class ErrorMessage {
private final Object source;
private final String message;
private final Severity severity;
private ErrorMessage(final Object source, final String message, Severity severity) {
this.source = source;
this.message = message;
this.severity = severity;
}
/**
* Get {@link Severity}.
*
* @return severity of current {@code ErrorMessage}.
*/
public Severity getSeverity() {
return severity;
}
/**
* Human-readable description of the issue.
*
* @return message describing the issue.
*/
public String getMessage() {
return message;
}
/**
* The issue source.
*
* Identifies the object where the issue was found.
*
* @return source of the issue.
*/
public Object getSource() {
return source;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ErrorMessage that = (ErrorMessage) o;
if (message != null ? !message.equals(that.message) : that.message != null) {
return false;
}
if (severity != that.severity) {
return false;
}
if (source != null ? !source.equals(that.source) : that.source != null) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = source != null ? source.hashCode() : 0;
result = 31 * result + (message != null ? message.hashCode() : 0);
result = 31 * result + (severity != null ? severity.hashCode() : 0);
return result;
}
}
}