Archive

Author Archive

Creating an object stream from a JDBC ResultSet

08/14/2014 5 comments

The introduction of  features Stream API and Lambda in Java 8 enables us to make an elegant conversion from a JDBC ResultSet to a stream of objects just providing a mapping function. Such function could be, of course, a lambda.

Basically, the idea is to generate a Stream using a ResultSet as Supplier:

public class ResultSetSupplier implements Supplier<T>{

		private final ResultSet rs;
		private final Function<ResultSet, T> mappingFunction;

		private ResultSetSupplier(ResultSet rs,
                    Function<ResultSet, T> mappingFunction) {
			this.rs = rs;
			this.mappingFunction = mappingFunction;
		}

		@Override
		public T get() {
			try {
				if (rs.next())
					return mappingFunction.apply(rs);
			} catch (SQLException e) {
				e.printStackTrace();
			}
			return null;
		}
	}

Parameter mappingFunction, which might be a lambda expression, is used to build T instances from a ResultSet. Just like ActiveRecord pattern, every row in such ResultSet maps to an instance of T, where columns are attributes of T.

Let’s consider class City:

public class City{
		String city;
		String country;
		public City(String city, String country) {
			this.city = city;
			this.country = country;
		}
		public String getCountry() {
			return country;
		}
		@Override
		public String toString() {
			return "City [city=" + city + ", country=" + country + ";]";
		}
		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + ((city == null) ? 0 : city.hashCode());
			result = prime * result
					+ ((country == null) ? 0 : country.hashCode());
			return result;
		}
		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			City other = (City) obj;
			if (city == null) {
				if (other.city != null)
					return false;
			} else if (!city.equals(other.city))
				return false;
			if (country == null) {
				if (other.country != null)
					return false;
			} else if (!country.equals(other.country))
				return false;
			return true;
		}
	}

The mapping function for City objects could be a lambda expression like the following:

(ResultSet rs) -> {
                try
                {
                  return new City(rs.getString("city"), rs.getString("country"));
		} catch (Exception e) {
		  return null;
		}}

We have assumed database columns are called city and country, respectively.

Although both PreparedStatement and ResultSet implement AutoCloseable interface, as a  resultSet must be provided to create the object stream, it does make sense to close such resultSet when the stream is closed as well.

A possible approach could be to use a proxy to intercept method invocation on the object stream. Thus, as close() method is invoked on the proxy, it will invoke close() on the provided resultSet. All method invocations will be invoked on the object stream as well, in order to be able to provide all Stream features. That is easy to achieve using a proxy.

Let’s have a look. We will have a proxy factory and a invocation handler:

public class ResultSetStreamInvocationHandler<T> implements InvocationHandler{

  private Stream<T> stream; // proxy will intercept method calls to such stream
  private PreparedStatement st;
  private ResultSet rs;

  public void setup(PreparedStatement st, Function<ResultSet, T> mappingFunction)
  throws SQLException{
    // PreparedStatement must be already setup in order
    // to just call executeQuery()
    this.st = st;
    rs = st.executeQuery();
    stream = Stream.generate(new ResultSetSupplier(rs, mappingFunction));
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args)
  throws Throwable {

    if (method == null)
      throw new RuntimeException("null method null");

    // implement AutoCloseable for PreparedStatement
    // as calling close() more than once has no effects
    if (method.getName().equals("close") && args == null){
    // invoked close(), no arguments
      if (st != null){
        st.close(); // closes ResultSet too
      }
    }

    return method.invoke(stream, args);
  }

private class ResultSetSupplier implements Supplier<T>{

  private final ResultSet rs;
  private final Function<ResultSet, T> mappingFunction;

  private ResultSetSupplier(ResultSet rs, Function<ResultSet, T> mappingFunction) {
    this.rs = rs;
    this.mappingFunction = mappingFunction;
  }

  @Override
  public T get() {
    try {
      if (rs.next())
        return mappingFunction.apply(rs);
    } catch (SQLException e) {
     e.printStackTrace();
    }
    return null;
  }
}

}

Please note how invoke is used to intercept method calls. In case close() is called, close() is called on PreparedStatement as well. For every method called, the corresponding method call is invoked in the stream being proxied.

And the factory:

 

public class ResultSetStream<T>{

	@SuppressWarnings("unchecked")
	public Stream<T> getStream(PreparedStatement st,
            Function<ResultSet, T> mappingFunction) throws SQLException{
		final ResultSetStreamInvocationHandler<T> handler =
                    new ResultSetStreamInvocationHandler<T>();
		handler.setup(st, mappingFunction);
		Stream<T> proxy = (Stream<T>) Proxy.newProxyInstance(getClass().getClassLoader(),
                new Class<?>[] {Stream.class},
                handler);
		return proxy;
	}
}

To put it all together, let’s write a simple test to show usage. Mockito will be used to mock both PreparedStatement and ResultSet to avoid running tests against a real database.


public class ResultSetStreamTest {

	private class City{
		String city;
		String country;
		public City(String city, String country) {
			this.city = city;
			this.country = country;
		}
		public String getCountry() {
			return country;
		}
		@Override
		public String toString() {
			return "City [city=" + city + ", country=" + country + "]";
		}
		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + getOuterType().hashCode();
			result = prime * result + ((city == null) ? 0 : city.hashCode());
			result = prime * result
					+ ((country == null) ? 0 : country.hashCode());
			return result;
		}
		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			City other = (City) obj;
			if (!getOuterType().equals(other.getOuterType()))
				return false;
			if (city == null) {
				if (other.city != null)
					return false;
			} else if (!city.equals(other.city))
				return false;
			if (country == null) {
				if (other.country != null)
					return false;
			} else if (!country.equals(other.country))
				return false;
			return true;
		}
		private ResultSetStreamTest getOuterType() {
			return ResultSetStreamTest.this;
		}
	}

	private String[][] data = new String[][]{
			{"Karachi", "Pakistan"},
			{"Istanbul", "Turkey"},
			{"Hong Kong", "China"},
			{"Saint Petersburg", "Russia"},
			{"Sydney", "Australia"},
			{"Berlin", "Germany"},
			{"Madrid", "Spain"}
		};

	private int timesCalled;
	private PreparedStatement mockPST;
	private ResultSet mockRS;

	@Before
	public void setup() throws SQLException{
		timesCalled = -1;
		mockRS = mock(ResultSet.class);
		mockPST = mock(PreparedStatement.class);

		when(mockRS.next()).thenAnswer(new Answer<Boolean>() {

			@Override
			public Boolean answer(InvocationOnMock invocation) throws Throwable {
				if (timesCalled++ &gt;= data.length)
					return false;
				return true;
			}
		});

		when(mockRS.getString(eq("city"))).thenAnswer(new Answer<String>() {

			@Override
			public String answer(InvocationOnMock invocation) throws Throwable {
				return data[timesCalled][0];
			}
		});
		when(mockRS.getString(eq("country"))).thenAnswer(new Answer<String>() {

			@Override
			public String answer(InvocationOnMock invocation) throws Throwable {
				return data[timesCalled][1];
			}
		});

		when(mockPST.executeQuery()).thenReturn(mockRS);
	}

	@Test
	public void simpleTest() throws SQLException{

		try (Stream<City> testStream = new ResultSetStream<City>().getStream(mockPST,
				(ResultSet rs) -> {try {
					return new City(rs.getString("city"), rs.getString("country"));
				} catch (Exception e) {
					return null;
				}})){

			Iterator<City> cities = testStream.filter(
					city -> !city.getCountry().equalsIgnoreCase("China"))
					.limit(3).iterator();

			assertTrue(cities.hasNext());
			assertEquals(new City("Karachi", "Pakistan"), cities.next());

			assertTrue(cities.hasNext());
			assertEquals(new City("Istanbul", "Turkey"), cities.next());

			assertTrue(cities.hasNext());
			assertEquals(new City("Saint Petersburg", "Russia"), cities.next());

			assertFalse(cities.hasNext());
		}

	}

}

Download full source code on Github.

Java yield-like using Stream API

08/11/2014 1 comment

Several programming languages, such as Ruby or Python to name a few, provides the yield command. Yield provides an effective way, in terms of memory consumption, to create series of values, by generating such values on demand. More information on Python Yield.

Let’s consider a class or method requiring a huge amount of secure random integers. The classical approach would be to create an array or collection of such integers. Yield provides two major advantages over such approach:

  • yield does not require to know the length of the series in advance.
  • yield does not require to store all values in memory.

Fortunately, yield features can be used in Java 8 thanks to Stream API:

 

 

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Date;
import java.util.function.Supplier;
import java.util.stream.Stream;

public class Yield {

	private static final Integer RANDOM_INTS = 10;

	public static void main(String[] args) {

		try (Stream randomInt = generateRandomIntStream()){
			Object[] randomInts = randomInt.limit(RANDOM_INTS)
                                .sorted().toArray();
			for (int i = 0; i < randomInts.length;i++)
				System.out.println(randomInts[i]);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
	}

	private static Stream generateRandomIntStream()
           throws NoSuchAlgorithmException{
		return Stream.generate(new Supplier() {

			final SecureRandom random = SecureRandom
                                .getInstance("SHA1PRNG");
			boolean init = false;
			int numGenerated = 0;

			@Override
			public Integer get() {
				if (!init){
					random.setSeed(new Date().getTime());
					init = true;
					System.out.println("Seeding");
				}
				final int nextInt = random.nextInt();
				System.out.println("Generated random "
                                         + numGenerated++
                                         + ": " + nextInt);
				return nextInt;
			}

		});
	}

}

Following is the output after provided code snippet is executed:

Seeding
Generated random 0: -896358073
Generated random 1: -1268521873
Generated random 2: 9627917
Generated random 3: -2106415441
Generated random 4: 935583477
Generated random 5: -1132421439
Generated random 6: -1324474601
Generated random 7: -1768257192
Generated random 8: -566921081
Generated random 9: 425501046
-2106415441
-1768257192
-1324474601
-1268521873
-1132421439
-896358073
-566921081
9627917
425501046
935583477

It is easy to see that Supplier is only instantiated one. Of course, we can take advantage of all Stream API features such as limit() and sorted().

The line randomInt.limit(RANDOM_INTS).sorted().toArray() triggers the generation of RANDOM_INTS values which are then sorted and stored as an array.

Compile-time checking JPA queries

JPA provides several alternatives for querying data. Such alternatives may be classified attending to a variety of criteria, eg, language used (SQL vs JPQL) or whether queries are static (compilation time) or dynamic (execution time).

Static queries are defined using annotations @NamedQuery (javax.persistence.NamedQuery) and @NamedQueries (javax.persistence.NamedQueries) in the @Entity class definition itself:

 @NamedQuery(
            name="findAllCustomersWithName",
            query="SELECT c FROM Customer c WHERE c.name LIKE :custName"
    )

On the other hand, EntityManager provides methods createQuery(…) y createNativeQuery(…) which take either a JPQL or a SQL query, respectively.

Thus, queries can be defined both in compilation or execution time.

(Note: It is advisable to always use parametrized queries using methods setParameter(…) from Query to avoid SQL Injection vulnerabilities.

Criteria API

However, JPA provides an alternative approach to query objects: Criteria API. Indeed, one of the motivations to switch to JPA is to deal with objects rather than SQL dialects, isn’t it ?

Let’s look a sample code.

Entity definition:

@Entity
public class User {

 @Id
 private Integer userId;

 @Basic
 @Column(length=15, nullable=false)
 private String name;

 @Basic
 @Column(length=64, nullable=false)
 private String userDigestedPasswd;

 @Basic
 @Column(length=50, nullable=true)
 private String email;

 @Basic
 @Column(nullable=false)
 public Integer privilegeLevel;

 @Basic
 @Column(nullable=false)
 private Boolean active;
}

Let’s query db and check results (using JUnit):

public class UserTest {
 @Test
 public void testUserCriteria(){
EntityManagerFactory emf = null;
EntityManager em = null;
try {
  emf = Persistence.createEntityManagerFactory("criteria");
  em = emf.createEntityManager();
  final CriteriaBuilder cb = em.getCriteriaBuilder();
  final CriteriaQuery<User> q = cb.createQuery(User.class);
  final Root<User> users = q.from(User.class);
  final Predicate condition = cb.equal(users.get("privilegeLevel"), 5);
  q.select(users).where(condition).orderBy(cb.asc(users.get("userId")));
  em.getTransaction().begin();
  List<User> result = em.createQuery(q).getResultList();
  em.getTransaction().commit();

  assertNotNull(result);
  assertEquals(2, result.size());

  assertEquals(1, (int)result.get(0).getUserId());
  assertEquals("Pepe", result.get(0).getName());

  assertEquals(3, (int)result.get(1).getUserId());
  assertEquals("Dolores", result.get(1).getName());} catch (Exception e) {
  fail("Unexpected Exception " + e.getMessage());
} finally {
  if (em != null)
    em.close();
  if (emf != null)
    emf.close();
}
}
}

Following lines show query creation:

final CriteriaBuilder cb = em.getCriteriaBuilder();
final CriteriaQuery<User> q = cb.createQuery(User.class);
final Root<User> users = q.from(User.class);
final Predicate condition = cb.equal(users.get("privilegeLevel"), 5);
q.select(users).where(condition).orderBy(cb.asc(users.get("userId")));

First of all, get a CriteriaBuilder from an EntityManager. Then, get a CriteriaQuery instance, setting the class to hold results. In our case, User.class:

final CriteriaBuilder cb = em.getCriteriaBuilder();
final CriteriaQuery<User> q = cb.createQuery(User.class);

Following, the Entity to run the query against must be set:

final Root<User> users = q.from(User.class);

Now it’s time to set query matching conditions. In the sample code, the condition is just attribute privilegeLevel to be equals to 5:

final Predicate condition = cb.equal(users.get("privilegeLevel"), 5);

Finally, query is built adding conditions on Root. Grouping and sorting options may be set too (ie, ascending sorting is set on userId):

q.select(users).where(condition).orderBy(cb.asc(users.get("userId")));

Please have a look at CriteriaBuilder for different options. Grouping and sorting options may be found at CriteriaQuery.

Using metamodel for compile-time checking

Note the query we have just build requires to keep track of object attributes names. Eg, to build the query, the name of the attribute privilegeLevel is used. However, if attribute name were changed later, the code would compile and only fail at runtime:

final CriteriaQuery<User> q = cb.createQuery(User.class);
final Root<User> users = q.from(User.class);
final Predicate condition = cb.equal(users.get("privilegeLevel"), 5);
q.select(users).where(condition).orderBy(cb.asc(users.get("userId")));

That is no good.

Fortunately, using metamodel, we will be able to build compile-time checked queries. A brief introduction can be found at The Java EE6 Tutorial.

Using metamodel, the code will reference an SingularAttribute of the object rather than using a String holding the object attribute name. So, if object attribute were changed later, the compiler would flag it for us.

First of all, the correspondent metamodel class (EntityType) must be created. Although it can achieved by several ways, probably the easiest one, for openJPA implementation, is to add a openJPA build flag:  -Aopenjpa.metamodel=true.

So we have the class User_ created, which is the correspondent metamodel class for User:

* Generated by OpenJPA MetaModel Generator Tool. **/
package com.wordpress.tododev.criteria.entities;
import javax.persistence.metamodel.SingularAttribute;
@javax.persistence.metamodel.StaticMetamodel
(value=com.wordpress.tododev.criteria.entities.User.class)
@javax.annotation.Generated
(value="org.apache.openjpa.persistence.meta.AnnotationProcessor6",date="Mon Mar 04 16:47:46 CET 2013")
public class User_ {
 public static volatile SingularAttribute<User,Boolean> active;
 public static volatile SingularAttribute<User,String> email;
 public static volatile SingularAttribute<User,String> name;
 public static volatile SingularAttribute<User,Integer> privilegeLevel;
 public static volatile SingularAttribute<User,String> userDigestedPasswd;
 public static volatile SingularAttribute<User,Integer> userId;
}

If such class were added to code repo, any later change to class User would remain unnoticeable. Moreover, it is not a good idea to add auto-generated items to code versioning systems.

Using ant, maven or similar tools, a target could be added to create metamodel classes. Such target should be executed after any change to JPA Entities.

Also possible to use IDE for that. Eg, for those using Eclipse, just need to add the already mentioned compilation flag to Properties->Java Compiler->Annotation Processor and the lib (jar) containing the Annotation Processor for the chosen JPA implementation to section Factory Path within Annotations Processor (could lead to compilation issues in auto mode, provided that metamodel class must be compiled before the code using it).

Let us add another test to the suite. This one will not provide a String containing the attribute name, but use the metamodel class instead:

@Test
 public void testUserCriteriaMetaModel(){
 EntityManagerFactory emf = null;
 EntityManager em = null;
 try {
 emf = Persistence.createEntityManagerFactory("criteria");
 em = emf.createEntityManager();
 final CriteriaBuilder cb = em.getCriteriaBuilder();
 final CriteriaQuery<User> q = cb.createQuery(User.class);
 final Metamodel m = em.getMetamodel();
 final Root<User> user = q.from(m.entity(User.class));
 final Predicate condition = cb.equal(user.get(User_.privilegeLevel), 5);
 q.select(user).where(condition).orderBy(cb.asc(user.get(User_.userId)));

 em.getTransaction().begin();
 List<User> result = em.createQuery(q).getResultList();
 em.getTransaction().commit();

 assertNotNull(result);
 assertEquals(2, result.size());

 assertEquals(1, (int)result.get(0).getUserId());
 assertEquals("Pepe", result.get(0).getName());

 assertEquals(3, (int)result.get(1).getUserId());
 assertEquals("Dolores", result.get(1).getName());
} catch (Exception e) {
 fail("Unexpected Exception " + e.getMessage());
 } finally {
 if (em != null)
 em.close();
 if (emf != null)
 emf.close();
 }
 }

More relevant changes are user.get(User_.privilegeLevel) instead of users.get(“privilegeLevel”) and  user.get(User_.userId) instead of  users.get(“userId”).

Download source code from GitHub

Detecting and Fixing XSS using OWASP tools

Much have been written about XSS vulnerabilities scanning. In this article we will try to go a little further and show how to fix them.

To illustrate the whole process, going from initial detection to providing a fix, we will use a very simple app consisting of two JSP pages: one is a payment form for credit card transactions and contains some XSS exploitable code. The other one has such code fixed: the later is just a patched version of the former. We will see how an attacker could trick users, exploiting the present XSS vulnerability, to collect their credit card data.

(Download vulnerable app)

XSS Attacks

 

The goal of XSS attacks is to have a injected script executed by the user web browser. In most cases, user is not even aware of what is going on. For further information about XSS please have a look at OWASP XSS Attack Description.

Let us have a look at our sample app. The vulnerable JSP (xss_html.jsp) contains the following code fragment:


<% 
final String amount = request.getParameter("amount"); 
Enumeration pNames = request.getParameterNames(); 
while (pNames.hasMoreElements()){     
   final String pName = pNames.nextElement();     
   final String pVal = request.getParameter(pName);     
%>
    <input type="hidden" name="<%=pName%>" />" value="" />;</pre>
<table>
<tbody>
<tr>
<td>Credit card</td>
<td><input type="text" maxlength="16" name="cc" size="16" value="" /></td>
</tr>
<tr>
<td>Exp Date (mm/yy)</td>
<td><input type="text" maxlength="2" name="expMonth" size="2" value="" />
/<input type="text" maxlength="2" name="expYear" size="2" value="" /></td>
</tr>
<tr>
<td>CVV2</td>
<td><input type="text" maxlength="2" name="expMonth" size="2" value="" />
/<input type="text" maxlength="2" name="expYear" size="2" value="" /></td>
</tr>
<tr>
<td colspan="2"><input id="button1" type="submit" name="button1" value="Pay" /></td>
</tr>
</tbody>
</table>

The form receives amount to charge as an HTTP Request parameter, collects credit card data form user and them charges her (of course, such last step is not included, you can try with any non-real credit card data). Users could be redirected here from any ecommerce site using a URL such as http://……/XSS_Vulnerable/xss_html.jsp?amount=12.25 (again, nobody would choose such a path for a payment gateway pretending to be a trusted one). But let us see what happens if an attacker tricks someone into loading a malicious URL as the one included in index.jsp instead when the user press Pay button (Firefox 24.0 for Linux):

firefox_vulnerable

Just an alert, but injected Javascript code could have created an image or some other kind of link, so the attacker would have been able to collect the data by just looking at Apache access logs. Indeed, some browsers are able to identify such threats and will not execute injected scripts, as shown (Chromium 28.0 for Linux).

chrome_refuses_exec_script

XSS Detection

 

Fortunately, there are a lot of tools that perform XSS threats scanning so, for the most common issues, there is no need to look at every line of code in every web page when trying to locate such vulnerabilities. One of those tools is OWASP Zed Attack Proxy Object (ZAP). Although it would not be fair to say it is just a XSS scanner, as it provides many many more interesting features.

ZAP can be used as a proxy (indeed, it is based on older Paros Proxy) being able to scan all pages accesed during the session. However, we are just going to introduce the URL( http://……/XSS_Vulnerable/xss_html.jsp?amount=12.25) and press the Attack button (we are using ZAP 2.2.2). To avoid several warnings and making scanning faster we disabled all scan types except for XSS.

ZAP_XSS_vulnerable

Starting from provided URL ( http://……/XSS_Vulnerable/xss_html.jsp?amount=12.25) ZAP has made several checks adding javascript code to be injected.

The tab at the bottom shows one successful attempt of a XSS attack. ZAP has replaced the numeric value of amount parameter by and URL encoded javascript code (as seen in URL field) which is just “><script>alert(1);</script> in plain text (as seen in Evidence field).

Moreover, in the tab above, where HTTP response is shown, the result of the XSS attack is clearly shown: the injected code in amount parameter first closes the double quotes (“) around value for amount field and closes HTML input (>). Afterwards, it adds the script alert(1); (<script>alert(1);</script>). The resulting HTML code to be executed by web browser contains:

<input type="hidden" name="amount" value=""><script>alert(1);</script>" />

XSS Fix

 
Although there is not a single fix for all XSS attacks, all of them are based on input validation, where “input” could be any from HTTP Request parameters, HTTP Headers values or even names… all depends on what the code uses as input.

In our sample app, a HTTP Request parameter is being used to write HTML code.

OWASP provides OWASP Enterprise Security API (ESAPI) in several languages, including, of course Java. ESAPI includes much more functionality related to security, from XSS and CSRF to crypto.

To fix our XSS vulnerability, we are just using a ESAPI encoder (ESAPI 2.1.0). The fix is based on writing the received amount parameter HTML encoded instead of as just received. This way, the user web browser will not execute the javascript code, as it will be seen as the value of the amount parameter.

The fix requires just HTML encoding the amount parameter (see xss_html_esapi.jsp) as follows:

<form method="POST" name="sendForm" id="sendForm" onsubmit="return sendPaymentRequest()">

<%
final String amount = request.getParameter("amount");
Enumeration<String> pNames = request.getParameterNames();
while (pNames.hasMoreElements()){
    final String pName = pNames.nextElement();
    final String pVal = request.getParameter(pName);

    final org.owasp.esapi.Encoder esapiEnc = DefaultEncoder.getInstance();
    final String encPVal = esapiEnc.encodeForHTML(pVal);

    %>
    <input type="hidden" name="<%=pName%>" value="<%=encPVal%>" />
    <%
}
%>

<table>

Running ZAP against fixed JSP (xss_html_esapi.jsp) does not report XSS Vulnerabilities.

Creación de consultas dinámicas en JPA sin SQL (ni JPQL): Usando el metamodelo

En la entrada anterior vimos cómo realizar consultas JPA sin escribir código SQL. Sin embargo,  echemos un vistazo a una porción concreta del código que usamos en aquel momento:

  final CriteriaQuery<User> q = cb.createQuery(User.class);
  final Root<User> users = q.from(User.class);
  final Predicate condition = cb.equal(users.get("privilegeLevel"), 5);
  q.select(users).where(condition).orderBy(cb.asc(users.get("userId")));

Puede observarse como, en ciertos lugares, se obtienen valores de atributos del objeto User, en concreto, privilegeLevel y userId, haciendo referencia al nombre del dicho atributo. Este enfoque tiene el problema de que no se realizan comprobaciones en tiempo de ejecución. Dicho de otro modo, si, por ejemplo,  se cambiase el nombre del atributo userId a user_id, el código seguiría siendo correcto desde el punto de vista del compilador. Sin embargo, al ejecutarlo, se producirían errores al no existir tal atributo.
Una posible solución consiste en usar el metamodelo de Java. Una introducción, con ejemplos no muy afortunados, desde mi punto de vista, puede encontrarse en The Java EE6 Tutorial.

El truco consiste en acceder a los atributos del objeto usando el metamodelo, de manera que un cambio en cualquiera de los atributos del objeto sería detectado mediante un fallo de compilación. Lo primero que hay que hacer es generar la clase del metamodelo correspondiente, la EntityType. Hay varias formas de hacerlo, aunque quizás la mas sencilla sea indicarlo con el flag de compilación correspondiente. Por ejemplo, para openJPA, el flag es  -Aopenjpa.metamodel=true. 

Se genera la clase User_, que no es mas que la clase de  metamodelo de User, siguiendo el estándar de nomenclatura .

* Generated by OpenJPA MetaModel Generator Tool. **/
package com.wordpress.tododev.criteria.entities;
import javax.persistence.metamodel.SingularAttribute;
@javax.persistence.metamodel.StaticMetamodel
(value=com.wordpress.tododev.criteria.entities.User.class)
@javax.annotation.Generated
(value="org.apache.openjpa.persistence.meta.AnnotationProcessor6",date="Mon Mar 04 16:47:46 CET 2013")
public class User_ {
 public static volatile SingularAttribute<User,Boolean> active;
 public static volatile SingularAttribute<User,String> email;
 public static volatile SingularAttribute<User,String> name;
 public static volatile SingularAttribute<User,Integer> privilegeLevel;
 public static volatile SingularAttribute<User,String> userDigestedPasswd;
 public static volatile SingularAttribute<User,Integer> userId;
}

Quizás la primera opción que se viene a la cabeza sea añadir este código autogenerado al sistema de control de versiones. Pero no suele ser buena idea añadir código autogenerado y, sobre todo, una modificación posterior en los atributos de User pasaría desapercibida por el compilador a menos que se generase de nuevo la clase User_.  En el caso de usar ant, podría solucionarse añadiendo un target específico que genere las clase del metamodelo. Dicho target debería ejecutarse cada vez que se realizara alguna modificación en cualquiera de las Entities JPA de la aplicación.

Otra opción consiste en instruir al IDE para generar código de acuerdo con las anotaciones. En Eclipse, hay que añadir el flag de compilación ya mencionado en  Properties->Java Compiler->Annotation Processor y, a continuación, añadir el jar que contiene el Annotation Processor de la implementación JPA elegida en la sección Factory Path (dentro de Annotations Processor). El problema es que, en este caso, la clase de metamodelo User_ se genera al compilar, pero para compilar se necesita la clase del metamodelo, y llegamos al dilema del huevo o la gallina.

En cualquier caso, una vez se disponga de la clase de metamodelo, se puede añadir otro test a la suite anterior que proporciona el mismo resultado sin necesidad de pasar una cadena con el nombre del atributo:

@Test
 public void testUserCriteriaMetaModel(){
 EntityManagerFactory emf = null;
 EntityManager em = null;
 try {
 emf = Persistence.createEntityManagerFactory("criteria");
 em = emf.createEntityManager();
 final CriteriaBuilder cb = em.getCriteriaBuilder();
 final CriteriaQuery<User> q = cb.createQuery(User.class);
 final Metamodel m = em.getMetamodel();
 final Root<User> user = q.from(m.entity(User.class));
 final Predicate condition = cb.equal(user.get(User_.privilegeLevel), 5);
 q.select(user).where(condition).orderBy(cb.asc(user.get(User_.userId)));

 em.getTransaction().begin();
 List<User> result = em.createQuery(q).getResultList();
 em.getTransaction().commit();

 assertNotNull(result);
 assertEquals(2, result.size());

 assertEquals(1, (int)result.get(0).getUserId());
 assertEquals("Pepe", result.get(0).getName());

 assertEquals(3, (int)result.get(1).getUserId());
 assertEquals("Dolores", result.get(1).getName());
} catch (Exception e) {
 fail("Unexpected Exception " + e.getMessage());
 } finally {
 if (em != null)
 em.close();
 if (emf != null)
 emf.close();
 }
 }

Los cambios importantes están en las líneas en negrita. Sobre todo,  user.get(User_.privilegeLevel), en lugar de users.get(“privilegeLevel”) y user.get(User_.userId) en lugar de users.get(“userId”).

Podéis encontrar el código fuente actualizado en GitHub.

MiriadaX: MOOCs en español

Según reza su propia página web, Miríada X ofrece una plataforma a los docentes o equipos docentes de cualquiera de las universidades iberoamericanas que conforman la Red Universia en la que poder crear e impartir Cursos Online Masivos en Abierto (más conocidos como MOOC’s), accesibles para todo el mundo de manera gratuita. Aunque, por ahora, la mayor parte de las universidades son españolas.

Aunque conocí su existencia hace poco, parece que ya tiene algunos cursos que pueden ser interesantes para el gremio (mis disculpas de antemano, algunos ya están comenzados), como, por ejemplo, Introducción al desarrollo web (CSS, Javascript, PHP y MySQL), Programación de apps para Android o Aplicaciones de la Teoría de Grafos a la vida real.

Aunque, dada la situación actual, no son desdeñar Inglés Profesional o Alemán para Hispanohablantes 😉

En cualquier caso, una alternativa a los que, por el idioma, no se acababan de decidir por opciones como Udacity o Coursera.

¿ Alguién ha realizado ya algún curso de MiriadaX ?

Creación de consultas dinámicas en JPA sin SQL (ni JPQL)

02/04/2013 7 comments

En JPA se pueden crear varios tipos de consultas. Dichas consultas pueden clasificarse de acuerdo a distintos criterios. En nuestro caso, nos interesan dos criterios de distinción: según el lenguaje en que se formulan (SQL, JPQL, …) y si son estáticas (definidas en el propio código) o dinámicas (se construyen en tiempo de ejecución).

Las consultas estáticas se definen usando las anotaciones @NamedQuery (javax.persistence.NamedQuery) y @NamedQueries (javax.persistence.NamedQueries) en la propia clase @Entity:

 @NamedQuery(
            name="findAllCustomersWithName",
            query="SELECT c FROM Customer c WHERE c.name LIKE :custName"
    )

Por otra parte, los objetos EntityManager proporcionan los métodos createQuery(…) y createNativeQuery(…) que aceptan como parámetro una consulta JQPL o SQL, respectivamente. Por lo tanto, las consultas pueden construirse dinámicamente, en tiempo de ejecución. (Importante: usar siempre consultas parametrizadas usando los métodos setParameter(…) de los objetos Query creados para evitar posibles vulnerabilidades SQL Injection).

Sin embargo, existe otra forma de crear consultas dinámicas en JPA sin tener que usar JPQL ni SQL: usando la API Criteria. En definitiva, uno de los motivos para usar JPA es tratar con objetos en lugar de con dialectos SQL y derivados, verdad ? A continuación se muestra un ejemplo sencillo.

En primer lugar, definimos una Entity:

@Entity
public class User {

 @Id
 private Integer userId;

 @Basic
 @Column(length=15, nullable=false)
 private String name;

 @Basic
 @Column(length=64, nullable=false)
 private String userDigestedPasswd;

 @Basic
 @Column(length=50, nullable=true)
 private String email;

 @Basic
 @Column(nullable=false)
 public Integer privilegeLevel;

 @Basic
 @Column(nullable=false)
 private Boolean active;
}

Partiendo de una base de datos en la que se han insertado varios objetos, procedemos a realizar una consulta y comprobamos los datos recibidos (es un test JUnit que puede ejecutarse directamente desde cualquier IDE actual):

public class UserTest {
 @Test
 public void testUserCriteria(){
EntityManagerFactory emf = null;
EntityManager em = null;
try {
  emf = Persistence.createEntityManagerFactory("criteria");
  em = emf.createEntityManager();
  final CriteriaBuilder cb = em.getCriteriaBuilder();
  final CriteriaQuery<User> q = cb.createQuery(User.class);
  final Root<User> users = q.from(User.class);
  final Predicate condition = cb.equal(users.get("privilegeLevel"), 5);
  q.select(users).where(condition).orderBy(cb.asc(users.get("userId")));
em.getTransaction().begin();
List<User> result = em.createQuery(q).getResultList();
em.getTransaction().commit();

assertNotNull(result);
assertEquals(2, result.size());

assertEquals(1, (int)result.get(0).getUserId());
assertEquals("Pepe", result.get(0).getName());

assertEquals(3, (int)result.get(1).getUserId());
assertEquals("Dolores", result.get(1).getName());} catch (Exception e) {
fail("Unexpected Exception " + e.getMessage());
} finally {
if (em != null)
em.close();
if (emf != null)
emf.close();
}
}
}

La creación de la consulta se realiza en las siguientes líneas:

 final CriteriaBuilder cb = em.getCriteriaBuilder();
 final CriteriaQuery<User> q = cb.createQuery(User.class);
 final Root<User> users = q.from(User.class);
 final Predicate condition = cb.equal(users.get("privilegeLevel"), 5);
 q.select(users).where(condition).orderBy(cb.asc(users.get("userId")));

En primer lugar, se obtiene un CriteriaBuilder a partir del EntityManager. A partir de dicho objeto se crea una CriteriaQuery, especificando la clase resultado, que en este caso es User.class:

final CriteriaBuilder cb = em.getCriteriaBuilder();
final CriteriaQuery<User> q = cb.createQuery(User.class);

A continuación, definimos la Root, que es otra cosa que la @Entity sobre la que se va a realizar la consulta:

final Root<User> users = q.from(User.class);

Queda ahora especificar las condiciones que deben cumplir los objetos seleccionados. En este caso, la condición es que el atributo privilegeLevel del objeto sea igual a 5:

final Predicate condition = cb.equal(users.get("privilegeLevel"), 5);

Finalmente, se contruye la consulta combinando la Root con las condiciones que deben cumplir los objetos a seleccionar (pueden añadirse y combinarse lógicamente varias condiciones, de la misma forma que se usaría AND y OR en SQL/JQPL), y especificando opciones de agrupación, ordenado, etc… En este caso,  orden ascendente según el valor del atributo userId:

q.select(users).where(condition).orderBy(cb.asc(users.get(“userId”)));

La consulta ya está creada y sólo quedaría ejecutarla.

Pueden obtenerse las diferentes opciones echando un vistazo a la referencia de la interfaz CriteriaBuilder. Las opciones de agrupación, ordenación, … (similares a ORDER BY, GROUP BY, …) se encuentran en la referencia de la interfaz CriteriaQuery.

Puede descargarse el código fuente (Eclipse Project) desde GitHub

%d bloggers like this: