Code organization

Contents[Hide]

1. Separation of concerns and code organization.

Following the extreme KISS principle ( https://en.wikipedia.org/wiki/KISS_principle ) a service in LightLink is defined by the presence of a file (or a classpath resource). Just like a static web resource.

On a web server, saving  “resource.json” file into  /www/directory creates a static service “/directory/resource.json

With LightLink, saving a JS file in “WEB-INF/lightlink/package/service.js” (or in classpath:/lightlink/package/service.js) defined a service “/package /service” that will execute the JS code on the JVM, using the new Nashorn JS engine.

response.writeObject("greeting","Hello "+ p.name+" !");

Rename that file “service.js” into “service.js.sql” and you get a sql-oriented service acting as a Data Access Controller with the minimum amount of code. 

SELECT * FROM MY_TABLE
<% if (p.name) {  %>
  WHERE NAME=:p.name
<% } %>

Keep both “service.js” and “service.js.sql” in the same package, and the “service.js” will be executed first, performing Security or Business Controller concerns. After successful completion of “service.js”, “service.js.sql” will be invoked to execute the Data Access logic.  

MyApp.getCountries({ address:{country:”USA”} },function(res){console.log(res.resultSet);})

Rename those “service.js” and “service.sql.js” into “service.POST.js” and “service.POST.sql.js” and as a REST Service it will only be available for POST method.

Add “service.postprocess.js” or “service.postprocess.js.sql” to the same package and it will be executed at the end, after Controller and Data Access logic.

 

Add “config.js” or “config.sql.js” or config.POST.js to a root of to any package, and it will be executed before any child package config.js and before all services defined in those packages. GET/POST/PUT/DELETE suffixes indicate matching HTTP methods.

 

 

In the above screenshot, the executions order for POST /package/service will be the following :

  1. /lightlink/config.js,
  2. /lightlink/package/config.js.sql,
  3. /lightlink/package/config.POST.js.sql,
  4. /lightlink/package/service.js
  5. /lightlink/package/service.js.sql
  6. /lightlink/package/service.postprocess.js

Config files are useful for any package level logic (security checks, library functions definition, logging, etc).

More obvious use case for config.js, however is initial datasource configuration (plain jdbc connections, JNDI datasource, DataSource from Spring application context,  JTA transaction, etc… )

Here is a an example of a basic package structure reflecting access control requirements:

 

Each color describes user access level necessary to execute each service.

More examples of config.js:

  • Using DataSource from Spring application Context:

 

var spring = Java.type("org.springframework.web.context.support.WebApplicationContextUtils")
    .getWebApplicationContext(env.getSession().getServletContext());
sql.setConnection(spring.getBean("dataSource").getConnection());
sql.setFetchSize(100);
  • Simple JNDI DataSource and JNDI level transactions around service calls:
sql.setConnection("com.mysql.jdbc.Driver","jdbc:mysql://localhost/light?","root","");
sql.getConnection().setAutoCommit(false);

  • JNDI DataSource and JTA transactions: 
sql.setDataSourceJndi("java:comp/env/jdbc/MainDB");
sql.setFetchSize(100);
tx.useTxJee(); // use JEE transactions
tx.setTxTimeout(30) // timeout in minutes for TransactionManager.setTransactionTimeout(..)

All connections taken from the datasource are closed (returned to the pool in case of DataSource) after service execution.

All transactions (JTA or JDBC) are committed in case of successful execution, if neither tx.setTxRollbackOnly() is called in LightLing API nor JTA transaction is marked for rollback only.