Entries tagged [javaee]
jQuery DataTables with JAX-RS
TweetPosted on Monday Mar 24, 2014 at 05:21PM in Technology
- jQuery DataTables plugin can delegate some data processing to server-side with interaction with JSON[2].
- It's efficient way when we have to go with large data sets.
- So I have tried it with JAX-RS.
Environment
- DataTables 1.9.4
- WildFly 8.0.0.Final
- Oracle JDK8
- PostgreSQL 9.2.4
Sample data
- In this example, we use some tables on PostgreSQL as the data source.
- These tables are parts of the job repository of jBeret (jBatch implementation of WildFly).
- We will execute a SQL which returns data set like this:
jobexecutionid | jobname | starttime | endtime | batchstatus
----------------+---------+-------------------------+-------------------------+-------------
2167 | myjob | 2014-03-19 14:31:13.343 | | STARTING
2166 | myjob | 2014-03-19 14:14:38.158 | 2014-03-19 14:14:59.388 | FAILED
2165 | myjob | 2014-03-19 14:13:24.104 | 2014-03-19 14:14:25.59 | STOPPED
2164 | myjob | 2014-03-19 14:11:32.238 | | STARTING
2163 | myjob | 2014-03-19 14:03:41.07 | 2014-03-19 14:10:29.391 | STOPPED
2162 | myjob | 2014-03-19 13:54:41.017 | 2014-03-19 13:56:55.365 | STOPPED
2161 | myjob | 2014-03-19 13:54:26.902 | 2014-03-19 13:54:38.077 | STOPPED
2160 | myjob | 2014-03-19 13:53:49.291 | 2014-03-19 13:54:22.496 | STOPPED
(8 rows)
jbatch=#
Resources
JobExecutionReportService.java
- Make sure a datasource named “java:jboss/jdbc/JBatchDS” configured correctly on the application server.
package org.nailedtothex.jaxrs_datatables;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.sql.DataSource;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import java.sql.*;
import java.text.Format;
import java.text.MessageFormat;
import java.util.*;
@Stateless
@Path("/JobExecutionReportService")
public class JobExecutionReportService {
private static final String SQL_COUNT = "SELECT COUNT(1) FROM job_execution";
private static final String SQL_FETCH = "SELECT\n" +
" e.jobexecutionid,\n" +
" i.jobname,\n" +
" e.starttime,\n" +
" e.endtime,\n" +
" e.batchstatus\n" +
"FROM\n" +
" job_execution e\n" +
" LEFT JOIN job_instance i ON e.jobinstanceid = i.jobinstanceid\n" +
"ORDER BY\n" +
" e.jobexecutionid {0}\n" +
"LIMIT ?\n" +
"OFFSET ?";
private static final Set<String> VALID_SORT_DIR = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("asc", "desc")));
@Resource(lookup = "java:jboss/jdbc/JBatchDS")
private DataSource ds;
private Format sqlFormat;
@PostConstruct
protected void init() {
sqlFormat = new MessageFormat(SQL_FETCH);
}
@GET
@Produces(MediaType.APPLICATION_JSON)
public DataTablesBean getJobExecutionReport(
@QueryParam("sEcho") Integer sEcho,
@QueryParam("iDisplayLength") Integer iDisplayLength,
@QueryParam("iDisplayStart") Integer iDisplayStart,
@QueryParam("sSortDir_0") String sSortDir_0) throws SQLException {
final Long count;
try (Connection cn = ds.getConnection();
Statement st = cn.createStatement();
ResultSet rs = st.executeQuery(SQL_COUNT);) {
rs.next();
count = rs.getLong(1);
}
if (sSortDir_0 == null || !VALID_SORT_DIR.contains(sSortDir_0)) {
throw new IllegalArgumentException(sSortDir_0);
}
final String sql = sqlFormat.format(new Object[]{sSortDir_0});
final List<List<String>> aaData = new ArrayList<>();
try (Connection cn = ds.getConnection();
PreparedStatement ps = cn.prepareStatement(sql)) {
ps.setInt(1, iDisplayLength);
ps.setInt(2, iDisplayStart);
try (ResultSet rs = ps.executeQuery()) {
final int columns = rs.getMetaData().getColumnCount();
while (rs.next()) {
List<String> data = new ArrayList<>(columns);
for (int i = 1; i <= columns; i++) {
data.add(rs.getString(i));
}
aaData.add(data);
}
}
}
final DataTablesBean bean = new DataTablesBean();
bean.setsEcho(sEcho);
bean.setiTotalDisplayRecords(String.valueOf(count));
bean.setiTotalRecords(String.valueOf(count));
bean.setAaData(aaData);
return bean;
}
}
DataTablesBean.java
package org.nailedtothex.jaxrs_datatables;
import java.util.List;
public class DataTablesBean {
private Integer sEcho;
private String iTotalRecords;
private String iTotalDisplayRecords;
private List<List<String>> aaData;
public String getiTotalRecords() {
return iTotalRecords;
}
public void setiTotalRecords(String iTotalRecords) {
this.iTotalRecords = iTotalRecords;
}
public String getiTotalDisplayRecords() {
return iTotalDisplayRecords;
}
public void setiTotalDisplayRecords(String iTotalDisplayRecords) {
this.iTotalDisplayRecords = iTotalDisplayRecords;
}
public List<List<String>> getAaData() {
return aaData;
}
public void setAaData(List<List<String>> aaData) {
this.aaData = aaData;
}
public Integer getsEcho() {
return sEcho;
}
public void setsEcho(Integer sEcho) {
this.sEcho = sEcho;
}
}
index.html
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css"
href="http://ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.4/css/jquery.dataTables.css">
<script type="text/javascript" charset="utf8"
src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.8.2.min.js"></script>
<script type="text/javascript" charset="utf8"
src="http://ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.4/jquery.dataTables.min.js"></script>
<script>
$(document).ready(function () {
$('#example').dataTable({
"bFilter": false,
"sPaginationType": "full_numbers",
"aaSorting": [
[ 0, "desc" ]
],
"aoColumns": [
{ "bSortable": true },
{ "bSortable": false },
{ "bSortable": false },
{ "bSortable": false },
{ "bSortable": false }
],
"bProcessing": true,
"bServerSide": true,
"sAjaxSource": "webapi/JobExecutionReportService"
});
});
</script>
<title></title>
</head>
<body>
<table id="example">
<thead>
<tr>
<th>id</th>
<th>jobName</th>
<th>startTime</th>
<th>endTime</th>
<th>batchStatus</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</body>
</html>
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.nailedtothex</groupId>
<artifactId>jaxrs-datatables</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
</dependency>
</dependencies>
</project>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<servlet-mapping>
<servlet-name>javax.ws.rs.core.Application</servlet-name>
<url-pattern>/webapi/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
In action

- Pagination and sorting with id are working correctly.
References
Tags: javaee
Manage application config with Commons Configuration
TweetPosted on Wednesday Feb 26, 2014 at 01:53PM in Technology
Environment
- Commons Configuration 1.10
- WildFly 8.0.0.Final
- Oracle JDK7u51
Why go this way?
Various portable ways are available for manage configurations of the Java EE application. such as:
- Configuration table in DB
- I guess it's not bad, but I feel that is bit tedious work to coding.
- There's a way to use this mechanism via Commons Configuration, but I haven't tried it yet.
- Custom JNDI resources
- I thought that it's most efficient at first, but negative points here:
- Badness of lookup performance.
- Editing JNDI resources is very annoying.
- I thought that it's most efficient at first, but negative points here:
So sometimes I'm thinking of any better way, and I found Commons Configurations[1]. notable capabilities of Commons Configuration are:
- Simple API
- Auto-reloading and saving mechanism provided
- Support of various formats and placement styles
So I'd try it.
Prepare sample project and related resources
Create a system property
- Create a system property which specifies the path of configuration file.
- I'm using WildFly so go with jboss-cli:
[standalone@localhost:9990 /] /system-property=app.config:add(value=/Users/kyle/app.properties)
{"outcome" => "success"}
[standalone@localhost:9990 /]
Create the properties file
- Location is same as “app.config” system property.
hoge=fuge
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.nailedtothex</groupId>
<artifactId>config</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<failOnMissingWebXml>false</failOnMissingWebXml>
</properties>
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
<version>1.10</version>
</dependency>
</dependencies>
</project>
Create an application-scoped bean
package org.nailedtothex.config;
import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
@Named
@ApplicationScoped
public class AppConfigBean {
private Configuration config;
@PostConstruct
void init() throws ConfigurationException {
String path = System.getProperty("app.config");
PropertiesConfiguration propConfig = new PropertiesConfiguration(path);
propConfig.setReloadingStrategy(new FileChangedReloadingStrategy());
config = propConfig;
}
public Configuration getConfig(){
return config;
}
}
- If you want to read the config at startup of application, I recommend that use of @javax.ejb.Singleton and @javax.ejb.Startup[2].
Create a servlet
- Get a configuration variable from singleton bean.
package org.nailedtothex.config;
import java.io.IOException;
import java.io.PrintWriter;
import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(urlPatterns = "/*")
public class HelloServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Inject
private AppConfigBean bean;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try (PrintWriter pw = response.getWriter()) {
pw.write(bean.getConfig().getString("hoge"));
}
}
}
Access via browser

- It needs only very small and clean code.
Check runtime reloading
Edit app.properties:
kyle-no-MacBook:~ kyle$ echo hoge=FUGE > app.properties kyle-no-MacBook:~ kyle$
Reload browser:

- I just reloaded the browser and I didn't do any other operations, but config was updated immediately.
Collaboration with CDI
- There's a way to inject properties to CDI managed bean at [2]. that must be useful.
XMLConfiguration
- XMLConfiguration is good for hierarchical configuration.
- It depended on Commons Collection so don't forget to add the dependency.
pom.xml
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
<version>1.10</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
XmlTest.java
package org.nailedtothex.config;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.configuration.XMLConfiguration;
public class XmlTest {
public static void main(String[] args) throws Exception {
XMLConfiguration config = new XMLConfiguration();
config.load(XmlTest.class.getResourceAsStream("/config.xml"));
String backColor = config.getString("colors.background");
String textColor = config.getString("colors.text");
String linkNormal = config.getString("colors.link[@normal]");
String defColor = config.getString("colors.default");
int rowsPerPage = config.getInt("rowsPerPage");
List<Object> buttons = config.getList("buttons.name");
System.out.println(backColor);
System.out.println(textColor);
System.out.println(linkNormal);
System.out.println(defColor);
System.out.println(rowsPerPage);
System.out.println(buttons);
System.out.println();
Object o;
Iterator it = config.getKeys("colors.");
while(it.hasNext()){
System.out.println(it.next());
}
}
}
config.xml
<?xml version="1.0" encoding="UTF-8"?>
<gui-definition>
<colors>
<background>#808080</background>
<text>#000000</text>
<header>#008000</header>
<link normal="#000080" visited="#800080"/>
<default>${colors.header}</default>
</colors>
<rowsPerPage>15</rowsPerPage>
<buttons>
<name>OK,Cancel,Help</name>
</buttons>
<numberFormat pattern="###\,###.##"/>
</gui-definition>
Log
#808080 #000000 #000080 #008000 15 [OK, Cancel, Help] colors.background colors.text colors.header colors.link[@normal] colors.link[@visited] colors.default
References
Tags: javaee