I am trying to use AspectJ in a standalone application but does not seem to work.
Here are the classes I created-
package oata.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
#Aspect
public class AspectJTest {
#Around("execution(* *..*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("around fired");
jp.proceed();
}
}
package oata;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Target(value = ElementType.METHOD)
#Retention(RetentionPolicy.SOURCE)
public #interface AspectTest {
}
package oata;
import oata.AspectTest;
public class TestAspect {
public void doItWithout(int i) {
double s = Math.acos(i);
}
#AspectTest
public void doItAnnotated(int i) {
double s = Math.acos(i);
}
public void doItAspect(int i) {
double s = Math.acos(i);
}
}
package oata;
import java.util.Date;
public class Test {
public Test() {
}
public static void main(String arg[]) {
// performance testing
// invoke method without aspect
long t1 = new Date().getTime();
for (int i = 0; i < 1; i++) {
new TestAspect().doItWithout(i);
}
System.out.println("Invoke without aspect:"
+ (new Date().getTime() - t1));
// invoke method with annotated aspect
t1 = new Date().getTime();
for (int i = 0; i < 1; i++) {
new TestAspect().doItAnnotated(i);
}
System.out.println("Invoke annotated aspect method:"
+ (new Date().getTime() - t1));
// invoke method with aspect but not annotated
t1 = new Date().getTime();
for (int i = 0; i < 1; i++) {
new TestAspect().doItAspect(i);
}
System.out.println("Invoke aspect method:"
+ (new Date().getTime() - t1));
}
}
Also under src/META_INF folder I have created aop.xml file
<aspectj>
<aspects>
<aspect name="oata.aspect.AspectJTest" />
</aspects>
<weaver>
<include within="oata.*" />
</weaver>
</aspectj>
Then from the command line when I try running the Test.java using the below command the System.out.println in the advice does not get printed-
\TestAspectJ\bin>java -javaagent:D:\Project\workspaces\RCS_3.2.1\TestAspectJ\src\aspectjweaver-1.6.10.jar oata.Test
Can anyone please let me know what is it that I am doing wrong.
Thanks
AA
Few things:
Is your META-INF/* folder definitely being copied to your bin folder where you are running the app from?
You are specifying an include of oata.*, that will only include direct classes in the oata package, if you want further sub packages (and I think you do) you need oata..*
Have you tried specifying weaver options="-verbose" - does that show you anything? If it shows you nothing the aop.xml file is not being found. If it does show you something it will tell you which aspects are being turned on. Perhaps then augment it with -debug to see more about what is going on.
I might include a !within(AspectJTest) complaint to your pointcut or you could end up self advising and failing with a stack overflow when it does start working.
Finally, I know you aren't using it now but if you intend to use that annotation for matching with AspectJ you will need to change it from SOURCE retention because AspectJ works at the byte code level and won't see if it has source retention.
Andy is right with everything he said. Because you seem to be a beginner in AspectJ as well as Java, I have refactored your sample code a bit in order to help you get started. Things I noticed along the way:
You use a very old AspectJ version 1.6.10. It is from 2010 and not even the latest 1.6 version (which would be 1.6.12). How about using the current AspectJ version 1.8.6?
I am a clean code guy and noticed that your class names are rather obfuscating what you want to demonstrate with the sample code. So I renamed them:
Test → Application
TestAspect → Helper
AspectTest → MyAnnotation
AspectJTest → MethodInterceptor
I also changed the Helper methods' return types so as to return something other than void in order to demonstrate the next issue.
Your #Around advice has a return type of void. This way it does not work if the pointcut hits a non-void method. I changed the return type to Object and the code to return the result of proceed() in order to show how this can be done in a more generic way.
Your #Around advice always logs the same message. I updated it to log the actual joinpoint info (before and after the proceed() call) so we can see what is happening on the console log.
As Andy said, obviously you plan to use the annotation in order to match annotated methods with a pointcut. Thus, I changed the retention scope to RUNTIME.
Your pointcut targets all method executions including Application.main and Helper.doItWithout. I changed the pointcut to only target methods either bearing #MyAnnotation or with a substring "Aspect" in their method name.
You seem to wish to profile method execution times and compare methods with aspects applied to methods not targeted by aspects. Instead of creating lots of Date instances and calling new Date().getTime() (returns milliseconds) you can just use System.nanoTime() (returns nanoseconds).
When profiling, you want to measure method execution time, not object creation time. Thus, I changed the code to just create one Helper instance which is then reused throughout the main method.
The Application class does not need an empty default constructor because it will be generated automatically by the JVM.
In order to get meaningful profiling results you should use a bigger number of repetitions (such as a million). I introduced a constant named LOOP_COUNT in order to simplify this for all three loops.
Attention! If you want to measure method execution times you should not print anything in your aspect because then you would also be measuring the time it takes to write something to the console. Thus, I have commented out the printing statements in the aspect. You can still activate them for smaller numbers of repetitions in order to see what is going on.
Refactored code:
package oata;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Target(value = ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnnotation {}
package oata;
import oata.MyAnnotation;
public class Helper {
public double doItWithout(int i) {
return Math.acos(i);
}
#MyAnnotation
public double doItAnnotated(int i) {
return Math.acos(i);
}
public double doItAspect(int i) {
return Math.acos(i);
}
}
package oata;
public class Application {
private static final int LOOP_COUNT = 100000000;
public static void main(String arg[]) {
Helper helper = new Helper();
System.out.printf(
"Profiling statistics for %,d repetitions%n%n",
LOOP_COUNT
);
long startTime = System.nanoTime();
for (int i = 0; i < LOOP_COUNT; i++)
helper.doItWithout(i);
System.out.printf(
"Method not targeted by aspect:%n %,15d ns%n",
System.nanoTime() - startTime
);
startTime = System.nanoTime();
for (int i = 0; i < LOOP_COUNT; i++)
helper.doItAnnotated(i);
System.out.printf(
"Method targeted by aspect because it is annotated:%n %,15d ns%n",
System.nanoTime() - startTime
);
startTime = System.nanoTime();
for (int i = 0; i < LOOP_COUNT; i++)
helper.doItAspect(i);
System.out.printf(
"Method targeted by aspect because of its name:%n %,15d ns%n",
System.nanoTime() - startTime
);
}
}
package oata.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
#Aspect
public class MethodInterceptor {
#Around("execution(#oata.MyAnnotation * *(..)) || execution(* *Aspect*(..))")
public Object around(ProceedingJoinPoint jp) throws Throwable {
// System.out.println("BEFORE " + jp);
Object result = jp.proceed();
// System.out.println("AFTER " + jp);
return result;
}
}
Sample console log for 1 repetition with aspect log statements enabled:
Profiling statistics for 1 repetitions
Method not targeted by aspect:
153.893 ns
BEFORE execution(double oata.Helper.doItAnnotated(int))
AFTER execution(double oata.Helper.doItAnnotated(int))
Method targeted by aspect because it is annotated:
3.102.128 ns
BEFORE execution(double oata.Helper.doItAspect(int))
AFTER execution(double oata.Helper.doItAspect(int))
Method targeted by aspect because of its name:
55.295 ns
As you can see here, the results are not very conclusive with just one call per method.
Sample console log for 100,000,000 (a hundred million) repetitions with aspect log statements disabled:
Profiling statistics for 100.000.000 repetitions
Method not targeted by aspect:
843.407.034 ns
Method targeted by aspect because it is annotated:
1.219.571.173 ns
Method targeted by aspect because of its name:
1.175.436.574 ns
Now the result is more conclusive: The method not targeted by any aspect is executed more quickly than the next two methods which have about equal execution time of 1.2 seconds, which was to be expected because the pointcuts used can be determined statically during compilation time (for CTW) or weaving time (for LTW).
Related
ORIGINAL QUESTION:
I have a method annotated with two aspects:
#Aspect1Annotation
#Aspect2Annotation
public SomeResult handle() {
// some code
return null;
}
I would like the first annotaion to execute and based on its results prevent execution of the second aspects logic.
How to do it with AspectJ? Please help.
EDIT:
I am developing an application which has multiple methods with these annotations:
#Monitored
#Secured(Permission.SOME_PERMISSION)
#Audited
public SomeResult handle() {
// some code
return null;
}
The MonitoringAspect logic must always run despite security permissions.
The permission check should be done after the MonitoringAspect and if access is denied, a runtime exception should be thrown.
AuditAspect logic should only be executed if access is allowed.
You want to use a combination of
#Around advice in your aspects and
#DeclarePrecedence for declaring aspect precedence.
This way, you can chain advice execution exactly the way you want to and dynamically decide whether each advice should
proceed to the next advice in the chain depending on its precedence (or to the target method, if there is no more advice in the chain), or
not to proceed but return a result calculated by the advice itself, or
to throw an exception instead of returning something.
Of course, you can combine these things, e.g. proceed but discard or modify the result or proceed, but depending on the result still throw an exception.
If you just want to decide to either throw an exception or just pass through the call and its result unchanged, depending on authorisation status, of course a simple #Before advice will also do and #Around is unnecessary.
Here is a simple example with 2 aspects, which you can easily adapt to your situation. Feel free to ask directly related follow-up questions in comments, if anything is unclear about the sample code.
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
System.out.println(" " + doSomething());
}
}
public static String doSomething() {
return "doing something";
}
}
import java.util.Random;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
#Aspect
public class FirstAspect {
private static final Random RANDOM = new Random();
#Around("execution(String doSomething())")
public Object myAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("FirstAspect");
switch (RANDOM.nextInt(3)) {
// Do not proceed to 2nd aspect, create own return value
case 0: return "1st aspect";
// Proceed to 2nd aspect, modify response
case 1: return joinPoint.proceed() + " - 1st aspect";
// Proceed to 2nd aspect, return response unchanged
default: return joinPoint.proceed();
}
}
}
package de.scrum_master.aspect;
import java.util.Random;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclarePrecedence;
#Aspect
#DeclarePrecedence("FirstAspect, SecondAspect")
public class SecondAspect {
private static final Random RANDOM = new Random();
#Around("execution(String doSomething())")
public Object myAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("SecondAspect");
switch (RANDOM.nextInt(3)) {
// Do not proceed to target method, create own return value
case 0: return "2nd aspect";
// Proceed to target method, but modify return value
case 1: return joinPoint.proceed() + " - 2nd aspect";
// Proceed to target method, return response unchanged
default: return joinPoint.proceed();
}
}
}
The console log might look like this:
FirstAspect
SecondAspect
doing something - 2nd aspect - 1st aspect
FirstAspect
1st aspect
FirstAspect
SecondAspect
2nd aspect - 1st aspect
FirstAspect
SecondAspect
doing something - 1st aspect
FirstAspect
1st aspect
FirstAspect
SecondAspect
doing something
FirstAspect
1st aspect
FirstAspect
SecondAspect
doing something - 1st aspect
FirstAspect
1st aspect
FirstAspect
SecondAspect
doing something - 2nd aspect
You can see how sometimes the result is passed through unchanged by both aspects, sometimes only one of the aspect or both aspects modify the result or how sometimes the second aspect is not even called, because the first one does not proceed. Instead of not proceeding, throwing an exception would also be possible.
Is there a way to give the java compiler some kind of variable that is accessible to the running java code?
In C/C++ I can give the compile -DKEY=VALUE and that would cause the preprocessor to have a #define for KEY equals to VALUE. I can then check this value in compile time to effect what code is being compiled.
I found java's -D, but that puts values give the the java command line in System.getProperty(). I want an argument give in compile time, not invocation time.
javac has the
-Akey[=value]
commandline option to pass information to annotation processors.
With java annotations it is possible to generate additional code on the fly, which can be configured on command line. It allow to produce more source code, configuration files, xml files, ... The main limitation is that you are allowed only to (re)generate new source files, you cannot modify existing ones.
Below is a short tutorial on how to allow from javac command specify parameters which will be visible in Java code. How usefull is that? Ie. you could specify a boolean option which would disable some parts of code, I am preety sure this parts of code could be removed using tools like proguard - or even optimized out by javac. Other uses is to specify new version number. Those use cases are mostly what c++ marcros are used for.
So, you need :
a dummy annotation class which will allow processor to run. It should be specified only once in your application.
a processor class which will run for above dummy annotation, and generate options class. It will also read options from javac command line.
a dummy Main class for testing purposes.
You will have to also compile your processor file before compiling Main class. This of course is done only when processor class is modified. All the three files are at the bottom. Now the compilation looks as follows (I am on windows):
Compile processor:
javac .\com\example\ConfigWritterAnnotationProcessor.java
Then Main.java with additional parameters to processor:
javac -processor com.example.ConfigWritterAnnotationProcessor -AtextToPrint="Hello World!" -AenablePrint=true ./com/example/Main.java
And thats all, now you may run Main.class and it will use Options class generated during compilation with above parameters set. It will look as follows:
package com.example;
public class Options {
public static final String textToPrint = "Hello World!";
public static final boolean enablePrint = true;
}
ProcessorStarterAnnotation.java
package com.example;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Target({ElementType.TYPE})
public #interface ProcessorStarterAnnotation {
}
Main.java
package com.example;
#ProcessorStarterAnnotation
public class Main {
public static void main(String[] args) {
if ( com.example.Options.enablePrint ) {
System.out.println(com.example.Options.textToPrint);
}
else {
System.out.println("Print disabled");
}
}
}
ConfigWritterAnnotationProcessor.java
package com.example;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.Map;
import java.util.Set;
#SupportedAnnotationTypes("com.example.ProcessorStarterAnnotation")
#SupportedSourceVersion(SourceVersion.RELEASE_6)
#SupportedOptions({"textToPrint", "enablePrint"})
public class ConfigWritterAnnotationProcessor extends AbstractProcessor {
private Map<String,String> options;
#Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
options = processingEnv.getOptions();
}
#Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment currentRound) {
if (!currentRound.processingOver()) {
// This for-s are because processor is also run on newly created Options class.
for (TypeElement te : annotations) {
for (Element e : currentRound.getElementsAnnotatedWith(te)) {
try {
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Creating com.example.Options");
JavaFileObject javaFile = processingEnv.getFiler().createSourceFile("com.example.Options");
Writer w = javaFile.openWriter();
try {
PrintWriter pw = new PrintWriter(w);
pw.println("package com.example;");
pw.println("public class Options {");
pw.println(" public static final String textToPrint = \"" + options.get("textToPrint") + "\";");
pw.println(" public static final boolean enablePrint = " + options.get("enablePrint") + ";");
pw.println("}");
pw.flush();
} finally {
w.close();
}
} catch (IOException x) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
x.toString());
}
}
}
}
return false;
}
}
There is nothing like this in Java. Compile time constants must be declared in source code, and as far as I know, there is no pre-processor.
BTW, you could use flags given to java command line (-D args) to initialize java constants at runtime, that would mimic what you are looking for.
Ex:
class Foo {
private static final String BAR;
static {
String foobar = System.getProperty("foo.bar");
if(foobar != null && foobar.length()>0) {
BAR = foobar;
} else {
BAR = "somedefaultvalue";
}
}
}
Invoke with java Xxx -Dfoo.bar=foobar
As there is no notion of preprocessing in Java, a solution is to design your own.
One may think of using a standard C preprocessor or a custom-made one and compile the preprocessed output, but this has the disadvantage of duplicating the files, so that the project will become more complex, and the support from the development environment will degrade (like ability to jump to a syntax error).
My suggestion is to use annotations via comments that will guide a custom preprocessor and let it do substitutions before compiling.
For example,
public static void main(String[] args) {
int nDisks = 3;
doTowers(nDisks, 'A', 'B', 'C');
}
would become
public static void main(String[] args) {
int nDisks = /*#NDISKS*/ 3 /**/;
doTowers(nDisks, 'A', 'B', 'C');
}
Then your preprocessor would have a definition file such as
NDISKS 5
turning the code in
public static void main(String[] args) {
int nDisks = /*#NDISKS*/ 5 /**/;
doTowers(nDisks, 'A', 'B', 'C');
}
Similarly, you can emulate conditional code compilation with
doTowers(topN - 1, from, to, inter);
/*!PRINT*/
System.out.println("Disk "
+ topN + " from " + from + " to " + to);
/**/
doTowers(topN - 1, inter, from, to);
which could be turned by the preprocessor (with a definition like PRINT OFF) into
doTowers(topN - 1, from, to, inter);
/*!PRINT
System.out.println("Disk "
+ topN + " from " + from + " to " + to);
*/
doTowers(topN - 1, inter, from, to);
You can use alternative syntaxes, but the main ideas are
that the annotated code remains compilable,
that the preprocessor substitutions are reversible.
It would be against the design of the language to have it that way. What -DKEY=VALUE does is that it actually replaces KEY with VALUE in the source during preprocessor in C/C++.
Java does not have a preprocessor so that's mechanism is not available. If you want something "equivalent" you have to question what you mean by that. By not preprocessing the source it wouldn't be really equivalent.
If you on the other hand would like it to mean to set the value of the symbol KEY to the value VALUE you'd run into the problem that you would need to declare the symbol KEY anyway to determine it's type. In this case it would only be yet another constant/variable with the constraints that implies.
This means that even with such a feature it would not actually alter the generated code and you would hardly be better of than defining the value att launch time. That's why supplying the parameter via java would be the way to go.
I've got a problem which is kinda obvious, though I'm not sure how to solve it.
I've got 2 classes, 1 of which is Interceptor.
#Stateless
#Interceptors(AutoId.class)
public class TestClass {
private static final Logger LOG = Logger.getLogger(RepositoryBean.class.getName());
public void executeUpdate(){
int k=0;
for (int i = 0; i < 1000000; i++) {
for (int j = 0; j < 100000; j++) {
for (int r = 0; r < 1000000; r++) {
k = 1;
}
}
}
getLogger().log(Level.INFO, "Current time some time ago was "+AutoId.MyTime/1000);
}
private Logger getLogger() {
return Logger.getLogger(getClass().getCanonicalName());
}}
and here is Interceptor class:
public class AutoId {
public static Long MyTime;
#AroundInvoke
public Object addLog(InvocationContext context) throws Exception {
MyTime= System.currentTimeMillis();
return context.proceed();
}
}
an obvious problem is that if I run this application (when it's deployed on a glassfish server) and then in a couple of seconds I run another copy of it, it is going to rewrite MyTime variable with new time and, as a result, both programs will print same time.
One of the obvious solutions is to make a variable inside executeUpdate which will save the value of MyTime, BUT this is not good for the real project I'm working on.
I was told that I might want to do something with ContextResolver and #Context.
Any thoughs on how do I solve this?
Thanks.
EDIT
I found one solution, though I don't think it is the best
public class AutoId {
private static Long[] MyTime = new Long[1000];
#AroundInvoke
public Object addLog(InvocationContext context) throws Exception {
MyTime[(int)Thread.currentThread().getId()]= System.currentTimeMillis();
return context.proceed();
}
public static Long MyTime(){
return MyTime[(int)Thread.currentThread().getId()];
}
}
naming array the same way as procedure allows to minimize code changes in main class only by adding () after AutoId.MyTime -> AutoId.MyTime()
That's still not the best Idea, though it doesn't cause rewriting of variable anymore.
EDIT2 please don't really mind all the code in executeUpdate() procedure. It is just written in a way it takes some tome to finish working, so that I can execute 1 more copy of it and print out AutoId.MyTime. The value of this variable is the only thing that matters.
Also it's qute obvious that if I wasn't using Interceptor and just created an AutoId variable within class to call it before any other procedure (that's what interceptors for) that error wouldn't appear since every copy of program will have its own id easily - that's not option though. Interceptors are required for autorisation here before executing any procedure. Hope that explains everything I haven't told before :)
You could use #Produces for logger creation and use then #Inject to inject your logger in your class and interceptor. This way you should log different times.
How is it possible, to improve your logging mechanism, by not having the overhead of string concatenations?
Consider the following example:
import java.util.logging.Level;
import java.util.logging.Logger;
public class LoggerTest {
public static void main(String[] args) {
// get logger
Logger log = Logger.getLogger(LoggerTest.class.getName());
// set log level to INFO (so fine will not be logged)
log.setLevel(Level.INFO);
// this line won't log anything, but will evaluate the getValue method
log.fine("Trace value: " + getValue());
}
// example method to get a value with a lot of string concatenation
private static String getValue() {
String val = "";
for (int i = 0; i < 1000; i++) {
val += "foo";
}
return val;
}
}
The log method log.fine(...) will not log anything, because the log level is set to INFO. The problem is, that the method getValue will be evaluated anyway.
And this is a big performance issue in big applications with a lot of debug statements.
So, how to solve this problem?
Since Java8 it is possible to use the new introduced lambda expressions for this scenario.
Here is a modified example of the logging:
LoggerTest.class
import java.util.logging.Level;
import java.util.logging.Logger;
public class LoggerTest {
public static void main(String[] args) {
// get own lambda logger
LambdaLogger log = new LambdaLogger(LoggerTest.class.getName());
// set log level to INFO (so fine will not be logged)
log.setLevel(Level.INFO);
// this line won't log anything, and will also not evaluate the getValue method!
log.fine(()-> "Trace value: " + getValue()); // changed to lambda expression
}
// example method to get a value with a lot of string concatenation
private static String getValue() {
String val = "";
for (int i = 0; i < 1000; i++) {
val += "foo";
}
return val;
}
}
LambdaLogger.class
import java.util.concurrent.Callable;
import java.util.logging.Level;
import java.util.logging.Logger;
public class LambdaLogger extends Logger {
public LambdaLogger(String name) {
super(name, null);
}
public void fine(Callable<String> message) {
// log only, if it's loggable
if (isLoggable(Level.FINE)) {
try {
// evaluate here the callable method
super.fine(message.call());
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
With this modification you can improve the performance of your applications a lot, if you have many log statements, which are only for debugging purposes.
Of course you can use any Logger you want. This is only an example of the java.util.Logger.
#bobbel has explained how to do it.
I'd like to add that while this represents a performance improvement over your original code, the classic way of dealing with this is still faster:
if (log.isLoggable(Level.FINE)) {
log.fine("Trace value: " + getValue());
}
and only marginally more verbose / wordy.
The reason it is faster is that the lambda version has the additional runtime overheads of creating the callable instance (capture cost), and an extra level of method calls.
And finally, there is the issue of creating the LambdaLogger instances. #bobbel's code shows this being done using a constructor, but in reality java.util.logging.Logger objects need to be created by a factory method to avoid proliferation of objects. That implies a bunch of extra infrastructure (and code changes) to get this to work with a custom subclass of Logger.
Apparently Log4j 2.4 includes support for lambda expressions which are exactly useful for your case (and which other answers have replicated manually):
From https://garygregory.wordpress.com/2015/09/16/a-gentle-introduction-to-the-log4j-api-and-lambda-basics/
// Uses Java 8 lambdas to build arguments on demand
logger.debug("I am logging that {} happened.", () -> compute());
Just create wrapper methods for your current logger as:
public static void info(Logger logger, Supplier<String> message) {
if (logger.isLoggable(Level.INFO))
logger.info(message.get());
}
and use it:
info(log, () -> "x: " + x + ", y: " + y);
Reference: JAVA SE 8 for the Really Impatient eBook, pages 48-49.
use a format String, and an array of Supplier<String>. this way no toString methods are called unless the the log record is actually publishable. this way you dont have to bother with ugly if statements about logging in application code.
Does anyone know if there is a way to generate different code in the catch block automatically depending on the exception?
The Eclipse function 'Surround with try/catch' generates a try/catch block which just includes dumping a stack trace.
I'm doing a bunch of similar things in the code and so most of my exceptions will boil down to probably three or so different types. I'd like to have different catch block code for each one and have eclipse auto format based on the exception.
For example:
if my code generates a RemoteConnectionException I'd like to display a dialog to the user to reconnect.
If it generates a RemoteContentException I'd like to log it.
(I made these up.)
Thanks in advance
UPDATE:
I've been poking around and have two potential solutions.
1) I've found something called the fast code plugin which might do what I'm looking for.
http://fast-code.sourceforge.net/index.htm
2) For specifically handling exceptions I'll probably just write a generic exception handler and modify the catch block code to pass the exception to that instead of printing the stack trace. Then the java code will determine which action to take based on exception type.
Templating has it's limits. However your problem can be solved very elegantly with Aspect. ( http://www.eclipse.org/aspectj/ ) Just create a new annotation for every type of "template-case" you need and use an around advice.
Ps: don't use printStackTrace() to syserr/sysout. There are so many production grade, lightweight logging frameworks.... pleeeaseee... don't abuse poor little System.out/err :)
EDIT:
Some example for a logging / benchmarking advice. (note: I'm using spring AOP for aspects, and lombok for easy access to the logging framework. The getCurrentUser() code is not really relevant here, it's just for getting the current user from Spring Security)
package com.XXXXXXXX.aspects;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
#Component
#Aspect
#Slf4j
public class LoggerAspect {
private final static String DOMAIN = "XXXXXXXX";
private static String getCurrentUser() {
String username = "Unknown";
try {
Object principal = SecurityContextHolder.getContext().
getAuthentication().
getPrincipal();
if (principal instanceof UserDetails) {
username = ((UserDetails) principal).getUsername();
} else {
username = principal.toString();
}
} catch (Exception e) {
}
return username;
}
#Pointcut("within(com.XXXXXXXX.services..*)")
public void inServiceLayer() {
}
#Pointcut("execution(* getMatcherInfo(..)) || execution(* resetCounter(..))")
public void notToAdvise() {
}
#Around("com.XXXXXXXX.aspects.LoggerAspect.inServiceLayer() && !com.XXXXXXXX.aspects.LoggerAspect.notToAdvise()")
public Object doLogging(ProceedingJoinPoint pjp)
throws Throwable {
long start = System.nanoTime();
StringBuilder sb = new StringBuilder(DOMAIN);
sb.append('/').
append(getCurrentUser()).
append(" accessing ").
append(pjp.getSignature().
getDeclaringTypeName()).
append('.').
append(pjp.getSignature().
getName());
log.trace("START: " + sb.toString());
Object retVal = pjp.proceed(pjp.getArgs());
long duration = System.nanoTime() - start;
log.trace("STOP: " + duration / 1000000 + " msec. " + sb.toString());
return retVal;
}
}
I'm not sure if there is such an option available in eclipse. I've been using the surround with try/catch option for quite sometime now and it always dumps the default e.printStackTrace() line in the catch block for the Exception e.