terça-feira, 31 de maio de 2011

Configuração do JDK para o Maven no Eclipse

Ao instalar o plugin do Maven para o Eclipse é solicitado que o Eclipse esteja configurado para trabalhar com JDK. Quando isso não acontece, é exibida uma mensagem semelhante a:


Como a própria mensagem sugere, é preciso realizar duas alterações: uma em Installed JREs e outra em eclipse.ini.

Portanto, cria uma nova entrada em Installed JREs, definindo uma JDK como default do Eclipse.

E em eclipse.ini coloque as linhas da seguinte forma:
-vm
C:\desenvolvimento\java\jdk1.6.0_24\bin\javaw.exe

quinta-feira, 12 de maio de 2011

Execução de comandos de forma síncrona

"Esse post é escrito com a colaboração de "@ed_sobrinho".

O Ed enfrentou uma situação onde ele precisava executar uma exportação de uma base de dados Oracle para um arquivo. Para saber o resultado da exportação (sucesso/erro), seria necessário ler um arquivo de log gerado pela própria rotina de exportação.

A primeira tentativa foi feita com a classe Runtime do Java:

public static void main(String[] args) {
String command = "cmd.exe /c EXP <usuario>/<senha>@<instancia> file=<caminho_do_arquivo_do_dump>.dmp log=<caminho_do_arquivo_do_log>_log.log owner=<owner> STATISTICS=NONE";
try {
Runtime.getRuntime().exec(command);
} catch (IOException e) {
e.printStackTrace();
}
}

Essa implementação não supriu a necessidade porque era preciso tratar como chamada síncrona, ou seja, era necessário aguardar a conclusão da exportação para ler o arquivo de Log. O que não estava acontecendo.

Então, através da biblioteca da Apache Commons Exec, o Ed obteve esse sucesso. O código ficou assim:

public static void main(String[] args) {
String command = "cmd.exe /c EXP <usuario>/<senha>@<instancia> file=<caminho_do_arquivo_do_dump>.dmp log=<caminho_do_arquivo_do_log>_log.log owner=<owner> STATISTICS=NONE";
CommandLine cmdLine = CommandLine.parse(command);
DefaultExecutor executor = new DefaultExecutor();
executor.setExitValue(1);

try {
executor.execute(cmdLine);
} catch (Exception e) {
e.printStackTrace();
}
}

sexta-feira, 11 de março de 2011

Jenkins - Configuração de uma tarefa


Neste post, vamos ver como configurar uma tarefa simples no Jenkins. Para este exemplo, temos:
  • SVN como repositório de código
  • Maven na versão 2.2.1
  • Projeto web construído com maven-archetype-webapp
  • Notificação via email
  • Build periódico
  • Servidor Tomcat versão 6.0.32

Vamos, então, explorar estes tópicos:

Configuração do SVN
Basicamente, você deve inserir a URL do repositório na seção 'Gerenciamento de Código Fonte':
Na versão do Jenkins que usei para este post, precisei instalar Subversion Plugin para poder configurar o SVN na tarefa. Isto é feito no Plugin Manager do Jenkins.
Programação de build
Na seção 'Disparadores de Construção', marque as opções de consulta periódica ao SVN e de build do projeto. E defina a periodicidade da tarefa de acordo com a sintaxe do cron:
Configuração do build pelo Maven
Você deve especificar a versão no Maven que será utilizada no build, assim como o caminho do pom e o goal do ciclo de vida do Maven:
Notificação do resultado do build por email
Você pode especificar vários destinatários do resultado do build:
Deploy do arquivo do projeto no Tomcat
No pacote da integração contínua, está a automatização do deploy de sua aplicação. Neste exemplo, temos um war enviado para o Tomcat:
Lembre-se de configurar os usuários no arquivo tomcat-users.xml:

<tomcat-users>
<role rolename="manager"/>
<role rolename="admin"/>
<user username="admin" password="admin" roles="admin,manager"/>
</tomcat-users>

Jenkins - Configuração do Maven

Para que uma ferramenta de integração contínua seja utilizada com sucesso, é preciso respeitar a premissa de que é necessário utilizar uma ferramenta de build. E basicamente, temos o Ant e o Maven, como opções.
Minha escolha foi pelo Maven, que na minha opinião oferece melhor estrutura e praticidade que o Ant.
Para configurar o Maven no Jenkins, acesse Gerenciar Jenkins --> Configurar Sistema. Na seção Maven desta página, você pode cadastrar as instalações disponíveis do Maven para todas as tarefas controladas pelo Jenkins:

terça-feira, 25 de janeiro de 2011

Alterar titlebar do panel no primefaces

Algumas vezes, gostaríamos de dar outra cor à barra de título do painel no primefaces.

Para fazer isso, precisamos alterar o css do painel, assim:


.ocupada .ui-panel-titlebar {
background: #993333;
color:#CCCCCC;
}


E a declaração do componente fica assim:

<p:panel header="Título" styleClass="ocupada">
</p:panel>


Desta forma, apenas o painel onde você aplicar o style sofrerá alteração.

segunda-feira, 17 de janeiro de 2011

Assinatura Digital de Documentos

Fiz este pequeno projeto para auxiliar na tarefa de assinatura digital de documentos.

O projeto consiste de DispatchActions, Servlets e Applets.

Os testes foram feitos com o token EPASS2000 da Pronova. Onde o funcionamento depende de dois arquivos principais: pkcs11.cfg (arquivo de configuração) e ngp11v211.dll.

Aqui eu separo os elementos do projeto em várias partes, caso o link de download fique quebrado.

Applet
O módulo de applet está com as classes SignatureManager e TokenSignerApplet.

package br.com.certificacao;

import java.applet.Applet;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.Security;
import java.security.cert.Certificate;
import java.util.Enumeration;

/**
 * Classe responsável por gerenciar a assinatura contida em 
 * um token. É necessário efetuar o login no dispositivo
 * antes de utilizá-lo.
 */
public class SignatureManager {
 private KeyStore keyStore;
 private char[] PIN;
  
 private final String CONFIG_DIR_NAME = "C:\\assinatura\\";
 private final String CONFIG_FILE_NAME = "pkcs11.cfg";
 private final String WINDOWS_DIR_NAME = "C:\\Windows\\System32\\";
 private final String DLL_FILE_NAME = "ngp11v211.dll";
 
 private String arquivoDLLApp = "";
  
 
 public void createTokenFiles(String urlApp){
  criarDiretorio();
  criarDLL(urlApp);
  createConfigFile();
 }
 
 private void criarDiretorio(){
  try {
   File dir = new File(CONFIG_DIR_NAME);
   if (!dir.exists()){
    dir.mkdirs();
   }
  }catch (Exception e) {
  }
 }
 
 private void createConfigFile(){
  try {
   File file = new File(CONFIG_DIR_NAME + CONFIG_FILE_NAME);
   file.createNewFile();
   FileWriter writer = new FileWriter(file);
   writer.write("name = PKCS11\n");
   writer.write("library = " + arquivoDLLApp + "\n");
   writer.write("slot = 1\n");
   writer.write("disabledMechanisms = {\n");
   writer.write("    CKM_SHA1_RSA_PKCS\n");
   writer.write("}\n");
   writer.flush();
   writer.close();
  } catch (IOException e) {
   e.printStackTrace();
  }
 }
    
    /**
  * Metodo que verifica a situação da DLL do Token e-Pass da Pronova.
  * Verifica se a DLL já está criada na pasta Windows\System32.
  * Se o arquivo não existir, cria uma cópia na pasta apontada pela variável CONFIG_DIR_NAME.
  * 
  * @param urlApp
  */
 private void criarDLL(String urlApp){
  try {
   File dllWindows = new File(WINDOWS_DIR_NAME + DLL_FILE_NAME);
   if (dllWindows.exists()){
    arquivoDLLApp = WINDOWS_DIR_NAME + DLL_FILE_NAME;
   }else{
    arquivoDLLApp = CONFIG_DIR_NAME + DLL_FILE_NAME;
    
    URL url = new URL(urlApp+ "DLLServlet");
    HttpURLConnection connectionGet = (HttpURLConnection) url.openConnection();
    connectionGet.setRequestProperty("Request-Method", "GET");  
    connectionGet.setDoInput(true);
    connectionGet.setDoOutput(false);
    connectionGet.connect();
    
    // imprime o numero do resultado
    System.out.println("Resultado: "+ connectionGet.getResponseCode()+ "/"+ connectionGet.getResponseMessage());   
    InputStream input = connectionGet.getInputStream();
    File file = new File(arquivoDLLApp);
    file.createNewFile();
    FileOutputStream output = new FileOutputStream(file);
    
    int b = 0;
    while (-1 != ((b = input.read()))) {
     output.write(b);
    }
    output.close();  
   }
  } catch (MalformedURLException e) {
   e.printStackTrace();
  } catch (IOException e) {
   e.printStackTrace();
  }
 }
    
 /**
  * @param password representa o PIN informado pelo usuário
  * @return true se houve sucesso no login ou false se ocorreu 
  * algum erro
  */
 public boolean login(String password, Applet applet)throws Exception{
  boolean status = true;

  try {
   PIN = password.toCharArray();
   
   //A configuracao do provider eh possivel diante de tres opcoes:
   //1.Configuracao do java.security
   //  p. ex:security.provider.10=sun.security.pkcs11.SunPKCS11 D:\\desenvolvimento\\projetos\\funasa\\pkcs11.cfg
   //2.Pedindo para o usuario fornecer o arquivo de configuracao, através de um uploader (argh!!)
   //3.Manualmente, no codigo da applet

   //Foi escolhida a forma manual.
   Provider p = new sun.security.pkcs11.SunPKCS11(CONFIG_DIR_NAME + CONFIG_FILE_NAME);
   Security.addProvider(p);
   
   keyStore = KeyStore.getInstance("PKCS11");
   keyStore.load(null, PIN);
  }catch(Exception e){
   throw new Exception(e);
  }
  return status;
 }

 /**
  * @return chain representa o certificado armazenado no token
  */
 public Certificate[] getCertificate(){
  Certificate[] chain = null;
  try {
   Enumeration aliasesEnum = keyStore.aliases();
   while (aliasesEnum.hasMoreElements()){
    String alias = aliasesEnum.nextElement();
    if(keyStore.isKeyEntry(alias)){
     chain = keyStore.getCertificateChain(alias);
     break;
    }
   }
  } catch (GeneralSecurityException e) {
   e.printStackTrace();
  } 
  return chain;
 }

 /**
  * @return privateKey representa a chave privada armazenada no Token
  */
 public PrivateKey getPrivateKey() {
  PrivateKey privateKey = null;
  try {
   Enumeration aliasesEnum = keyStore.aliases();
   while (aliasesEnum.hasMoreElements()){
    String alias = aliasesEnum.nextElement();
    if(keyStore.isKeyEntry(alias)){
     privateKey = (PrivateKey)keyStore.getKey(alias, PIN);
     
     keyStore.getCertificateChain(alias);
     break;
    }
   }
  } catch (GeneralSecurityException e) {
   e.printStackTrace();
  } 
  return privateKey;
 }
}


package br.com.certificacao;

import java.applet.Applet;
import java.awt.Button;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Calendar;

import javax.swing.JOptionPane;

import netscape.javascript.JSObject;


import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfSignatureAppearance;
import com.lowagie.text.pdf.PdfStamper;

public class TokenSignerApplet extends Applet {
 private static final long serialVersionUID = 4023705496194390163L;

 private static final String PIN_CODE_PARAM = "pinCodeField";
 private static final String NOME_PARAM = "nomeField";
 private static final String APP_URL_PARAM = "appUrlField";
 private static final String SIGN_BUTTON_CAPTION_PARAM = "signButtonCaption"; 
 
 private Button mSignButton;

 private SignatureManager manager = new SignatureManager(); 

    /**
     * Cria e inicializa a interface grafica da applet.
     */
 public void init() {
     criarArquivoToken();
        String signButtonCaption = this.getParameter(SIGN_BUTTON_CAPTION_PARAM);
        mSignButton = new Button(signButtonCaption);
        mSignButton.setLocation(0, 0);
        Dimension appletSize = this.getSize();
        mSignButton.setSize(appletSize);
        
        mSignButton.setBackground(new Color(190, 1, 1));
        mSignButton.setForeground(new Color(255,255,255));
        mSignButton.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e) {
                assinaStream();
            }
        });
        this.setLayout(null);
        this.add(mSignButton);
    }

    public void assinaStream(){
     JSObject browserWindow = JSObject.getWindow(this);
     JSObject mainForm = (JSObject)browserWindow.eval("document.forms[0]");
     String pinCodeParam = getParameter(PIN_CODE_PARAM);
     JSObject pinCodeField = (JSObject)mainForm.getMember(pinCodeParam);
     String pinCode = (String)pinCodeField.getMember("value");

     String nomeParam = getParameter(NOME_PARAM);
     JSObject nomeField = (JSObject)mainForm.getMember(nomeParam);
     String nome = (String)nomeField.getMember("value");
        
        String appParam = this.getParameter(APP_URL_PARAM);
        JSObject appField = (JSObject) mainForm.getMember(appParam);
        String appUrl = (String) appField.getMember("value");
     
        try{
         //Chama o servlet para leitura do pdf.
         String urlArquivoServlet = appUrl + "/ArquivoServlet";
            URL url = new URL(urlArquivoServlet);
            HttpURLConnection connectionGet = (HttpURLConnection)url.openConnection();
            connectionGet.setRequestProperty("Request-Method", "GET");
            connectionGet.setDoInput(true);
            connectionGet.setDoOutput(false);
            connectionGet.connect();

            //Stream de LEITURA de dados do pdf
            InputStream input = connectionGet.getInputStream();

            SignatureManager manager = new SignatureManager();
            if(manager.login(pinCode, this)){
                java.security.PrivateKey key = manager.getPrivateKey();
                java.security.cert.Certificate certificate[] = manager.getCertificate();
                try{
                    PdfReader pdfReader = new PdfReader(input);
                    //Monta a conexao com o Servlet para escrever o arquivo assinado digitalmente 
                    HttpURLConnection connectionPost = (HttpURLConnection)url.openConnection();
                    connectionPost.setRequestProperty("Content-Type", "application/pdf");
                    connectionPost.setDoOutput(true);
                    connectionPost.connect();
                    OutputStream output = connectionPost.getOutputStream();
                    //Assina PDF
                    PdfStamper stamper = PdfStamper.createSignature(pdfReader, output, '\0');
                    PdfSignatureAppearance signAppearance = stamper.getSignatureAppearance();
                    signAppearance.setCrypto(key, certificate, null, PdfSignatureAppearance.WINCER_SIGNED);
                    signAppearance.setReason("Empresa S/A");
                    signAppearance.setLocation("Sao Paulo - SP");
                    signAppearance.setSignDate(Calendar.getInstance());
                    signAppearance.setCertificationLevel(1);
                    stamper.close();
                    BufferedReader rd = new BufferedReader(new InputStreamReader(connectionPost.getInputStream()));
                    String line;
                    while((line = rd.readLine()) != null){
                        System.out.println(line);
                    }
                    output.close();
                    input.close();
                    rd.close();
                    //chamada de uma funcao javascript no jsp 
                    browserWindow.call("chamaAction", new Object[] {nome});
                }
                catch(Exception e){
                    e.printStackTrace();
                }
            } else{
                JOptionPane.showMessageDialog(this, "Erro ao assinar documento.");
            }
            input.close();
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }
 
 private void criarArquivoToken(){
  try {
         JSObject browserWindow = JSObject.getWindow(this);
         JSObject mainForm = (JSObject) browserWindow.eval("document.forms[0]");

         String appParam = this.getParameter(APP_URL_PARAM);
         JSObject appField = (JSObject) mainForm.getMember(appParam);
         String app = (String) appField.getMember("value");
         
         manager.createTokenFiles(app);
  }catch (Exception e) {
   e.printStackTrace();
  }
 } 
}


<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<html>

<head>
    <title>Teste Token Signer Applet</title>
</head>

<body>
 <script type="text/javascript">
  function chamaAction(tarefa){
   var forward = "./certificacao.do?method=doFimCertificacao" + 
    "&idTarefa=" + tarefa;
   doSubmit(forward);
  }
 </script>

    <form>
     <input type="hidden" name="appUrl" value=${pageContext.request.contextPath}>
        Código PIN:
        <input type="text" width="100" size=80 name="pinCode">
        <br>
        Nome:
        <input type="text" width="100" size=80 name="nome" value=${nome_request}>
    </form>

 <object
     classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93"
     codebase="http://java.sun.com/update/1.5.0/jinstall-1_5-windows-i586.cab#Version=5,0,0,5"
     width="130" height="25" name="TokenSignerApplet">
     <param name="code" value="br.com.certificacao.TokenSignerApplet">
     <param name="archive" value="TokenSignerApplet.jar , ./certificacao/bcprov-jdk14-138.jar , ./certificacao/iText-2.0.8.jar">
     <param name="mayscript" value="true">
     <param name="type" value="application/x-java-applet;version=1.5">
     <param name="scriptable" value="false">
     <param name="signButtonCaption" value="Assinar Documento">
     <param name="appUrlField" value="appUrl">
     <param name="pinCodeField" value="pinCode">
     <param name="nomeField" value="nome">

     <comment>
   <embed
             type="application/x-java-applet;version=1.5"
             code="br.com.certificacao.TokenSignerApplet" 
             archive="TokenSignerApplet.jar , ./certificacao/bcprov-jdk14-138.jar , ./certificacao/iText-2.0.8.jar"
             width="130" height="25" scriptable="true"
       pluginspage="http://java.sun.com/products/plugin/index.html#download"
             appUrlField="appUrl"
             pinCodeField="pinCode"
             signButtonCaption="Assinar Documento"
             nomeField="nome">
   </embed>
     </comment>
 </object>
</body>
</html>

Servlet
package br.com.servlet;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet que gerencia o armazenamento do arquivo assinado.
 *  
 * @author marcelo.garcia
 *
 */
public class ArquivoServlet extends HttpServlet {
 private static final long serialVersionUID = 4102000674926121528L;

 /**
  * Recupera um arquivo e envia para o cliente (por ex. um applet)
  */
 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  System.out.println("ArquivoServlet - doGet");
  lerArquivo(request, response);
 }

 /**
  * Recebe o arquivo assinado e grava em disco no servidor.
  */
 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  System.out.println("ArquivoServlet - doPost");
  try {
   
   InputStream input = request.getInputStream();

   File file = new File("D:\\temp\\ResumoServletCriado.pdf");
   file.createNewFile();
   FileOutputStream output = new FileOutputStream(file);

   int b = 0;
   while (-1 != ((b = input.read()))) {
    output.write(b);
   }
   output.close();   
  } catch (IOException e) {
   e.printStackTrace();
  }
 }
 
 /**
  * Recupera um arquivo pdf
  * 
  * @param request
  * @param response
  * @throws ServletException
  * @throws IOException
  */
 private void lerArquivo (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  System.out.println("ArquivoServlet - lerArquivo");
  try {
   response.setContentType("application/pdf");
   InputStream is = new FileInputStream("D:\\temp\\Resumo.pdf");
   
   geraOutput(response, is);
  } catch (Exception e) {
   e.printStackTrace();
  }
 }
 
 /**
  * Gera saída do arquivo PDF gerado
  * 
  * @param response
  * @param is
  * @throws IOException
  */
 private void geraOutput(HttpServletResponse response, InputStream is) throws IOException {
  BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
  try {
   response.setCharacterEncoding("iso-8859-1");
   int c;
   while ((c = is.read()) != -1) {
              byte[] b = new byte[1];
              b[0] = (byte)c;
              out.write(b);
   }
  } finally {
   if (is != null)
    is.close();
   if (out != null){
    out.flush();
    out.close();
   }
  }
 } 
}


package br.com.servlet;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet que controla a DLL do token da pronova. 
 * 
 * @author marcelo.garcia
 */
public class DLLServlet extends HttpServlet {
 private static final long serialVersionUID = -3363073130063055864L;

 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  System.out.println("DO-post");
  try {
   
   InputStream url  = this.getServletContext().getResourceAsStream("\\dll\\ngp11v211.dll");
   OutputStream output = response.getOutputStream();

   int b = 0;
   while (-1 != ((b = url.read()))) {
    output.write(b);
   }
   output.close();
   url.close();
  } catch (IOException e) {
   e.printStackTrace();
  }
 }
 
 protected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
  doGet(request, response);
 }
}


Struts

package br.com.struts;

import java.io.IOException;

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

import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.actions.DispatchAction;

/**
 * 
 * @author marcelo.garcia
 * @version 1.0
 */
public class ActionCertificacao extends DispatchAction {

 public ActionForward doInicioCertificacao(ActionMapping mapping, ActionForm actionForm, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
  
  System.out.println("Metodo inicial da certificacao");
  
        return mapping.findForward("token");
 }

 /**
  * Metodo que finaliza o processo de assinatura digital.
  * Chamado por uma funcao javascript no token.jsp. 
  * 
  * @param mapping
  * @param actionForm
  * @param request
  * @param response
  * @return
  * @throws IOException
  * @throws ServletException
  */
 public ActionForward doFimCertificacao(ActionMapping mapping, ActionForm actionForm, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
  
  System.out.println("Metodo final da certificacao ");
  
        return null;
 }
}



          "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN"
          "http://jakarta.apache.org/struts/dtds/struts-config_1_2.dtd">





    
        
         
        
    
    
 

    
                    path="/certificacao"
            type="br.com.struts.ActionCertificacao"
            parameter="method"
            scope="request"
            name="formCertificacao"
            validate="false">
            
   
     
 

sexta-feira, 14 de janeiro de 2011

Descompilação de códigos Java

Uma ferramenta muito útil e simples para descompilar códigos java é conhecida como JAD, que pode ser baixada gratuitamente na Net.

Basicamente, você usará duas formas de descompilação:

1. Para descompliar um único arquivo, use o comando:

jad -sjava Pessoa.class

Com isso, será criado o arquivo Pessoa.java

2. Para descompilar vários arquivos organizados em pacotes, use o comando:

jad -o -r -sjava -dsrc bin/**/*.class

Jad descompilará todos os .class considerando as subpastas de "bin" e criará os respectivos .java organizados em subpastas dentro de "src".
Por exemplo, se você tiver a seguinte hierarquia de pastas "bin/com/br/Pessoa.class" (Uma classe Pessoa em uma pacote "com.br"), então o arquivo descompilado será "src/com/br/Pessoa.java".