Thursday, October 24, 2013

How to get TCL "huddle" from inside JACL

JACL can parse the "old TCL" dict format.

TCL has a very nice library to deal with this kind of format, called Huddle. Huddle is part of the tcllib package and, specifically, a very useful command called "compile" was added after version 1.11 (I don't know exactly which version, but it's before 1.15).

Huddle relies on another TCL library called "dict". This library was incorporated into the language since TCL 8.5.

The problem here now is Jacl, the java TCL interpreter. It was not build with "dict" support, so you need a patch to include the "dict" command (http://sourceforge.net/p/tcljava/patches/15).

Finally, jacl identifies itself as "less than TCL 8.5", and the first thing that huddle checks is if it needs "dict". See $PATHTO/tcllib1.15/yaml/huddle.tcl

right in the beginning, you'll see
if { [package vcompare [package provide Tcl] 8.5] < 0 } {
    package require dict
}

to avoid the annoying message of "Exception in thread "main" tcl.lang.TclException:
can't find package dict", you MUST comment this part of the huddle code, so it will be just like this

if { [package vcompare [package provide Tcl] 8.5] < 0 } {
#    package require dict
}

Then you'll be able to write java code just like this

        String x = "{username {root}} {shadow {x}}";
        interp.eval("lappend auto_path \"LIB\"".replaceAll("LIB", "/PATH/TO/tcllib1.15/"));     
        interp.eval("package require huddle");
        interp.eval("set x [ huddle compile { list { dict } }  {"+x+"} ]");
        interp.eval("set i [ huddle jsondump $x ]");
        String json = interp.getVar("i", 0).toString();
        System.out.println(json);

to get this kind of result

[
  {"username": "root"},
  {"shadow": "x"}
]

ps. Tom kindly answered my email at the tcl/java users mailing list, suggesting a cleaner and more elegant solution for this, that I'll certainly evaluate. Thanks Tom!

Friday, October 11, 2013

I cannot audit with TomEE, so what should I do?

Basic CRUD operations audit, even being an old problem of many apps, is still unresolved by most java frameworks (just as reloadable config files), which gives me the sensation that we, programmers, are sometimes running in circles.

So I've filled a bug for TomEE (probably related to eclipse and OSGI?) because openJPA @Auditable seems not to work on any tomee distribution I can test (1.5.2, 1.5.3, 1.6.0). The sample code you can get at the bug report.

What is the workaround?

Interceptors to the rescue.

First, add this to your src/META-INF/beans.xml

<beans>
  <interceptors>
    <class>AuditInterceptor</class>
  </interceptors>
</beans>


To implement the interceptor, you need an annotation and the interceptor itself

@InterceptorBinding
@Target({TYPE, METHOD})
@Retention(RUNTIME)
public @interface Audit {
}


and


@Interceptor
@Audit
public class AuditInterceptor implements Serializable{

    private static final long serialVersionUID = 6988858067583207506L;
   
    @EJB
    private LogManagerEJB logManagerEJB;

    private JSONSerializer json = new JSONSerializer();

    @AroundInvoke
    public Object logMethodEntry(InvocationContext ctx) throws Exception {
       
        String action = ctx.getMethod().getDeclaringClass()+"."+ctx.getMethod().getName();
        String subject = json.serialize(ctx.getParameters());
        logManagerEJB.saveTelemetryLogs(action, subject);

        return ctx.proceed();
    }
}


and logManagerEJB looks like

    public void saveTelemetryLogs(String action, String subject) {
        TelemetryLog telemetryLog = new TelemetryLog();
        telemetryLog.setAction(action);
        telemetryLog.setActor(JSFUtil.getAuthenticatedUser().getUsername());
        telemetryLog.setSubject(subject);
        telemetryLog.setTs(new Timestamp(System.currentTimeMillis()));
        this.baseService.getTelemetryLogDAO().add(telemetryLog);
    }


DAO is omitted because it's just entity.persist() on the object.

Then you add the @Audit annotation for all methods you want to audit (usually in your business logic layer)

    @Audit
    public void saveUserGroupAccess(UserGroupAccessPK pk) {
        UserGroupAccess uga = new UserGroupAccess();
        uga.setPk(pk);
        this.baseService.getUserGroupAccessDAO().add(uga );       
    }





Wednesday, October 9, 2013

RUP humps are not that accurate

Actually, it was never intended to be, as many can think :-)

http://en.wikipedia.org/wiki/RUP_hump

(...)Over the years, this diagram has become increasingly connected with RUP, so that sometimes it is regarded as a logo for the process. The chart has then since been spread widely over the Internet. A known misconception about the hump chart is, that it is based on empirical assessment of actual projects rather than on Kruchten's educated guess.
...I always insisted that these humps were just illustrative, as well as the number and duration of iterations shown on the horizontal axis, but many people wanted to read much more meaning in that diagram than I intended. For example, a gentleman from Korea once wrote me to ask for a large original diagram to measure the heights, and ‘integrate' the area under the humps, to help him do project estimation...(...)

 We use to call them "the whales" :-)


Thursday, October 3, 2013

JSF Generic Searchable Table with Primefaces

Just because http://www.primefaces.org/showcase/ui/datatableDynamicColumns.jsf is just "a little" over for me.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:p="http://primefaces.org/ui">
<h:head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>Search</title>
</h:head>
<h:body>
    <h:form id="sm">
        <p:spacer height="10" />
        <p:fieldset legend="Search">
        <p:panel>
            <p:dataTable
                var="dataRow"
                value="#{MB.data}"
                paginator="true" rows="10" 
                paginatorTemplate="{RowsPerPageDropdown} {FirstPageLink} {PreviousPageLink} {CurrentPageReport} {NextPageLink} {LastPageLink}" 
                rowsPerPageTemplate="5,10,15"   
                   emptyMessage="No sites found with given criteria"
                   filteredValue="#{MB.filteredData}"
                   sortBy="#{MB.defaultColumnSort}"
                id="rowsTable">
                
                <p:columns value="#{MB.columns}" var="column" columnIndexVar="colIndex" sortBy="#{column}" filterBy="#{column}" filterMatchMode="contains"> 
                    <f:facet name="header">#{column}</f:facet> 
                    #{dataRow[column]} 
                   </p:columns>
                    
           </p:dataTable>
        </p:panel>
        </p:fieldset>
           
    </h:form>
</h:body>
</html>


and the MB goes like this

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

@ManagedBean
@SessionScoped
public class MB implements Serializable {
   
    private static final long serialVersionUID = -3964302189254255157L;
   
    private List<String> columns = new ArrayList<String>();
   
    private List<Map<String,String>> data;
   
    private List<Map<String,String>> filteredData;
   
    private String defaultColumnSort;

    public void clear() {
        data = null;
        setFilteredData(null);
    }

    @EJB
    private EJB ejb;
   
    @PostConstruct
    public void init() {
        try {
            open("xyz");
        } catch (Exception e) {
            e.printStackTrace();
            JSFUtil.addErrorMessage("Could not list");
        }
    }
   

    /**
     * entry point
     *
     * @return
     */
    public String open(String table){
        try {
            columns = ejb.getTableColumns(table);
            data = ejb.getTable(table);
            filteredData = data;
            defaultColumnSort = columns.get(0);
            return "thispage";
        } catch (Exception e) {
            e.printStackTrace();
            JSFUtil.addErrorMessage("Could not open");
        }
        return null;
    }
   
    public List<Map<String, String>> getData() {
        return data;
    }

    public void setData(List<Map<String, String>> data) {
        this.data = data;
    }


    public List<Map<String,String>> getFilteredData() {
        return filteredData;
    }


    public void setFilteredData(List<Map<String,String>> filteredData) {
        this.filteredData = filteredData;
    }


    public List<String> getColumns() {
        return columns;
    }


    public void setColumns(List<String> columns) {
        this.columns = columns;
    }


    public String getDefaultColumnSort() {
        return defaultColumnSort;
    }


    public void setDefaultColumnSort(String defaultColumnSort) {
        this.defaultColumnSort = defaultColumnSort;
    }
}