Sunday, April 28, 2013

Exporting smartgwt grid data to csv

I needed the feature to export the ListGrid Data to Excel file, but this feature is available only for smargwt PRO and EE version. I explored if there is any way can download through js, since the data is already present with client. but couldn't find because Javascript by design do not have access to local disk. and some browsers has limited support for data URI.

so I decided to send the content back to server (I feel its not correct way), and servlet write back data.

here is example for adding context menu to grid with export options.


import com.google.gwt.user.client.ui.FormPanel;
import com.google.gwt.user.client.ui.Hidden;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.smartgwt.client.types.Autofit;
import com.smartgwt.client.types.ListGridFieldType;
import com.smartgwt.client.types.SelectionStyle;
import com.smartgwt.client.widgets.grid.ListGrid;
import com.smartgwt.client.widgets.grid.ListGridField;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import com.smartgwt.client.widgets.menu.Menu;
import com.smartgwt.client.widgets.menu.MenuItem;
import com.smartgwt.client.widgets.menu.events.ClickHandler;
import com.smartgwt.client.widgets.menu.events.MenuItemClickEvent;

public class ExportableGrid extends ListGrid{
    public ListGridRecord[] records;

    public ExportableGrid() {
        setHeight100();
        setWidth100();
       
        addContextMenu();
       
    }
   
    private StringBuilder exportCSV(boolean all) {
        StringBuilder stringBuilder = new StringBuilder(); // csv data in here
        ListGridField[] fields = getFields();
        for (int i = 0; i < fields.length; i++) {
            ListGridField listGridField = fields[i];
            if(!listGridField.getType().equals(ListGridFieldType.IMAGE) && !listGridField.getName().equalsIgnoreCase("$72V")) {
                stringBuilder.append("\"");
                stringBuilder.append(listGridField.getName().toUpperCase());
                stringBuilder.append("\",");
            }
        }
        stringBuilder.deleteCharAt(stringBuilder.length() - 1); // remove last
        stringBuilder.append("\n");

        ListGridRecord[] recs = null;
        if (all) {
            recs = getRecords();
        } else {
            recs = getSelection();
        }
        for (int i = 0; i < recs.length; i++) {
            ListGridRecord listGridRecord = recs[i];
            ListGridField[] listGridFields = getFields();
            for (int j = 0; j < listGridFields.length; j++) {
                ListGridField listGridField = listGridFields[j];
                if(!listGridField.getType().equals(ListGridFieldType.IMAGE) && !listGridField.getName().equalsIgnoreCase("$72V")) {
                    stringBuilder.append("\"");
                    String val = listGridRecord.getAttribute(listGridField.getName());
                    stringBuilder.append(val);
                    stringBuilder.append("\",");
                }
            }
            stringBuilder.deleteCharAt(stringBuilder.length() - 1); // remove last ","
            stringBuilder.append("\n");
        }
        return stringBuilder;
    }
   
    public void export(StringBuilder stringBuilder, String fileExt) {   
        final FormPanel formPanel = new FormPanel();
        formPanel.setAction(getExportFileUrl());
        formPanel.setMethod(FormPanel.METHOD_POST);
       
        VerticalPanel verticalPanel = new VerticalPanel();
   
        Hidden data = new Hidden();
        data.setName("exprtData");
        data.setValue(stringBuilder.toString());
        verticalPanel.add(data);
       
        Hidden exportType = new Hidden();
        exportType.setName("exportType");
        exportType.setValue("clientExport");
        verticalPanel.add(exportType);
       
        Hidden fileType = new Hidden();
        fileType.setName("fileType");
        fileType.setValue(fileExt);
        verticalPanel.add(fileType);
   
        formPanel.add(verticalPanel);
        formPanel.setVisible(false);
        RootPanel.get().add(formPanel);   
        formPanel.submit();
       
    }
   
    public void addContextMenu() {
        setCanDragSelect(true); 
        setSelectionType(SelectionStyle.MULTIPLE);
        setAutoFitData(Autofit.VERTICAL); 
        setShowRollOver(false);
       
        MenuItem csvAll = new MenuItem("Export all as CSV");
        csvAll.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(MenuItemClickEvent event) {
                export(exportCSV(true), "csv");
            }
        });
       
        MenuItem csvSelected = new MenuItem("Export selected as CSV");
        csvSelected.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(MenuItemClickEvent event) {
                export(exportCSV(false), "csv");
            }
        });
       
        Menu menu = new Menu(); 
        menu.setWidth(150); 
        menu.addItem(csvAll); 
        menu.addItem(csvSelected); 
       
        setContextMenu(menu);
    }
   
    private String getExportFileUrl() {
        StringBuilder urlBuilder = new StringBuilder("exportFiles/exportFiles.htm?");
        return urlBuilder.toString();
    }
   
    public void updateData() {
        records = new ListGridRecord[2];
        for (int i=0; i            records[i] = new ListGridRecord();
            records[i].setAttribute("Test", "Test"+i);
        }
    }
}


My server side controller looks like below...

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;

public class Exporter extends AbstractController {

    @Override
    protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
        String exportType = request.getParameter("exportType");
        if(exportType != null && exportType.equalsIgnoreCase("clientExport")){
            String fileType = request.getParameter("fileType");
            String exportData = request.getParameter("exprtData");
            byte[] bytes = exportData.getBytes();
            response.setContentLength((int) bytes.length);
            response.setHeader("Content-type" ,"application/"+fileType);
            String fileNameHeader = "attachment; filename=download."+fileType ;
            response.setHeader("Content-disposition" , fileNameHeader); 
            ServletOutputStream  stream = response.getOutputStream();   
            stream.write(bytes);
        }
        return null;
    }
}

Saturday, April 27, 2013

Water mark plugin for flot

recently i needed to add some marker to all graphs in my project, since many people are using our tool and taking print-screen of the graphs for mail chain, without giving any credit :P.
so we decided to add marker text to all graphs, I found there is no straight forward way or plug-in available to add water mark or text to flot graphs. then I explored the flot API, found its well designed and easy to add plug-ins.
i created jquery.flot.marker.js
(function ($) {
    var options = {
        marker: {
            mode: null, // thinking to use for image based , text base markers
            value: "My Graph", // default value
            color: "rgba(100, 100, 100, 0.80)",
            font: "14px Arial",
            position: 'top-right' //top-left, bottom-right, bottom-left
        }
    };
    function init(plot) {
        plot.hooks.draw.push(function (plot, ctx) {
            var c = plot.getOptions().marker;
            if (!c.mode)
                return;
            //alert(plot.offset().left);
            ctx.save();
            ctx.font=c.font;
            var w;
            var h;
            if((c.position=='top-right') || (c.position=='bottom-right')) {
                w = plot.getPlaceholder().width()*0.88;
            } else {
                w = plot.offset().left-40;
            }
            if((c.position=='top-left') || (c.position=='top-right')) {
                h = 45;
            } else {
                h = plot.height();
            }
            //alert(h);
            ctx.fillStyle=c.color;
            ctx.fillText(c.value, w, h);

        });
    }
    $.plot.plugins.push({
        init: init,
        options: options,
        name: marker,
        version: '1.0'
    });
})(jQuery);
Then including plug-in file helped me to get marker on all graphs.
<script language="javascript" type="text/javascript" src="../jquery.flot.marker.js"></script>
  plot = $.plot($("#placeholder"), 
                      ${data}, 
                          marker: {
                                mode: "text",
                                value:"My Graph",
                                color:"rgba(100, 100, 100, 0.60)",
                                font: "10px Lucida Sans Unicode",
                                position: 'top-right'                           
                            },

                            grid: { hoverable: true, autoHighlight: false },
                            yaxis: { min: -1.2, max: 1.2 }
                        });

Saturday, February 16, 2013

Generating Unique Ids across multiple JVM and over restart using long data type only

/*
* Long.MAX_VALUE : 9 2 2 3 3 7 2 0 3 6 8 5 4 7 7 5 8 0 7  Total 19 digits
*
* lets use fist 2 digit for peer id
* now we are left with 17 digits
* if we restrict peer id between 10 - 91
*
* then rest all 17 could be all 9's to hold in long
*
* lets create 10 UIDGenerator Objects each with unique id [0-9]
*
* so now we are left with 16 digits
*
* so if we start at some time
* System.currentTimeMillis() : 1360443174566 (13 digit)
*
* ID : XXY1360443174566TTT
*                         XX : peer id
*                          Y : object instance ID
*     1360443174566 : System.currentMillis(); //seed will change on every restart
*                       TTT : 000     Allowing to generate 999 message max per millisecond

* so we can generate max 999 unique ids per millisecond
* means safe to generate approx: 1000 per millisecond, to overcome uniqueness over jvm restart
*
* we can generate 9999999999999999-1360443174566000 = 863955682543999 * 10 unique ids
* (Aprox:8000 Trillion )before getting duplicate if we start on 1360443174566
*
* Possible Failure
* 1. system time is reset
* 2. generating more than 10000 per millisecond then does not gives guarantee over jvm restart.
*/

public class UIDUtil {
    private static UIDUtil instance;
    private UIDGenerator[] uidGenerators;
    private static short MAX_ALLOWED_PEER_ID = 91;
    private static short MIN_ALLOWED_PEER_ID = 10;
    private long totalIdsGenerated;
    private long startTime;
    private UIDUtil() {
        short peerId = loadPeerId();
        uidGenerators = new UIDGenerator[10];
        for(byte i=0; i<10; i++) {
            uidGenerators[i] = new UIDGenerator(peerId, i);
        }
        startTime = System.currentTimeMillis();
    }
    public static UIDUtil getInstance() {
        if(instance == null)
            instance = new UIDUtil();
        return instance;
    }
    public long getNextId() {
        return uidGenerators[(int) ((totalIdsGenerated++)%10)].getNextID();
    }
    private short loadPeerId() {
        //TODO:    load peerId from properties, db, remote etc
        short peerId = 90;
        if(peerId > MAX_ALLOWED_PEER_ID || peerId < MIN_ALLOWED_PEER_ID) {
            peerId = (short) (MIN_ALLOWED_PEER_ID + (short)(Math.random() * ((MAX_ALLOWED_PEER_ID - MIN_ALLOWED_PEER_ID) + 1)));
            try {
                throw new Exception("Peer ID must be in the range : "+MIN_ALLOWED_PEER_ID+"-"+MAX_ALLOWED_PEER_ID+" inclusive");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return peerId;
    }
    public long getTotalIdsGenerated() {
        return totalIdsGenerated;
    }
    public double getIdProducingRate() {
        long elapsedTime = System.currentTimeMillis()-startTime;
        if(elapsedTime == 0)
            elapsedTime = 1;
        return (double) (totalIdsGenerated/elapsedTime)*1000;
    }
}

 

public class UIDGenerator {
    private long variableId ;
    private long fixedId;

    private static long MAX_ALLOWED_VARIABLE_ID = 9999999999999999l;
    public UIDGenerator(short peerId, byte index) {
        //place peer on proper decimal location
        fixedId = (long) (peerId*Math.pow(10, 17));//exp : 9100000000000000000
        fixedId += (long) (index*Math.pow(10, 16));//exp : 9130000000000000000
        //initialise with 16 digit seed
        variableId = loadVariableId();
    }

    public synchronized long getNextID() {
        long newId = variableId++;
        if(newId == MAX_ALLOWED_VARIABLE_ID) {
            variableId = loadVariableId();
        }
        return (fixedId + newId);
    }

    private long loadVariableId() {
        long currentTime = System.currentTimeMillis();
        long temp = currentTime;
        byte digits = 0;
        while(temp > 0) {
            temp /= 10;
            digits++;
        }
        return (long) (currentTime*Math.pow(10, 16-digits));
    }
}

Search Ranjeet's Blog