Log the return value of a method using spring aop - spring-aop

I have a method which returns an object. I would like to print that object's value in my log using spring AOP. How can I achieve that?
Please help!

Using #AfterReturning with a returnValue param.
You could then interogate the object returned
This is an example where I do it on everything but get methods in a repository
#AfterReturning(value = "#target(org.springframework.stereotype.Repository) && !execution(* get*(..))", returning = "returnValue")
public void loggingRepositoryMethods(JoinPoint joinPoint, Object returnValue) {
String classMethod = this.getClassMethod(joinPoint);
if(returnValue !=null)
{
//test type of object get properties (could use reflection)
log it out
}
else
{
//do logging here probably passing in (joinPoint, classMethod);
}
}

In our case, majority of the time we return entity classes of spring modal, so we overridden the toString method of all entity classes with required minimal information and printed as below
#AfterReturning(pointcut = "within(#org.springframework.stereotype.Service *)", returning = "result")
public void logAfterReturning(JoinPoint joinPoint, Object result) {
logger.info(" ###### Returning for class : {} ; Method : {} ", joinPoint.getTarget().getClass().getName(), joinPoint.getSignature().getName());
if (result != null) {
logger.info(" ###### with value : {}", result.toString());
} else{
logger.info(" ###### with null as return value.");
}
}

Spent quite sometime on this, so putting here...
package com.mycomp.poc.JcachePoc.config;
import java.util.HashMap;
import java.util.Map;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
#Aspect
#Component
#Slf4j
public class LoggingAspectConfig {
private static LoggingAspectConfig loggingAspectConfig;
#Bean
public LoggingAspectConfig createBean() {
if(loggingAspectConfig==null)
return new LoggingAspectConfig();
else
return loggingAspectConfig;
}
private LoggingAspectConfig () {
}
#Before("execution(* com.mycom.poc.JcachePoc.service*.*.*(..)) && #annotation(Log)")
public void logBefore(JoinPoint joinPoint) {
if(log.isDebugEnabled()) {
Object[] args= joinPoint.getArgs();
Map<String, String> typeValue= new HashMap<>();
for(Object obj: args) {
if(obj!=null) {
typeValue.put(obj.getClass().getName(), obj.toString());
}
}
//log.debug("calling Method:"+joinPoint.getSignature().getDeclaringTypeName()+", "+joinPoint.getSignature().getName()+", Parameter:-> "+ typeValue);
}
}
#AfterReturning(pointcut = "execution(* com.mycom.poc.JcachePoc.service*.*.*(..)) && #annotation(Log)", returning = "result")
public void logAfter(JoinPoint joinPoint, Object result) {
if (log.isDebugEnabled() && result!=null) {
log.debug("Method returned:" +
joinPoint.getSignature().getName() + ", Result: " + result.getClass().getName()+" -->"+result);
}
//log.info(gson.toJson(result));
}
}

Related

Implemented methods are not working in native interface(android) for Fused location provider

I'm trying to apply FusedLocation provider in cn1 through native interface(android). I've implemented ConnectionCallbacks and OnConnectionFailedListener interfaces. It generates methods like onConnected(), onConnectionSuspended() and onConnectionFailed() in native android, which are not working when app is built in cn1.
Moreover, lifecycle methods like onResume, onDestroy of FusedLocationImpl etc are also not working. In the coming days, I'm planning to create the fused GPS library for general purpose as well.
PS. There are no build errors and got no any other errors when debugged.
FusedLocationImpl.java
import com.codename1.impl.android.AndroidNativeUtil;
import android.os.Bundle;
import android.location.Location;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationAvailability;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.location.LocationListener;
public class FusedLocationImpl implements GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
private GoogleApiClient mGoogleApiClient;
private final static int PLAY_SERVICES_RESOLUTION_REQUEST = 1000;
private Location mLastLocation;
private LocationRequest mLocationRequest;
public boolean isSupported() {
return true;
}
public void getFusedLocationPermission() {
if (!com.codename1.impl.android.AndroidNativeUtil.checkForPermission(Manifest.permission.ACCESS_FINE_LOCATION, "Please allow location permission")) {
}
}
public void fusedLocation() {
if (checkPlayServices()) {
buildGoogleApiClient();
}
}
public void onConnected(Bundle bundle) { // not working...
Log.i("onConnected", "GoogleApiClient connected!");
createLocationRequest();
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
Log.i("onConnected", " Location: " + mLastLocation);
}
protected void createLocationRequest() { // not working...
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(10000);
mLocationRequest.setFastestInterval(5000);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, new LocationCallback() {
#Override
public void onLocationResult(final LocationResult locationResult) {
Log.i("onLocationResult",locationResult + "");
}
#Override
public void onLocationAvailability(LocationAvailability locationAvailability) {
Log.i("onLocationAvailability", "onLocationAvailability: isLocationAvailable = " + locationAvailability.isLocationAvailable());
}
}, null);
}
public void onConnectionSuspended(int i) {
mGoogleApiClient.connect();
}
public void onConnectionFailed(ConnectionResult result) {
Log.i("onConnectionFailed", "Connection failed: ConnectionResult.getErrorCode() = "
+ result.getErrorCode());
}
public void onPause() {
if (mGoogleApiClient.isConnected()) {
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, (LocationListener) this);
mGoogleApiClient.disconnect();
}
}
public void onResume() {
checkPlayServices();
}
public void onStart() {
if (mGoogleApiClient != null) {
mGoogleApiClient.connect();
Log.i("onStart", "mGoogleApiClient.connect()");
}
}
public void onDestroy() {
Log.i("onDestory", "Service destroyed!");
mGoogleApiClient.disconnect();
}
private boolean checkPlayServices() {
int resultCode = GooglePlayServicesUtil
.isGooglePlayServicesAvailable(AndroidNativeUtil.getActivity());
if (resultCode != ConnectionResult.SUCCESS) {
if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) {
GooglePlayServicesUtil.getErrorDialog(resultCode, AndroidNativeUtil.getActivity(),
PLAY_SERVICES_RESOLUTION_REQUEST).show();
} else {
AndroidNativeUtil.getActivity().finish();
}
return false;
}
return true;
}
protected synchronized void buildGoogleApiClient() {
Log.i("buildGoogleApiClient", "Building GoogleApiClient");
mGoogleApiClient = new GoogleApiClient.Builder(AndroidNativeUtil.getActivity())
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
}
FusedLocation.java
public interface FusedLocation extends NativeInterface{
public void getFusedLocationPermission();
public void fusedLocation();
}
MyApplication.java
public void start() {
if (current != null) {
current.show();
return;
}
Form hi = new Form("Hi World", BoxLayout.y());
hi.show();
Button btn = new Button("ask for permission");
FusedLocation fl = (FusedLocation) NativeLookup.create(FusedLocation.class);
Button btn1 = new Button("fused network");
hi.add(btn1);
btn1.addActionListener(e->{
if (fl != null && fl.isSupported()) {
fl.getFusedLocationPermission();
fl.fusedLocation();
System.out.println("fusedLocation");
}
});
}
PS. permission for location (ACCESS_FINE_LOCATION) succeeds. The main issue I've got is onConnected() and other implemented methods are not called. Thankyou
cn1 Location manager: this is not giving quite a performance as the native fused location provider
public final void checkGPS() {
if (Display.getInstance().getLocationManager().isGPSDetectionSupported()) {
if (Display.getInstance().getLocationManager().isGPSEnabled()) {
InfiniteProgress ip = new InfiniteProgress();
final Dialog ipDlg = ip.showInifiniteBlocking();
//Cancel after 20 seconds
Location loc = LocationManager.getLocationManager().getCurrentLocationSync(20000);
ipDlg.dispose();
if (loc != null) {
lat = loc.getLatitude();
lng = loc.getLongitude();
Dialog.show("location", "lat: " + lat + " lon: " + lng, "ok", null);
} else {
Dialog.show("GPS error", "Your location could not be found, please try going outside for a better GPS signal", "Ok", null);
}
} else {
Dialog.show("GPS disabled", "AppName needs access to GPS. Please enable GPS", "Ok", null);
}
} else {
Dialog.show("Warning", "GPS is not supported in your device", "ok", null);
}
}
I'm not sure why you aren't using the location listener which uses fused location internally when play services is enabled (the default).
You asked for permissions in the XML but didn't use the Android 6+ permissions which need a different syntax. Again, if you just use the location API this is all seamless...

spring aop pointcut not triggered

The issue is the #Before and #AfterReturning are working but it's not the case for Pointcut.
Here is my aspect.
As part of a springboot service, What I want do is trigger the pointcut with first method profile to show execution time and other things.
Am I missing something ?
package com.myproj.service.myagg.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
/**
* Created by shammami on 26/05/2017.
*/
#Aspect
#Component
public class LoggingService {
#Pointcut("execution(public void com.myproj.service.myagg.listener.MyMessageConsumer.handleMessage(..))")
public Object profile(ProceedingJoinPoint pjp) throws Throwable {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
boolean isExceptionThrown = false;
try {
// execute the profiled method
return pjp.proceed();
} catch (RuntimeException e) {
isExceptionThrown = true;
throw e;
} finally {
stopWatch.stop();
StopWatch.TaskInfo taskInfo = stopWatch.getLastTaskInfo();
// Log the method's profiling result
String profileMessage = taskInfo.getTaskName() + ": " + taskInfo.getTimeMillis() + " ms" +
(isExceptionThrown ? " (thrown Exception)" : "");
System.out.println(profileMessage);
}
}
#Before("execution(public void com.myproj.service.myagg.listener.MyMessageConsumer.handleMessage(..))")
public void before(JoinPoint joinPoint) {
System.out.println("Started: " + joinPoint.getStaticPart().getSignature().toLongString());
}
#AfterReturning("execution(public void com.myproj.service.myagg.listener.MyMessageConsumer.handleMessage(..))")
public void completed(JoinPoint joinPoint) {
System.out.println("Completed: " + joinPoint.getStaticPart().getSignature().toLongString());
}
}
When you annotate something with #Pointcut you are basically defining the pointcut signature, you can not do any sort of processing in there. What you need to do is to create another method which has all the processing details and uses the pointcut signature you evaluated above. Hence,
#Pointcut("execution(public void com.myproj.service.myagg.listener.MyMessageConsumer.handleMessage(..))")
public void myPointcutSignature(){
//This basically has nothing :)
}
#Around("myPointcutSignature")
public Object profile(ProceedingJoinPoint pjp) throws Throwable {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
boolean isExceptionThrown = false;
//And the remaining code
-------
}
Hope this works. Also remember that ProceedingJoinPoint can be used only with #Around advice.

Custom FunctionQuery ConstValueSource

I wrote small function query which returns constant value base on key passed in.
It works great for the first request, but when I change qk it doesn't actually return anything new because it is cached.
Is there any way to force solr not to cache this function query results, or make qk key for the cache, so if I change qk it will search again?
import java.io.IOException;
import java.util.Map;
import org.apache.lucene.index.IndexReader;
import org.apache.solr.search.function.DocValues;
import org.apache.solr.search.function.ValueSource;
public class ConstValueSource extends ValueSource {
final Map<String, Float> constants;
final String qk;
final String field;
public ConstValueSource(Map<String, Float> constants, String qk, String field) {
this.constants = constants;
this.qk=qk;
this.field=field;
}
public DocValues getValues(Map context, IndexReader reader) throws IOException {
return new DocValues() {
public float floatVal(int doc) {
return constants.get(qk);
}
public int intVal(int doc) {
return (int)floatVal(doc);
}
public long longVal(int doc) {
return (long)floatVal(doc);
}
public double doubleVal(int doc) {
return (double)floatVal(doc);
}
public String strVal(int doc) {
return Float.toString(floatVal(doc));
}
public String toString(int doc) {
return description();
}
};
}
#Override
public String description() {
return field + "_" + qk;
}
#Override
public boolean equals(Object o) {
if (!(o instanceof ConstValueSource))
return false;
ConstValueSource other = (ConstValueSource) o;
return this.field.equals(other.field) && this.qk.equals(other.qk);
}
#Override
public int hashCode() {
return field.hashCode() * qk.hashCode();
}
}
Here is my value parser
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.search.FunctionQParser;
import org.apache.solr.search.ValueSourceParser;
import org.apache.solr.search.function.ValueSource;
public class ConstSourceParser extends ValueSourceParser {
public void init(NamedList namedList) {}
public ValueSource parse(FunctionQParser fqp) {
try {
SolrParams paramters = fqp.getParams();
return new ConstValueSource(data, paramters.get("qk"), "qk");
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
Ok it was issue with old cache, after updating description and clearing cache it start working.
Is there any way to filter by response?
You can control caching with meta {!cache=false}
For example: fl={!cache=false}field(your_const_field)
And in filter query: fq={!frange l=2 cache=false}field(your_const_field)
More information:
https://cwiki.apache.org/confluence/display/solr/Common+Query+Parameters
http://searchhub.org/2012/02/10/advanced-filter-caching-in-solr/

Easy way to dynamically invoke web services (without JDK or proxy classes)

In Python I can consume a web service so easily:
from suds.client import Client
client = Client('http://www.example.org/MyService/wsdl/myservice.wsdl') #create client
result = client.service.myWSMethod("Bubi", 15) #invoke method
print result #print the result returned by the WS method
I'd like to reach such a simple usage with Java.
With Axis or CXF you have to create a web service client, i.e. a package which reproduces all web service methods so that we can invoke them as if they where normal methods. Let's call it proxy classes; usually they are generated by wsdl2java tool.
Useful and user-friendly. But any time I add/modify a web service method and I want to use it in a client program I need to regenerate proxy classes.
So I found CXF DynamicClientFactory, this technique avoids the use of proxy classes:
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.endpoint.dynamic.DynamicClientFactory;
//...
//create client
DynamicClientFactory dcf = DynamicClientFactory.newInstance();
Client client = dcf.createClient("http://www.example.org/MyService/wsdl/myservice.wsdl");
//invoke method
Object[] res = client.invoke("myWSMethod", "Bubi");
//print the result
System.out.println("Response:\n" + res[0]);
But unfortunately it creates and compiles proxy classes runtime, hence requires JDK on the production machine. I have to avoid this, or at least I can't rely on it.
My question:
Is there another way to dinamically invoke any method of a web service in Java, without having a JDK at runtime and without generating "static" proxy classes? Maybe with a different library? Thanks!
I know this is a really old question but if you are still interested you could use soap-ws github project: https://github.com/reficio/soap-ws
Here you have a sample usage really simple:
Wsdl wsdl = Wsdl.parse("http://www.webservicex.net/CurrencyConvertor.asmx?WSDL");
SoapBuilder builder = wsdl.binding()
.localPart("CurrencyConvertorSoap")
.find();
SoapOperation operation = builder.operation()
.soapAction("http://www.webserviceX.NET/ConversionRate")
.find();
Request request = builder.buildInputMessage(operation)
SoapClient client = SoapClient.builder()
.endpointUrl("http://www.webservicex.net/CurrencyConvertor.asmx")
.build();
String response = client.post(request);
As you can see it is really simple.
With CXF 3.x this could be possible with StaxDataBinding. Follow below steps to get the basics. Of course, this could be enhanced to your needs.
Create StaxDataBinding something like below. Note below code can be enhanced to your sophistication.
class StaxDataBinding extends AbstractInterceptorProvidingDataBinding {
private XMLStreamDataReader xsrReader;
private XMLStreamDataWriter xswWriter;
public StaxDataBinding() {
super();
this.xsrReader = new XMLStreamDataReader();
this.xswWriter = new XMLStreamDataWriter();
inInterceptors.add(new StaxInEndingInterceptor(Phase.POST_INVOKE));
inFaultInterceptors.add(new StaxInEndingInterceptor(Phase.POST_INVOKE));
inInterceptors.add(RemoveStaxInEndingInterceptor.INSTANCE);
inFaultInterceptors.add(RemoveStaxInEndingInterceptor.INSTANCE);
}
static class RemoveStaxInEndingInterceptor
extends AbstractPhaseInterceptor<Message> {
static final RemoveStaxInEndingInterceptor INSTANCE = new RemoveStaxInEndingInterceptor();
public RemoveStaxInEndingInterceptor() {
super(Phase.PRE_INVOKE);
addBefore(StaxInEndingInterceptor.class.getName());
}
public void handleMessage(Message message) throws Fault {
message.getInterceptorChain().remove(StaxInEndingInterceptor.INSTANCE);
}
}
public void initialize(Service service) {
for (ServiceInfo serviceInfo : service.getServiceInfos()) {
SchemaCollection schemaCollection = serviceInfo.getXmlSchemaCollection();
if (schemaCollection.getXmlSchemas().length > 1) {
// Schemas are already populated.
continue;
}
new ServiceModelVisitor(serviceInfo) {
public void begin(MessagePartInfo part) {
if (part.getTypeQName() != null
|| part.getElementQName() != null) {
return;
}
part.setTypeQName(Constants.XSD_ANYTYPE);
}
}.walk();
}
}
#SuppressWarnings("unchecked")
public <T> DataReader<T> createReader(Class<T> cls) {
if (cls == XMLStreamReader.class) {
return (DataReader<T>) xsrReader;
}
else {
throw new UnsupportedOperationException(
"The type " + cls.getName() + " is not supported.");
}
}
public Class<?>[] getSupportedReaderFormats() {
return new Class[] { XMLStreamReader.class };
}
#SuppressWarnings("unchecked")
public <T> DataWriter<T> createWriter(Class<T> cls) {
if (cls == XMLStreamWriter.class) {
return (DataWriter<T>) xswWriter;
}
else {
throw new UnsupportedOperationException(
"The type " + cls.getName() + " is not supported.");
}
}
public Class<?>[] getSupportedWriterFormats() {
return new Class[] { XMLStreamWriter.class, Node.class };
}
public static class XMLStreamDataReader implements DataReader<XMLStreamReader> {
public Object read(MessagePartInfo part, XMLStreamReader input) {
return read(null, input, part.getTypeClass());
}
public Object read(QName name, XMLStreamReader input, Class<?> type) {
return input;
}
public Object read(XMLStreamReader reader) {
return reader;
}
public void setSchema(Schema s) {
}
public void setAttachments(Collection<Attachment> attachments) {
}
public void setProperty(String prop, Object value) {
}
}
public static class XMLStreamDataWriter implements DataWriter<XMLStreamWriter> {
private static final Logger LOG = LogUtils
.getL7dLogger(XMLStreamDataWriter.class);
public void write(Object obj, MessagePartInfo part, XMLStreamWriter writer) {
try {
if (!doWrite(obj, writer)) {
// WRITE YOUR LOGIC HOW you WANT TO HANDLE THE INPUT DATA
//BELOW CODE JUST CALLS toString() METHOD
if (part.isElement()) {
QName element = part.getElementQName();
writer.writeStartElement(element.getNamespaceURI(),
element.getLocalPart());
if (obj != null) {
writer.writeCharacters(obj.toString());
}
writer.writeEndElement();
}
}
}
catch (XMLStreamException e) {
throw new Fault("COULD_NOT_READ_XML_STREAM", LOG, e);
}
}
public void write(Object obj, XMLStreamWriter writer) {
try {
if (!doWrite(obj, writer)) {
throw new UnsupportedOperationException("Data types of "
+ obj.getClass() + " are not supported.");
}
}
catch (XMLStreamException e) {
throw new Fault("COULD_NOT_READ_XML_STREAM", LOG, e);
}
}
private boolean doWrite(Object obj, XMLStreamWriter writer)
throws XMLStreamException {
if (obj instanceof XMLStreamReader) {
XMLStreamReader xmlStreamReader = (XMLStreamReader) obj;
StaxUtils.copy(xmlStreamReader, writer);
xmlStreamReader.close();
return true;
}
else if (obj instanceof XMLStreamWriterCallback) {
((XMLStreamWriterCallback) obj).write(writer);
return true;
}
return false;
}
public void setSchema(Schema s) {
}
public void setAttachments(Collection<Attachment> attachments) {
}
public void setProperty(String key, Object value) {
}
}
}
Prepare your input to match the expected input, something like below
private Object[] prepareInput(BindingOperationInfo operInfo, String[] paramNames,
String[] paramValues) {
List<Object> inputs = new ArrayList<Object>();
List<MessagePartInfo> parts = operInfo.getInput().getMessageParts();
if (parts != null && parts.size() > 0) {
for (MessagePartInfo partInfo : parts) {
QName element = partInfo.getElementQName();
String localPart = element.getLocalPart();
// whatever your input data you need to match data value for given element
// below code assumes names are paramNames variable and value in paramValues
for (int i = 0; i < paramNames.length; i++) {
if (paramNames[i].equals(localPart)) {
inputs.add(findParamValue(paramNames, paramValues, localPart));
}
}
}
}
return inputs.toArray();
}
Now set the proper data binding and pass the data
Bus bus = CXFBusFactory.getThreadDefaultBus();
WSDLServiceFactory sf = new WSDLServiceFactory(bus, wsdl);
sf.setAllowElementRefs(false);
Service svc = sf.create();
Client client = new ClientImpl(bus, svc, null,
SimpleEndpointImplFactory.getSingleton());
StaxDataBinding databinding = new StaxDataBinding();
svc.setDataBinding(databinding);
bus.getFeatures().add(new StaxDataBindingFeature());
BindingOperationInfo operInfo = ...//find the operation you need (see below)
Object[] inputs = prepareInput(operInfo, paramNames, paramValues);
client.invoke("operationname", inputs);
If needed you can match operation name something like below
private BindingOperationInfo findBindingOperation(Service service,
String operationName) {
for (ServiceInfo serviceInfo : service.getServiceInfos()) {
Collection<BindingInfo> bindingInfos = serviceInfo.getBindings();
for (BindingInfo bindingInfo : bindingInfos) {
Collection<BindingOperationInfo> operInfos = bindingInfo.getOperations();
for (BindingOperationInfo operInfo : operInfos) {
if (operInfo.getName().getLocalPart().equals(operationName)) {
if (operInfo.isUnwrappedCapable()) {
return operInfo.getUnwrappedOperation();
}
return operInfo;
}
}
}
}
return null;
}

Audit trail in hibernate with new and old values using an Interceptor

I can easily log last modified date, modified by etc. However, I need old and new value to be logged too. In the interceptor, I can fire a select before postflush starts executing to get the value of the current record. Then I can run a diff between this record and the new one to see what changed and log that information as old and new values. Is there a better way?
The problem is my object to be modified can be really huge with references to other objects too. Doing a diff can be expensive.
-thanks
Overriding onFlushDirty of EmptyInterceptor(IInterceptor) gives you arrays previousState and currentState. You can use these two arrays to find the oldvalue and newvalue.
look at this example
Why not use audit tables and triggers?
You can try Envers , whichis now part of Hibernate: http://www.jboss.org/envers
I audit this way, but dates are ugly:
persistence.xml: property name="hibernate.ejb.interceptor" value="siapen.jpa.interceptor.MeuInterceptador" />
package siapen.jpa.interceptor;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import org.apache.commons.lang3.ObjectUtils;
import org.hibernate.CallbackException;
import org.hibernate.EmptyInterceptor;
import org.hibernate.type.Type;
import siapen.model.BaseEntity;
public class MeuInterceptador extends EmptyInterceptor {
private static final long serialVersionUID = 7853236444153436270L;
private String strSQL = "";
String acao;
#SuppressWarnings("rawtypes")
BaseEntity entity;
String s = "";
#SuppressWarnings("unchecked")
// 1
public boolean onSave(Object obj, Serializable id, Object[] valores, String[] propertyNames, Type[] types)
throws CallbackException {
if (obj instanceof BaseEntity) {
entity = (BaseEntity) obj;
for (int i = 0; i < valores.length; i++) {
if (valores[i] != null && !valores[i].equals("")) {
s += propertyNames[i] + ":" + valores[i];
if (i != valores.length - 1) {
s += "___";
}
}
}
}
return false;
}
#SuppressWarnings("unchecked")
// 1
public boolean onFlushDirty(Object obj, Serializable id, Object[] valoresAtuais, Object[] valoresAnteriores,
String[] propertyNames, Type[] types) throws CallbackException {
if (obj instanceof BaseEntity) {
entity = (BaseEntity) obj;
for (int i = 0; i < valoresAtuais.length; i++) {
if (!ObjectUtils.equals(valoresAtuais[i], valoresAnteriores[i])) {
if (!s.equals("")) {
s += "___";
}
s += propertyNames[i] + "-Anterior:" + valoresAnteriores[i] + ">>>Novo:" + valoresAtuais[i];
}
}
}
return false;
}
#SuppressWarnings("unchecked")
// 1
public void onDelete(Object obj, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
if (obj instanceof BaseEntity) {
entity = (BaseEntity) obj;
}
}
// CHAMADO ANTES DO COMMIT
// 2
#SuppressWarnings("rawtypes")
public void preFlush(Iterator iterator) {
}
// 3
public String onPrepareStatement(String sql) {
acao = "";
if (sql.startsWith("/* update")) {
acao = "update";
} else if (sql.startsWith("/* insert")) {
acao = "insert";
} else if (sql.startsWith("/* delete")) {
acao = "delete";
}
if (acao != null) {
strSQL = sql;
}
return sql;
}
// CHAMADO APÓS O COMMIT
// 4
#SuppressWarnings("rawtypes")
public void postFlush(Iterator iterator) {
if (acao != null) {
try {
if (acao.equals("insert")) {
AuditLogUtil audi = new AuditLogUtil();
audi.LogIt("Salvo", entity, s);
}
if (acao.equals("update")) {
AuditLogUtil audi = new AuditLogUtil();
audi.LogIt("Atualizado", entity, s);
}
if (acao.equals("delete")) {
AuditLogUtil audi = new AuditLogUtil();
audi.LogIt("Deletado", entity, "");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
strSQL = "";
s = "";
}
}
}
}
For those using Envers this can be easily implemented using custom Envers listeners. Also it'll be probably bit cleaner solution.
Implement custom evnvers integrator and register your listeners (look at envers docs )
Implement your Update/Insert listener
public class UpdateEnversListener extends EnversPostUpdateEventListenerImpl {
private static Logger log = LoggerFactory.getLogger(UpdateEnversListener.class);
public UpdateEnversListener(EnversService enversService) {
super(enversService);
}
#Override
public void onPostUpdate(PostUpdateEvent event) {
List<String> auditedProperties = Arrays.asList(event.getPersister().getPropertyNames());
List<Integer> dirtyFieldsIndices = Ints.asList(event.getDirtyProperties());
// In the event you have a object
// In the persister you have indices of fields that changed and also their values
// Do your magic 🦄 stuff here
super.onPostUpdate(event);
}
}

Resources