
org.apache.tapestry5.ioc.internal.OperationTrackerImpl Maven / Gradle / Ivy
The newest version!
// Copyright 2008-2013, 2023 The Apache Software Foundation
//
// 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 org.apache.tapestry5.ioc.internal;
import org.apache.tapestry5.commons.util.CollectionFactory;
import org.apache.tapestry5.commons.util.ExceptionUtils;
import org.apache.tapestry5.commons.util.Stack;
import org.apache.tapestry5.ioc.IOOperation;
import org.apache.tapestry5.ioc.Invokable;
import org.apache.tapestry5.ioc.OperationTracker;
import org.apache.tapestry5.ioc.internal.util.InternalUtils;
import org.slf4j.Logger;
import java.io.IOException;
/**
* Core implementation that manages a logger and catches and reports exception.
*
* @see org.apache.tapestry5.ioc.internal.PerThreadOperationTracker
*/
public class OperationTrackerImpl implements OperationTracker
{
private final Logger logger;
private final Stack operations = CollectionFactory.newStack();
private boolean logged;
public OperationTrackerImpl(Logger logger)
{
this.logger = logger;
}
@Override
public void run(String description, final Runnable operation)
{
assert InternalUtils.isNonBlank(description);
assert operation != null;
long startNanos = start(description);
try
{
operation.run();
finish(description, startNanos);
} catch (RuntimeException ex)
{
logAndRethrow(ex);
} catch (Error ex)
{
handleError(ex);
} finally
{
handleFinally();
}
}
@Override
public T invoke(String description, Invokable operation)
{
assert InternalUtils.isNonBlank(description);
assert operation != null;
long startNanos = start(description);
try
{
T result = operation.invoke();
finish(description, startNanos);
return result;
} catch (RuntimeException ex)
{
return handleRuntimeException(ex);
} catch (Error ex)
{
return handleError(ex);
} finally
{
handleFinally();
}
}
@Override
public T perform(String description, IOOperation operation) throws IOException
{
InternalUtils.isNonBlank(description);
assert operation != null;
long startNanos = start(description);
try
{
T result = operation.perform();
finish(description, startNanos);
return result;
} catch (RuntimeException ex)
{
return handleRuntimeException(ex);
} catch (Error ex)
{
return handleError(ex);
} catch (IOException ex)
{
return logAndRethrow(ex);
} finally
{
handleFinally();
}
}
private T handleRuntimeException(RuntimeException ex)
{
// This is to prevent the error level log messages
if (ExceptionUtils.isAnnotationInStackTrace(ex, NonLoggableException.class))
// pass through without logging
throw ex;
else
return logAndRethrow(ex);
}
private void handleFinally()
{
operations.pop();
// We've finally backed out of the operation stack ... but there may be more to come!
if (operations.isEmpty())
{
logged = false;
}
}
private T handleError(Error error)
{
if (!logged)
{
log(error);
logged = true;
}
throw error;
}
private void finish(String description, long startNanos)
{
if (logger.isDebugEnabled())
{
long elapsedNanos = System.nanoTime() - startNanos;
double elapsedMillis = ((double) elapsedNanos) / 1000000.d;
logger.debug(String.format("[%3d] <-- %s [%,.2f ms]", operations.getDepth(), description, elapsedMillis));
}
}
private long start(String description)
{
long startNanos = -1l;
if (logger.isDebugEnabled())
{
startNanos = System.nanoTime();
logger.debug(String.format("[%3d] --> %s", operations.getDepth() + 1, description));
}
operations.push(description);
return startNanos;
}
private T logAndRethrow(RuntimeException ex)
{
if (!logged)
{
String[] trace = log(ex);
logged = true;
throw new OperationException(ex, trace);
}
throw ex;
}
private T logAndRethrow(IOException ex) throws IOException
{
if (!logged)
{
String[] trace = log(ex);
logged = true;
throw new OperationException(ex, trace);
}
throw ex;
}
private String[] log(Throwable ex)
{
logger.error(ExceptionUtils.toMessage(ex));
logger.error("Operations trace:");
Object[] snapshot = operations.getSnapshot();
String[] trace = new String[snapshot.length];
for (int i = 0; i < snapshot.length; i++)
{
trace[i] = snapshot[i].toString();
logger.error(String.format("[%2d] %s", i + 1, trace[i]));
}
return trace;
}
boolean isEmpty()
{
return operations.isEmpty();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy