can annotation get context object? - java

Maybe title "can annotation get context object?" is not correct, but I don't know how to give it a right and clear one.
I use Spring AOP + Java Annotation to save log, here is my code:
CategoryAction.java :
#ServiceTracker(methodDesp="save category, category name:"+this.category.getName())
public String save() throws Exception
{
this.categoryService.save(this.category);
this.setJsonDataSimply(null);
return "save";
}
TrackAdvice.java :
public Object trackAround(ProceedingJoinPoint point) throws Throwable
{
String log = "success";
ServiceTracker tracker = null;
Method method = null;
AbstractAction action = null;
try
{
Object result = point.proceed();
action = (AbstractAction) point.getTarget();
MethodSignature signature = (MethodSignature) point.getSignature();
method = signature.getMethod();
tracker = method.getAnnotation(ServiceTracker.class);
return result;
}
catch (Exception e)
{
log = e.getMessage();
throw e;
}
finally
{
if (tracker != null)
{
String userId = (String) ActionContext.getContext().getSession().get(Constant.USERID);
if (userId == null)
{
userId = "unknown";
}
TrackLog t = new TrackLog();
t.setWhen(new Date());
t.setUserId(userId);
t.setResult(log);
t.setMethodName(action.getClass().getCanonicalName() + "." + method.getName());
t.setMethodDesp(tracker.methodDesp());
this.trackService.save(t);
}
}
}
ServiceTracker is my own annotation, in my TrackAdvice class, I get the current executing method, if the method has a ServiceTracker annotation, then save the methodDesp in annotation to database.
Now the question is the methodDesp in annotation is dynamic, I want to get this object and retrieve its category property.
It seems that Java Annotation doesn't support this, maybe it supports but I don't know how.

What you can do is use some sort of expression language in the annotation value and then run some interpreter in your advice code. One example using SPEL could look like this:
#ServiceTracker(methodDesp="save category, category name: #{category.name}")
And in your advice code, you can then extract the expression token, make use of a SpelExpression and pass it the target reference as root object (you may want to check what's available out of the box in the SPEL API for supporting your use-case(s)).

It seems that Java Annotation doesn't support this
You are correct - there is no way to do this in pure java.
The reason is that because annotations are static metadata that is wired into classes and defined at compile-time (this start to exist only at run-time, not compile-time).
In other words there is no straightforward way to make methodDesp of some annotated method of some class dynamic, since it's value has to be resolved statically, at compile-time.
However, technically there is a way to do something like you want. What I talk about is using javassist to either manipulate or create your classes (and annotations applied to them) at runtime. But be warned that this is rather hacky way and I generally would not recommend to go there.

Related

Avoid two passes over a list of validators

I am validating the parameters passed to a series of commands in a file using the following code:
for (Parameter p : s.getCommand(idx).getParameters()) {
for (ValidationFactory.TYPES validationType : ValidationFactory.TYPES.values()) {
validator = ValidationFactory.getValidator(validationType, errors);
try {
validator.validate(p);
} catch (ValidationException e) {
Report.logErrorMessage("Failed to validate: " + validationType);
continue;
}
}
}
Then in the ValidationFactory I have:
public final class ValidationFactory {
public enum TYPES {
PROPERTIES,
PORTS
};
private ValidationFactory() {
}
public static AbstractValidator getValidator(TYPES validationType,
ValidationErrors errors) {
switch (validationType) {
case PROPERTIES:
return new PropertiesValidator(errors);
case PORTS:
return new PortRangeValidator(errors);
default:
return null;
}
}}
This code works really nicely and allows for new validators to be added at a later date. There is one relatively minor problem though...
The outer for loop iterates over a list of parameters that will be passed to the command, while the inner for loop iterates over a list of validators which can do the validation. Depending on the parameter however, it may not be necessary to continue the validation with the second validator, the first one may have already done the work... So, PropertiesValidator might have done the work needed, and now there is no need to call the second validator, but it is going to call it anyway. I guess I could use a variable to maintain validation state, and then it could skip if already done.. both validators extend an AbstractValidator class which would be the best place for this.
I would like to do the validation in one pass while keeping the structure of the Factory pattern. I was thinking of putting in some sort of delegator class.. I am using java 1.6 so I can't switch on string arguments which would be nice.
Define a Generic Validator, which is going to be common to all the validator, and define specific validation in properties and port validation. So now there is no duplication of validation by moving common logic into generic validator and specific validation in others.

Convert empty string to null using javax.validation annotations

I have the following variable annotated for data validation:
#Size(min=8, max=16, message="the size of the parameter must be between 8 and 16")
private String param;
However, the param can be null. It is required that it be 8-16 chars long only if it is not null. The problem I face is if the client app (JSON API) supplies an empty string, I want to treat it as though it were not supplied at all, i.e. is null. I was wondering if there is an elegant way to do this using the javax.validation annotations, i.e. convert an empty string to null, as opposed to the plain Java way the way I'm doing it right now:
public void setParameter(String _param) {
if(_param != null && !_param.trim().isEmpty()){
this.param = _param;
} else {
this.param = null;
}
}
I would like to have a very simple setter:
public void setParameter(String _param) {
this.param = _param;
}
and have the is-empty-string boilerplate done by an annotation. Is there a way to do it?
You could can implement your own custom constraint validator.
see here. I've used this many times and works like a charm.
https://docs.jboss.org/hibernate/validator/5.0/reference/en-US/html/validator-customconstraints.html
You would just need to set this condition (if null return "" or vice-versa) in the isValid method.

NoSuchFieldException on getClass().getField()

java.lang.NoSuchFieldException: id
The below line is creating the exception.
String fieldValue =String.valueOf(studyplanCategory.getClass().getField(filterProperty).get(studyplanCategory));
studyplanCategory is a valid object and has got actual values. Beacuse of this exception the load method and the search function in the LazyLoading DataTable of my JSF webapp is not working.
From the Javadoc for Class.getField(...):
Returns a Field object that reflects the specified public member field
of the class or interface represented by this Class object. The name
parameter is a String specifying the simple name of the desired field.
The field to be reflected is determined by the algorithm that follows.
Let C be the class represented by this object:
If C declares a public field with the name specified, that is the
field to be reflected. If no field was found in step 1 above, this
algorithm is applied recursively to each direct superinterface of C.
The direct superinterfaces are searched in the order they were
declared. If no field was found in steps 1 and 2 above, and C has a
superclass S, then this algorithm is invoked recursively upon S. If C
has no superclass, then a NoSuchFieldException is thrown. See The Java
Language Specification, sections 8.2 and 8.3.
If the field you are trying to retrieve via:
studyplanCategory.getClass().getField(filterProperty)
is private, then you will get a NoSuchFieldException. For private fields, try this instead:
studyplanCategory.getClass().getDeclaredField(filterProperty)
And to get around potential illegal access exceptions when setting values via a field this way:
Field field = studyplanCategory.getClass().getDeclaredField(filterProperty);
field.setAccessible(true);
field.get(studyplanCategory);
App fires up this exception because its doesn't see attribudes your want to give back.
Method getField() return non-private attribudes so if your attribudes are private, method doesn't see them. You can check http://docs.oracle.com/javase/tutorial/reflect/member/fieldTrouble.html
So you can do that your attribudes will change on protected or public and then should work it right. But this way (its same like example on primefaces) simulate real database.
public List<Car> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String,String> filters) {
List<Car> data = new ArrayList<Car>();
//filter
for(Car car : datasource) {
boolean match = true;
for(Iterator<String> it = filters.keySet().iterator(); it.hasNext();) {
try {
String filterProperty = it.next();
String filterValue = filters.get(filterProperty);
String fieldValue = String.valueOf(car.getClass().getField(filterProperty).get(car));
...
So this list simulate real database only for example. If you want to use it. so you shoud do it on backing bean class and there do it. You open connection already with some filter or don't and then return data from database.
//EDIT: Man wrote that you should use getDeclaredField() but i did try this and it didn't work well, and throws up IlegalAccessException. When a pretype attribudes to protected, it works fine. I don't know why.
Best solutions for getClass().getField() problem are:
Use getDeclaredField() instead of getField()
1)
String propertyName = "test";<br/>
Class.forName(this.getClass().getName()).getDeclaredField(propertyName);
2)
String propertyName = "name";<br/>
Replace **"HelloWorld"** with your class name<br/>
HelloWorld.class.getDeclaredField(propertyName)
I had faced the same problem. My issue was my variables are not public . Make sure your class variables are public
private Object getVariableValue(Object clazz, String variableName) {
try {
Field field = clazz.getClass().getField(variableName);
return field.get(clazz);
} catch (NoSuchFieldException e) {
return "";
} catch (IllegalAccessException e) {
return "";
}
}
I agree that we should Use getDeclaredField() instead of getField()
private Field getOwnProperty(Object clazz, String propertyName) {
try {
return clazz.getClass().getDeclaredField(propertyName);
} catch (NoSuchFieldException e) {
log.warn( "Object has no property : " + propertyName );
return null;
}
}

servlets checking params

So I have a big long query string that can either be ...
//url=z&surl=y&time=z&codec=a264&acodec=mp3&width=400x100
or
//url=z&surl=y&time=z&optlevel=w
Im using request.getQueryString("url") to check if a) the qs is there and b) make sure its not null. This is all leading to a big messy set of if statements. I was just wondering if there is a better way to do it.
example..
if(request.getParameter("originalURL") != null &&
request.getParameter("originalURL").equals("") && ................)
Thanks guys
Sure, just refactor the duplicated code into methods or make use of an existing framework.
Basic kickoff example of refactored code:
String field1 = getField(request, "field1", true);
String field2 = getField(request, "field2", true);
String field3 = getField(request, "field3", false);
...
public static String getField(HttpServletRequest request, String fieldName, boolean required) throws ValidatorException {
String fieldValue = request.getParameter(fieldName);
if (fieldValue == null || fieldValue.trim().isEmpty()) {
if (required) {
throw new ValidatorException("Field is required");
} else {
fieldValue = null; // Make empty string null so that you don't need to hassle with equals("") afterwards.
}
}
return fieldValue;
}
You can of course go a step further and adopt an existing MVC framework with validation (and conversion) capabilities, such as Sun JSF or Apache Struts.
I don't know if you are using any framework but, as other mentioned, most of them are providing utility classes for this purpose. If you aren't, you should maybe create such a class.
Personally, I like Spring's ServletRequestUtils which exposes several strong typed static methods to get parameters from the request, allowing fallback values and checking for required parameters. If I had to code something equivalent (sigh), I'd mimic this class.
Frameworks such as JSF and Struts offer nicer abstractions for dealing with requests. When I do work with the raw Servlet APIs I use a little utility library to deal with this, and also parsing ints and dates etc.
getStringParam( request, "originalUrl" ) {}
which would throw an exception if the param is not found, or more often I use a varient that provides a default value if the param is missing:
getStringParam (request, "origanlUrl", "http://someusefulDefault") {}
getIntParam(request, "howManyRivers", 93);
if("something".equals(request.getParameter("originalURL")))
No need for null checks, because equals will always return false if you pass a null as atribute

Java annotations

I've created simple annotation in Java
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface Column {
String columnName();
}
and class
public class Table {
#Column(columnName = "id")
private int colId;
#Column(columnName = "name")
private String colName;
private int noAnnotationHere;
public Table(int colId, String colName, int noAnnotationHere) {
this.colId = colId;
this.colName = colName;
this.noAnnotationHere = noAnnotationHere;
}
}
I need to iterate over all fields, that are annotated with Column and get name and value of field and annotation. But I've got problem with getting value of each field, since all of them are of different data type.
Is there anything that would return collection of fields that have certain annotation?
I managed to do it with this code, but I don't think that reflection is good way to solve it.
Table table = new Table(1, "test", 2);
for (Field field : table.getClass().getDeclaredFields()) {
Column col;
// check if field has annotation
if ((col = field.getAnnotation(Column.class)) != null) {
String log = "colname: " + col.columnName() + "\n";
log += "field name: " + field.getName() + "\n\n";
// here i don't know how to get value of field, since all get methods
// are type specific
System.out.println(log);
}
}
Do I have to wrap every field in object, which would implement method like getValue(), or is there some better way around this? Basicly all I need is string representation of each field that is annotated.
edit: yep field.get(table) works, but only for public fields, is there any way how to do this even for private fields? Or do I have to make getter and somehow invoke it?
Every object should has toString() defined. (And you can override this for each class to get a more meaningful representation).
So you where your "// here I don't know" comment is, you could have:
Object value = field.get(table);
// gets the value of this field for the instance 'table'
log += "value: " + value + "\n";
// implicitly uses toString for you
// or will put 'null' if the object is null
Reflection is exactly the way to solve it. Finding out things about types and their members at execution time is pretty much the definition of reflection! The way you've done it looks fine to me.
To find the value of the field, use field.get(table)
Reflection is exactly the way to look at annotations. They are a form of "metadata" attached to the class or method, and Java annotations were designed to be examined that way.
Reflection is one way to process the object (probably the only way if the fields are private and don't have any kind of accessor method). You'll need to look at Field.setAccessible and perhaps Field.getType.
Another approach is to generate another class for enumerating the annotated fields using a compile-time annotation processor. This requires a com.sun API in Java 5, but support is better in the Java 6 JDK (IDEs like Eclipse may require special project configuration).

Categories

Resources