terça-feira, 27 de dezembro de 2011

URL encoding + erro 406 Http

Quando tentamos conectar através de uma URL passando parâmetros é importante que não existam espaços em branco nestes parâmetros.
Para resolver essa situação, o atributo em questão deve ser tratado da seguinte forma:
texto = texto.replaceAll(" ","%20");


Mais explicações sobre caracteres de escape que devem ser utilizados na passagem de parâmetros pode ser encontrado aqui e principalmente neste excelente link.


Quando tentei utilizar a chamada sem o encoding devido tive como retorno o erro http 406 ("Not Acceptable")
Mais explicações sobre retornos HTTP podem ser encontrados aqui.

Conectar URL através de um proxy em Java

import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Properties;

public class Main{

  public static void main(String s[]) throws Exception{
    try {
      Properties systemSettings = System.getProperties();
      systemSettings.put("proxySet""true");
      systemSettings.put("http.proxyHost""proxy.mycompany.local");
      systemSettings.put("http.proxyPort""80");

      URL u = new URL("http://www.java.com");
      HttpURLConnection con = (HttpURLConnectionu.openConnection();
      sun.misc.BASE64Encoder encoder = new sun.misc.BASE64Encoder();
      String encodedUserPwd = encoder.encode("domain\\username:password".getBytes());
      con.setRequestProperty("Proxy-Authorization""Basic " + encodedUserPwd);
      con.setRequestMethod("HEAD");
      System.out.println(con.getResponseCode() " : " + con.getResponseMessage());
      System.out.println(con.getResponseCode() == HttpURLConnection.HTTP_OK);
    catch (Exception e) {
      e.printStackTrace();
      System.out.println(false);
    }
  }
}


A fonte desta solução é o excelente site java2s.comCódigo

Download Excel (Csv) utilizando Ajax e Json

Em um dos projetos que trabalho precisei realizar o download de arquivo excel via Ajax, quando me deparei com um problema. O Ajax não trabalha com retorno binário. Ele trabalha apenas com retorno texto ou XML.


Para resolver essa situação, escrevi um código utilizando JSON + Ajax + Dwr que resolveu meu problema.



  1. Criei uma ƒunção javascript que chama uma servlet passando um objeto Json como atributo do request.
  2. A Servlet convert esse objeto JSON em um arquivo csv e devolve como resposta.



Segue trecho de código:
//função javascript
function exportarGridReclamacoesExcel(){
    exibirCarregando(true);    
    var REQUEST;
    if (window.XMLHttpRequest) {
        REQUEST = new XMLHttpRequest();        
    } else if (window.ActiveXObject) {
        REQUEST = new ActiveXObject("Microsoft.XMLHTTP");
    }
    REQUEST.open('POST', '/sistema/modulo/ExportacaoGridExcel?tipoExportacao=reclamacoes&dadosRelatorio',false);
    REQUEST.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
    
    var objetoJson = JSON.stringify(markersAgrupados);
    REQUEST.send(objetoJson);
    if(REQUEST.readyState == 4 && REQUEST.status == 200) {
        alert(REQUEST.responseText);
        exibirCarregando(false);    
    }


//código da servlet


protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
       // PrintWriter out = response.getWriter();
        try{                        
            response.setContentType("text/csv");
            response.setHeader("Content-Disposition","attachment; filename=relatorio_grid.csv");
            response.setHeader("pragma","public");
            response.setHeader("pragma","no-cache");
            response.setHeader("Cache-Control","cache");
            response.setHeader("Cache-Control","must-revalidate");

            String tipoExportacao = request.getParameter("tipoExportacao");
            String conteudoExportacao = (String) request.getAttribute("dadosRelatorio");
            JSONArray listaObjetos = new JSONArray(conteudoExportacao);
            Writer fw = new OutputStreamWriter(response.getOutputStream());            
            fw.write("Data Reclamação;Login;Numero da Reclamação;Número da Linha;Plano;Reclamação;Tecnologia;Tipo Ocorrência;Pesquisa Realizada;Localização Selecionada;Cobertura GSM;Cobertura 3G;BIOR;Latitude;Longitude\n");
            fw.flush();
            fw.close();
        }catch (Exception ex) {
            ex.printStackTrace();
           // fw.println("Erro ao tentar gerar relatório dos grids\n" + ex.getMessage());
        }finally {
            //fw.close();
        }
    }
    
     @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            processRequest(request, response);
        }

        /** 
         * Handles the HTTP POST method.
         * @param request servlet request
         * @param response servlet response
         * @throws ServletException if a servlet-specific error occurs
         * @throws IOException if an I/O error occurs
         */
        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            processRequest(request, response);
        }


Referências dos problemas:
http://www.w3.org/TR/XMLHttpRequest/#the-send-method
http://www.zachhunter.com/2011/06/json-to-csv/
http://www.zachhunter.com/2010/11/download-json-to-csv-using-javascript/
http://www.ibm.com/developerworks/web/library/j-ajax2/
http://www.ibm.com/developerworks/web/library/wa-ajaxintro3/
http://www.json.org/

Como escrever excel com POI com mais de 65.000 linhas

O arquivo excel com extensão xls (office 2003) possui um limite de 65.536 linhas por 256 colunas (fonte com detalhe completo sobre limites do arquivo). Já o xlsx (> Office 2007) esse limite foi modificado para 1.048.576 linhas por 16.384 colunas. Utilizando arquivos no padrão xlsx é possível tornar a escrita de arquivos excel através de código Java de maneira mais rápida e com suporte a maior quantidade de linhas.
Para quem tem essa facilidade (clientes que tem essa versão do Office, > 2007) vale a pena a modificação pois o ganho em desempenho é muito válido.


A modificação consiste em escrever em um XML o conteúdo e deixar que o POI cuide da transformação para o XLSX.
Achei bastante interessante e me ajudou.


O código segue abaixo (referência do código):


/* ====================================================================
   Licensed to the Apache Software Foundation (ASF) under one or more
   contributor license agreements.  See the NOTICE file distributed with
   this work for additional information regarding copyright ownership.
   The ASF licenses this file to You under the Apache License, Version 2.0
   (the "License"); you may not use this file except in compliance with
   the License.  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
==================================================================== */

package org.apache.poi.xssf.usermodel.examples;

import java.io.*;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.xssf.usermodel.*;

/**
 * Demonstrates a workaround you can use to generate large workbooks and avoid OutOfMemory exception.
 *
 * The trick is as follows:
 * 1. create a template workbook, create sheets and global objects such as cell styles, number formats, etc.
 * 2. create an application that streams data in a text file
 * 3. Substitute the sheet in the template with the generated data
 *
 * 
 *      Since 3.8-beta3 POI provides a low-memory footprint SXSSF API which implementing the "BigGridDemo" strategy.
 *      XSSF is an API-compatible streaming extension of XSSF to be used when
 *      very large spreadsheets have to be produced, and heap space is limited.
 *      SXSSF achieves its low memory footprint by limiting access to the rows that
 *      are within a sliding window, while XSSF gives access to all rows in the
 *      document. Older rows that are no longer in the window become inaccessible,
 *      as they are written to the disk.
 * 


 * See 
 *     http://poi.apache.org/spreadsheet/how-to.html#sxssf.

 *
 * @author Yegor Kozlov
 */
public class BigGridDemo {
    private static final String XML_ENCODING = "UTF-8";
    
    public static void main(String[] args) throws Exception {

        // Step 1. Create a template file. Setup sheets and workbook-level objects such as
        // cell styles, number formats, etc.

        XSSFWorkbook wb = new XSSFWorkbook();
        XSSFSheet sheet = wb.createSheet("Big Grid");

        Map styles = createStyles(wb);
        //name of the zip entry holding sheet data, e.g. /xl/worksheets/sheet1.xml
        String sheetRef = sheet.getPackagePart().getPartName().getName();

        //save the template
        FileOutputStream os = new FileOutputStream("template.xlsx");
        wb.write(os);
        os.close();

        //Step 2. Generate XML file.
        File tmp = File.createTempFile("sheet", ".xml");
        Writer fw = new OutputStreamWriter(new FileOutputStream(tmp), XML_ENCODING);
        generate(fw, styles);
        fw.close();

        //Step 3. Substitute the template entry with the generated data
        FileOutputStream out = new FileOutputStream("big-grid.xlsx");
        substitute(new File("template.xlsx"), tmp, sheetRef.substring(1), out);
        out.close();
    }

    /**
     * Create a library of cell styles.
     */
    private static Map createStyles(XSSFWorkbook wb){
        Map styles = new HashMap();
        XSSFDataFormat fmt = wb.createDataFormat();

        XSSFCellStyle style1 = wb.createCellStyle();
        style1.setAlignment(XSSFCellStyle.ALIGN_RIGHT);
        style1.setDataFormat(fmt.getFormat("0.0%"));
        styles.put("percent", style1);

        XSSFCellStyle style2 = wb.createCellStyle();
        style2.setAlignment(XSSFCellStyle.ALIGN_CENTER);
        style2.setDataFormat(fmt.getFormat("0.0X"));
        styles.put("coeff", style2);

        XSSFCellStyle style3 = wb.createCellStyle();
        style3.setAlignment(XSSFCellStyle.ALIGN_RIGHT);
        style3.setDataFormat(fmt.getFormat("$#,##0.00"));
        styles.put("currency", style3);

        XSSFCellStyle style4 = wb.createCellStyle();
        style4.setAlignment(XSSFCellStyle.ALIGN_RIGHT);
        style4.setDataFormat(fmt.getFormat("mmm dd"));
        styles.put("date", style4);

        XSSFCellStyle style5 = wb.createCellStyle();
        XSSFFont headerFont = wb.createFont();
        headerFont.setBold(true);
        style5.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
        style5.setFillPattern(XSSFCellStyle.SOLID_FOREGROUND);
        style5.setFont(headerFont);
        styles.put("header", style5);

        return styles;
    }

    private static void generate(Writer out, Map styles) throws Exception {

        Random rnd = new Random();
        Calendar calendar = Calendar.getInstance();

        SpreadsheetWriter sw = new SpreadsheetWriter(out);
        sw.beginSheet();

        //insert header row
        sw.insertRow(0);
        int styleIndex = styles.get("header").getIndex();
        sw.createCell(0, "Title", styleIndex);
        sw.createCell(1, "% Change", styleIndex);
        sw.createCell(2, "Ratio", styleIndex);
        sw.createCell(3, "Expenses", styleIndex);
        sw.createCell(4, "Date", styleIndex);

        sw.endRow();

        //write data rows
        for (int rownum = 1; rownum < 100000; rownum++) {
            sw.insertRow(rownum);

            sw.createCell(0, "Hello, " + rownum + "!");
            sw.createCell(1, (double)rnd.nextInt(100)/100, styles.get("percent").getIndex());
            sw.createCell(2, (double)rnd.nextInt(10)/10, styles.get("coeff").getIndex());
            sw.createCell(3, rnd.nextInt(10000), styles.get("currency").getIndex());
            sw.createCell(4, calendar, styles.get("date").getIndex());

            sw.endRow();

            calendar.roll(Calendar.DAY_OF_YEAR, 1);
        }
        sw.endSheet();
    }

    /**
     *
     * @param zipfile the template file
     * @param tmpfile the XML file with the sheet data
     * @param entry the name of the sheet entry to substitute, e.g. xl/worksheets/sheet1.xml
     * @param out the stream to write the result to
     */
 private static void substitute(File zipfile, File tmpfile, String entry, OutputStream out) throws IOException {
        ZipFile zip = new ZipFile(zipfile);

        ZipOutputStream zos = new ZipOutputStream(out);

        @SuppressWarnings("unchecked")
        Enumeration en = (Enumeration) zip.entries();
        while (en.hasMoreElements()) {
            ZipEntry ze = en.nextElement();
            if(!ze.getName().equals(entry)){
                zos.putNextEntry(new ZipEntry(ze.getName()));
                InputStream is = zip.getInputStream(ze);
                copyStream(is, zos);
                is.close();
            }
        }
        zos.putNextEntry(new ZipEntry(entry));
        InputStream is = new FileInputStream(tmpfile);
        copyStream(is, zos);
        is.close();

        zos.close();
    }

    private static void copyStream(InputStream in, OutputStream out) throws IOException {
        byte[] chunk = new byte[1024];
        int count;
        while ((count = in.read(chunk)) >=0 ) {
          out.write(chunk,0,count);
        }
    }

    /**
     * Writes spreadsheet data in a Writer.
     * (YK: in future it may evolve in a full-featured API for streaming data in Excel)
     */
    public static class SpreadsheetWriter {
        private final Writer _out;
        private int _rownum;

        public SpreadsheetWriter(Writer out){
            _out = out;
        }

        public void beginSheet() throws IOException {
            _out.write("" +
                    "" );
            _out.write("\n");
        }

        public void endSheet() throws IOException {
            _out.write("");
            _out.write("");
        }

        /**
         * Insert a new row
         *
         * @param rownum 0-based row number
         */
        public void insertRow(int rownum) throws IOException {
            _out.write("\n");
            this._rownum = rownum;
        }

        /**
         * Insert row end marker
         */
        public void endRow() throws IOException {
            _out.write("\n");
        }

        public void createCell(int columnIndex, String value, int styleIndex) throws IOException {
            String ref = new CellReference(_rownum, columnIndex).formatAsString();
            _out.write("");
            _out.write(""+value+"");
            _out.write("");
        }

        public void createCell(int columnIndex, String value) throws IOException {
            createCell(columnIndex, value, -1);
        }

        public void createCell(int columnIndex, double value, int styleIndex) throws IOException {
            String ref = new CellReference(_rownum, columnIndex).formatAsString();
            _out.write("");
            _out.write(""+value+"");
            _out.write("");
        }

        public void createCell(int columnIndex, double value) throws IOException {
            createCell(columnIndex, value, -1);
        }

        public void createCell(int columnIndex, Calendar value, int styleIndex) throws IOException {
            createCell(columnIndex, DateUtil.getExcelDate(value, false), styleIndex);
        }
    }
}

org.directwebremoting.extend.MarshallException

Este ano de 2011, encontrei esse problema ao utilizar o DWR e identifiquei a causa raiz como a ausência de métodos get/set para propriedades anotadas como @RemoteProperty. Ao tentar estabelecer a comunicação javascript -> Java acontecia esse erro.
#ficaadica


AVISO: Marshalling exception
org.directwebremoting.extend.MarshallException: Error marshalling objeto.Model: null. See the logs for more details.
        at org.directwebremoting.impl.PropertyDescriptorProperty.setValue(PropertyDescriptorProperty.java:89)
        at org.directwebremoting.convert.BasicObjectConverter.convertInbound(BasicObjectConverter.java:140)
        at org.directwebremoting.dwrp.DefaultConverterManager.convertInbound(DefaultConverterManager.java:159)
        at org.directwebremoting.dwrp.BaseCallMarshaller.marshallInbound(BaseCallMarshaller.java:155)
        at org.directwebremoting.servlet.PlainCallHandler.handle(PlainCallHandler.java:44)
        at org.directwebremoting.servlet.UrlProcessor.handle(UrlProcessor.java:101)
        at org.directwebremoting.servlet.DwrServlet.doPost(DwrServlet.java:146)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:754)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:847)
        at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1523)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:279)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:188)
        at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:641)
        at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:97)
        at com.sun.enterprise.web.PESessionLockingStandardPipeline.invoke(PESessionLockingStandardPipeline.java:85)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:185)
        at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:325)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:226)
        at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:165)
        at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:791)
        at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:693)
        at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:954)
        at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:170)
        at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:135)
        at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:102)
        at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:88)
        at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:76)
        at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:53)
        at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:57)
        at com.sun.grizzly.ContextTask.run(ContextTask.java:69)
        at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:330)
        at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:309)
        at java.lang.Thread.run(Thread.java:662)
Caused by: java.lang.IllegalArgumentException
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.directwebremoting.impl.PropertyDescriptorProperty.setValue(PropertyDescriptorProperty.java:81)
        ... 32 more

AVISO: --Erroring: batchId[2] message[org.directwebremoting.extend.MarshallException: Error marshalling com.vivo.smap.novosmapfalhas.model.SmapFiltroModel: null. See the logs for more details.]