Free Online Courses for Software Developers - MrBool
× Please, log in to give us a feedback. Click here to login
×

You must be logged to download. Click here to login

×

MrBool is totally free and you can help us to help the Developers Community around the world

Yes, I'd like to help the MrBool and the Developers Community before download

No, I'd like to download without make the donation

×

MrBool is totally free and you can help us to help the Developers Community around the world

Yes, I'd like to help the MrBool and the Developers Community before download

No, I'd like to download without make the donation

Creating a Custom Expression Language in Java

See in this article how to create your own Custom Expression Language in Java.

Large companies seek to find a way to gain more productivity and organization, something that always tries to be achieved, but there is always something to be improved: this is a fact. They can even use CASE tools, but they forget that the way the project is developed is one of the most critical points for achieving this goal. So there is no point to all the correct planning, adopting agile and well-defined documents, if we have a form of sustained development in archaic outdated theories.

Nowadays, the development speed depends heavily on standardization adopted rather than simply "spit" code without any predefined pattern.

This article will deal with creating a mechanism that works much like the Expression Language of Java EE. Our goal is to lower the coupling level, i.e, the dependency between elements or subsystems. Regarding the object orientation, the lower the coupling, the better the system.

What is Expression Language

Also known as EL Expression, it is an expression language used to create dynamic web pages on the Java EE platform that allows easy access to JavaBeans properties, since there is no need for deep knowledge in Java.

Anyone who has worked or works with JSF certainly has worked with EL Expressions when accessed a ManagedBean through the expression #{myManagedBean.myProperty}. We used an EL Expression to get into the managed bean and call an actionListener, action or even a property with its getter method.

Here's an example of its structure in Listing 1.

Listing 1. EL Expression example

{client.quantity * client.price} 


// Another example in JSF 
<h:commandButton actionListener=”#{myManagedBean.myAction()}” value=”Test” />

We have a simple expression that captures two properties from the client bean: the quantity and the price.

If we convert this to pure Java, we'll have the following code:

client.getQuantity() * client.getPrice();

In JSF we are required to use the EL Expression to make calls to actions and actionsListeners.

What we can see is that, in theory, each access to the property after the "." is done through a conversion to the method signature in Java, which in our case are the methods 'getQuantity' and 'getPrice'.

Below, we will understand the basics of this to begin to develop our own Expression Language.

What's wrong with using pure Java

Sometimes it can be very useful just to say what methods we would like to access and let someone else take care of making such access. On a client-server application, for example, we could send the bean via network and access the properties of this only on the server side, for performance issues or even any other logical operation. In distributed systems, this can be a very close reality, to make the customer a "dumb terminal", that is, that does not perform complex processing, only sends information to the server to process.

Another case is the abstraction layers of your system: imagine we have a standard method for performing searches in the system and this takes four arguments: the bean from which we will capture the parameters, a list of named parameters, the name of the properties that will fill the value of these parameters and the query, as in the following code:

public Object search(Object bean, String[] paramNames, String[] propertiesToParams, String query)

Imagine that the call method is the following:

search(myBean, new String[]{"pId","pData"}, new String[]{"id", "data"}, "SELECT * FROM MyBean WHERE id = :pId AND data = :pData");

Note that our method search() is generic to the point of receiving any type of object and perform the necessary search. For this, the use of an Expression Language greatly facilitate the call to the method, since only the name of the property is passed and internally the method will be responsible for capturing these values to fill in the parameters.

Another application of our Expression Language would be the initialization of the properties that we do not put with EAGER or that we haven't done a FETCH in the query. If we have a standard query for the whole system and we want to initialize the specific properties for specific beans, then the Expression Language "will fall like a glove."

Starting the development

Once we have understood the context of our problem, we can start to develop a simple Expression Language called MRExpression, from MrBool Expression, as shown in Listing 2.

Listing 2. MRExpression

package lab;


import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;


public class DMExpression {


	public static Object invoke(Object o, String qualifiedPath) throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
		int level = qualifiedPath.split("\\.").length;


		if (level == 1) {
			Method m = o.getClass().getMethod(getMethodSignature(qualifiedPath), null);
			return m.invoke(o, null);
		} else {
			Class actualClazz = null;
			for (int i = 1; i < level; i++) {
				if (o == null) {
					return " ";
				}
				actualClazz = o.getClass();
				o = actualClazz.getMethod(getMethodSignature(extractPropPerLevel(qualifiedPath, i)), null).invoke(o, null);
			}


			String finalProperty = qualifiedPath.split("\\.")[level - 1];
			Method m = o.getClass().getMethod(getMethodSignature(finalProperty), null);
			return m.invoke(o, null);
		}
	}


	private static String extractPropPerLevel(String qualifiedPath, int level) {
		return qualifiedPath.split("\\.")[level - 1];
	}


	private static String getMethodSignature(String qualifiedPath) {
		if (qualifiedPath.startsWith("!")) {
			return qualifiedPath.replace("!", "");
		} else {
			return "get" + Character.toString(qualifiedPath.charAt(0)).toUpperCase() + qualifiedPath.substring(1);
		}
	}
}

In line 5 we have the invoke() method, which is the first and only method that can be seen outside of our class.

Following the same logic of Reflection in Java, this method calls another one and the object. The name of this other method will be qualified only by the name of the properties separated by a point.

In line 9 we need to know how many levels there are in our method. For example, in "client.district" there are two levels, as it will make access to the first level "getClient()" which returns a Client object type, then the second level, which is the "getDistrict()", this being accessed by the return of getClient().

If there is only one level, then we can call the method directly, without using recursion to get into classes, as in line 13. However, we must first convert the property name to a get() method, that is exactly what the getMethodSignature() does.

With the name of the method we can then seek for the Method object through o.getClass().getMethod() and only then we can call the native invoke of Java.

Note that in line 17, when there is no point, we have a level. So to know the number of levels of the qualified name we could use the following formula:

Add Points + 1 = Levels Quantity

See that even with no points we have a level and for each new point we always have the number of points + 1.

In this case, on line 17, where we have more than one level, we began to iterate with a for loop through these.

The code snippet "if (o == null)" ensures that the loop will not continue if the return of the levels is null, so we avoid the NullPointerException.

On line 23, on the first iteration, we need to capture the value returned only of the first property before the point and, therefore, we use the extractPropPerLevel() method, passing the fully qualified path and the level. Thus, the return will be only the exact property of that level.

After that, we use the getMethodSignature() to convert this property to a valid method. Finally, we call the native invoke of Java to have the return of this method.

If there is another level, then the return of the given invoke() overrides the value of the object 'o' and the same iteration, explained on line 17, will happen again. This will continue until the last level is recovered.

When we reach the last level, so in fact the value to be returned should be what matters to our MRExpression, as shown in line 26.

In line 28, in possession of the name of the last property, we call the getMethodSignature(), which convert this property into an appropriateget() method and, thereafter, will capture the Method of this property. Finally we use the invoke() to return the desired value.

Testing our MRExpression

We have understood how works our MRExpression and how it was implemented. Now, we will start to use it. First, let's define two DTO's that we will use as an example of recovery values of their properties.

We could apply our example to an entity, for example, that, differently from DTO, is a class that has a direct mapping to a ORM framework, such as Hibernate.

Listing 3. ClientDTO

package lab;


import java.util.Date;


public class ClientDTO {
	
	private String name;
	private Integer age;
	private Date birthDate;
	private AddressDTO address;
				
	public String getName() {
		  return name;
	}
	public void setName(String name) {
		  this.name = name;
	}
	public Integer getAge() {
		  return age;
	}
	public void setAge(Integer age) {
		  this.age = age;
	}
	public Date getBirthDate() {
		  return birthDate;
	}
	public void setBirthDate(Date birthDate) {
		  this.birthDate = birthDate;
	}
	public AddressDTO getAddress() {
		  return address;
	}
	public void setAddress(AddressDTO address) {
		  this.address = address;
	}     
}

The properties that we will use in Listing 3 class are name, age, address and DateOfBirth, but the latter refers to another class, that is in Listing 4.

Listing 4. AddressDTO

package lab;
public class AddressDTO {
	
	private String patio;
	private String number;
	private String district;
	private String city;
	private String uf;
	public String getPatio() {
		  return patio;
	}
	public void setPatio(String patio) {
		  this.patio = patio;
	}
	public String getNumber() {
		  return number;
	}
	public void setNumber(String number) {
		  this.number = number;
	}
	public String getDistrict() {
		  return district;
	}
	public void setDistrict(String district) {
		  this.district = district;
	}
	public String getCity() {
		  return city;
	}
	public void setCity(String city) {
		  this.city = city;
	}
	public String getUf() {
		  return uf;
	}
	public void setUf(String uf) {
		  this.uf = uf;
	}           
	
}

In AddressDTO class we can reference the properties patio, number, district, city and uf. Let's create a class that has the main() method and performs the tests of our MRExpression.

In Listing 5 we have a very simple example to illustrate the use of our MRExpression, which shows how to access various levels of their property without the need for several getters for it.

Listing 5. Testing DMExpression

package lab;


import java.lang.reflect.InvocationTargetException;
import java.util.Date;


public class Test {


	public static void main(String[] args) {
		ClientDTO dto = new ClientDTO();
		dto.setBirthDate(new Date());
		dto.setAge(32);
		dto.setName("TEST MRBOOL");


		AddressDTO addrDto = new AddressDTO();
		addrDto.setBairro("District test");
		addrDto.setCidade("City test");
		addrDto.setLogradouro("Patio Teste");
		addrDto.setNumero("AB20");
		addrDto.setUf("NY");


		dto.setEndereco(addrDto);


		try {
			System.out.println(MRExpression.invoke(dto, "address.district") + " " + MRExpression.invoke(dto, "address.city"));
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

In line 6 we first instantiate an object of type ClientDTO, inflating this with the values that we define in the lines 7-9.

In line 11, after the creation of the object ClientDTO, we need to create the AddressDTO to have the object "address" of the ClientDTO object, and inflate the endDTO object, along the lines 12-16.

Note that our invoke() method is static, which enables us to call it straight in the class without creating a MRExpression object.

Conclusion

Obviously the native Java EE EL Expression has several other features not just enabling method access through the name of the properties. It can also perform logical operations (AND, OR, NOT ...), arithmetic operations (addition, subtraction, division, multiplication), comparing, among other features.

If you are using Hibernate you can even make an improvement in MRExpression, allowing the lazy properties init just by its name: the only difference is that you do not need to return the property value, but you must pass the same to Hibernate to perform initialization of the property while the session is still open.



Julio is a System analyst and enthusiast of Information Technology. He is currently a developer at iFactory Solutions company, working in the development of strategic systems, is also a JAVA instructor. He has knowledge and experi...

What did you think of this post?
Services
[Close]
To have full access to this post (or download the associated files) you must have MrBool Credits.

  See the prices for this post in Mr.Bool Credits System below:

Individually – in this case the price for this post is US$ 0,00 (Buy it now)
in this case you will buy only this video by paying the full price with no discount.

Package of 10 credits - in this case the price for this post is US$ 0,00
This subscription is ideal if you want to download few videos. In this plan you will receive a discount of 50% in each video. Subscribe for this package!

Package of 50 credits – in this case the price for this post is US$ 0,00
This subscription is ideal if you want to download several videos. In this plan you will receive a discount of 83% in each video. Subscribe for this package!


> More info about MrBool Credits
[Close]
You must be logged to download.

Click here to login