patterntesting.runtime.log.LazyObjectRecorder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of patterntesting-rt Show documentation
Show all versions of patterntesting-rt Show documentation
PatternTesting Runtime (patterntesting-rt) is the runtime component for
the PatternTesting framework. It provides the annotations and base classes
for the PatternTesting testing framework (e.g. patterntesting-check,
patterntesting-concurrent or patterntesting-exception) but can be also
used standalone for classpath monitoring or profiling.
It uses AOP and AspectJ to perform this feat.
/*
* Copyright (c) 2013 by Oli B.
*
* 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 orimplied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* (c)reated 01.09.2013 by Oli B. ([email protected])
*/
package patterntesting.runtime.log;
import java.io.*;
import java.util.*;
import org.apache.commons.lang.ObjectUtils;
import org.aspectj.lang.JoinPoint;
import org.slf4j.*;
import patterntesting.annotation.check.runtime.NullArgsAllowed;
import patterntesting.runtime.util.*;
/**
* In contradiction to {@link ObjectRecorder} this class only records joinpoints
* and return values if they are different from the last record. I.e. If a
* return value is always the same for the same joinpoint this pair is only
* recorded once.
*
* @author oliver ([email protected])
* @since 1.3.1 (01.09.2013)
*/
public class LazyObjectRecorder extends ObjectRecorder {
private static final Logger log = LoggerFactory.getLogger(LazyObjectRecorder.class);
private final Map cachedJoinpoints = new HashMap();
/**
* Instantiates a new lazy object recorder.
*/
public LazyObjectRecorder() {
super();
}
/**
* Instantiates a new lazy object recorder.
*
* @param logFile the log file
*/
public LazyObjectRecorder(final File logFile) {
super(logFile);
}
/**
* Instantiates a new lazy object recorder.
*
* @param ostream the ostream
*/
public LazyObjectRecorder(OutputStream ostream) {
super(ostream);
}
/**
* Both things are logged with this method: the call of a method (joinPoint)
* and the return value of this method. Constructors or method of type
* 'void' are not recorded because the have no return value.
*
* Because the given joinPoint cannot be used as key for a map in
*
* @param joinPoint the joinpoint
* @param returnValue the return value
* {@link ObjectPlayer} it is saved as string. As a side effect this will
* speedup the serialization stuff and shorten the generated record file.
*
*
* The given return value will be only stored if it is not the same as the
* last time.
*
*/
@Override
@NullArgsAllowed
public void log(final JoinPoint joinPoint, final Object returnValue) {
String statement = JoinPointHelper.getAsLongString(joinPoint);
if ((returnValue != null) && (SignatureHelper.hasReturnType(joinPoint.getSignature()))) {
try {
saveLazy(statement, returnValue);
} catch (IOException ioe) {
log.debug("Logging failed because of {}.", ioe.getMessage());
log.info("{} = {}", statement, returnValue);
}
} else {
log.trace("Not recorded: {}", statement);
}
}
private void saveLazy(final String statement, final Object returnValue) throws IOException {
ValueContainer saved = this.cachedJoinpoints.get(statement);
if (saved == null) {
this.cachedJoinpoints.put(statement, new ValueContainer(returnValue));
save(statement, returnValue);
} else {
if (ObjectUtils.equals(saved.value, returnValue)) {
log.trace("cached: {} = {}", statement, returnValue);
saved.count++;
} else {
saveCache(statement, saved);
saved.setValue(returnValue);
save(statement, returnValue);
}
}
}
private void saveCache(final String statement, ValueContainer saved) throws IOException {
log.trace("saving: {} = {}", statement, saved);
for (int i = 0; i < saved.count; i++) {
save(statement, saved.value);
}
}
// ------------------------------------------------------------------------
/**
* This container acts as a kind of cache. The count attribute is used to
* store the number of (unsaved) values.
*/
private static class ValueContainer {
protected Object value;
protected int count;
protected ValueContainer(final Object value) {
this.value = value;
}
protected void setValue(final Object newValue) {
this.value = newValue;
this.count = 0;
}
@Override
public String toString() {
return value + " (" + count + " x cached)";
}
}
}