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