Observem o seguinte programa hipotético de tratamento de arquivos:
- O usuário indica uma pasta onde vamos procurar por arquivos .txt
- O usuário indica uma pasta para onde os arquivos .txt devem ser movidos
- O usuário coloca o programa em operação ou em pausa
- O usuário é notificado das seguintes informações:
- Arquivos encontrados
- Erros (ao mover, ao ler)
- Data e hora da mensagem
Comecemos criando um projeto java normal, chamar de filewatcher. Ele começa humilde e simples, vamos fazer um JFrame. A dica aqui é instalar o WindowBuilder.
O código desta janela ficará assim:
package sample.filewatcher.view; import java.awt.EventQueue; import javax.swing.GroupLayout; import javax.swing.GroupLayout.Alignment; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.JToggleButton; import javax.swing.LayoutStyle.ComponentPlacement; import javax.swing.border.EmptyBorder; public class TheFrame extends JFrame { /** * */ private static final long serialVersionUID = -6742814419164072401L; private JPanel contentPane; private JTextField textField; private JTextField textField_1; private JTextArea textArea; private JToggleButton tglbtnIniciar; /** * Launch the application. */ public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { try { TheFrame frame = new TheFrame(); frame.setVisible(true); } catch (Exception e) { e.printStackTrace(); } } }); } /** * Create the frame. */ public TheFrame() { setTitle("Filewatcher"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setBounds(100, 100, 450, 300); contentPane = new JPanel(); contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); setContentPane(contentPane); JLabel lblOrigem = new JLabel("Origem"); textField = new JTextField(); textField.setColumns(10); JButton button = new JButton("..."); JLabel lblDestino = new JLabel("Destino"); textField_1 = new JTextField(); textField_1.setColumns(10); JButton button_1 = new JButton("..."); tglbtnIniciar = new JToggleButton("Iniciar"); JScrollPane scrollPane = new JScrollPane(); GroupLayout gl_contentPane = new GroupLayout(contentPane); gl_contentPane.setHorizontalGroup( gl_contentPane.createParallelGroup(Alignment.LEADING) .addGroup(gl_contentPane.createSequentialGroup() .addContainerGap() .addGroup(gl_contentPane.createParallelGroup(Alignment.LEADING) .addComponent(scrollPane, GroupLayout.DEFAULT_SIZE, 404, Short.MAX_VALUE) .addGroup(gl_contentPane.createSequentialGroup() .addGroup(gl_contentPane.createParallelGroup(Alignment.TRAILING) .addComponent(lblDestino) .addComponent(lblOrigem)) .addPreferredGap(ComponentPlacement.RELATED) .addGroup(gl_contentPane.createParallelGroup(Alignment.LEADING) .addComponent(textField_1, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 313, Short.MAX_VALUE) .addComponent(textField, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 315, Short.MAX_VALUE)) .addPreferredGap(ComponentPlacement.RELATED) .addGroup(gl_contentPane.createParallelGroup(Alignment.LEADING) .addComponent(button, Alignment.TRAILING) .addComponent(button_1, Alignment.TRAILING))) .addComponent(tglbtnIniciar, Alignment.TRAILING)) .addContainerGap()) ); gl_contentPane.setVerticalGroup( gl_contentPane.createParallelGroup(Alignment.LEADING) .addGroup(gl_contentPane.createSequentialGroup() .addContainerGap() .addGroup(gl_contentPane.createParallelGroup(Alignment.BASELINE) .addComponent(lblOrigem) .addComponent(textField, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) .addComponent(button)) .addPreferredGap(ComponentPlacement.RELATED) .addGroup(gl_contentPane.createParallelGroup(Alignment.BASELINE) .addComponent(lblDestino) .addComponent(textField_1, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) .addComponent(button_1)) .addPreferredGap(ComponentPlacement.RELATED) .addComponent(tglbtnIniciar) .addPreferredGap(ComponentPlacement.RELATED) .addComponent(scrollPane, GroupLayout.DEFAULT_SIZE, 143, Short.MAX_VALUE) .addContainerGap()) ); textArea = new JTextArea(); scrollPane.setViewportView(textArea); contentPane.setLayout(gl_contentPane); } }
É código automático, é feio mesmo.
Faça agora uma classe que desempenhará o papel de controle. Eis o esqueleto dela:
package sample.filewatcher.controller; import java.io.File; public class MyFileWatcher { private File source, destiny; public void setSource(String src) { } public void setDestiny(String dst) { } public void startWatch() { } public void stopWatch() { } }E, por fim, vamos fazer uma classe que entende do negócio:
package sample.filewatcher.business; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; public class TheTxtFileBusiness { public void moveTxts(File src, File dst) throws IOException { File[] files = src.listFiles(new FilenameFilter() { @Override public boolean accept(File arg0, String arg1) { return arg1.endsWith(".txt"); } }); for (File f : files) { Path destiny = Paths.get(dst.getCanonicalPath(), f.getName()); Files.move(f.toPath(), destiny); } } }
Podemos dizer que o negócio de mover arquivos é, em resumo, isso aí.
Ainda não funciona, mas vamos mudar nosso código, hora de integrar:
package sample.filewatcher.view; import java.awt.EventQueue; public class TheFrame extends JFrame { /** * */ private static final long serialVersionUID = -6742814419164072401L; private JPanel contentPane; private JTextField txtOrigem; private JTextField txtDestino; private JTextArea textArea; private JToggleButton tglbtnIniciar; private MyFileWatcher watcher = new MyFileWatcher(); private static final Logger LOG = Logger.getAnonymousLogger(); /** * Launch the application. */ public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { try { TheFrame frame = new TheFrame(); frame.setVisible(true); } catch (Exception e) { LOG.info(e.getMessage()); e.printStackTrace(); } } }); } private String getPath() { JFileChooser jfc = new JFileChooser(); jfc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); int i = jfc.showOpenDialog(this); if (i == JFileChooser.APPROVE_OPTION) return jfc.getSelectedFile().getAbsolutePath(); return null; } private void msg(Object message) { JOptionPane.showMessageDialog(this, message); LOG.info(message.toString()); } /** * Create the frame. */ public TheFrame() { setTitle("Filewatcher"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setBounds(100, 100, 450, 300); contentPane = new JPanel(); contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); setContentPane(contentPane); JLabel lblOrigem = new JLabel("Origem"); txtOrigem = new JTextField(); txtOrigem.setColumns(10); JButton button = new JButton("..."); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { String orig = getPath(); if (orig != null) txtOrigem.setText(orig); } }); JLabel lblDestino = new JLabel("Destino"); txtDestino = new JTextField(); txtDestino.setColumns(10); JButton button_1 = new JButton("..."); button_1.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { String orig = getPath(); if (orig != null) txtDestino.setText(orig); } }); tglbtnIniciar = new JToggleButton("Iniciar"); tglbtnIniciar.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { try { String orig = txtOrigem.getText(); String dest = txtDestino.getText(); if (orig.equals(dest)) { String s = "Origem não pode ser igual ao destino"; throw new Exception(s); } watcher.setSource(orig); watcher.setDestiny(dest); if (tglbtnIniciar.isSelected()) { watcher.startWatch(); tglbtnIniciar.setText("Parar"); } else { watcher.stopWatch(); tglbtnIniciar.setText("Iniciar"); } } catch (Exception ex) { ex.printStackTrace(); tglbtnIniciar.setSelected(false); watcher.stopWatch(); msg(ex); } } }); JScrollPane scrollPane = new JScrollPane(); GroupLayout gl_contentPane = new GroupLayout(contentPane); gl_contentPane .setHorizontalGroup(gl_contentPane .createParallelGroup(Alignment.LEADING) .addGroup( gl_contentPane .createSequentialGroup() .addContainerGap() .addGroup( gl_contentPane .createParallelGroup( Alignment.LEADING) .addComponent( scrollPane, GroupLayout.DEFAULT_SIZE, 404, Short.MAX_VALUE) .addGroup( gl_contentPane .createSequentialGroup() .addGroup( gl_contentPane .createParallelGroup( Alignment.TRAILING) .addComponent( lblDestino) .addComponent( lblOrigem)) .addPreferredGap( ComponentPlacement.RELATED) .addGroup( gl_contentPane .createParallelGroup( Alignment.LEADING) .addComponent( txtDestino, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 313, Short.MAX_VALUE) .addComponent( txtOrigem, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 315, Short.MAX_VALUE)) .addPreferredGap( ComponentPlacement.RELATED) .addGroup( gl_contentPane .createParallelGroup( Alignment.LEADING) .addComponent( button, Alignment.TRAILING) .addComponent( button_1, Alignment.TRAILING))) .addComponent( tglbtnIniciar, Alignment.TRAILING)) .addContainerGap())); gl_contentPane .setVerticalGroup(gl_contentPane .createParallelGroup(Alignment.LEADING) .addGroup( gl_contentPane .createSequentialGroup() .addContainerGap() .addGroup( gl_contentPane .createParallelGroup( Alignment.BASELINE) .addComponent(lblOrigem) .addComponent( txtOrigem, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) .addComponent(button)) .addPreferredGap( ComponentPlacement.RELATED) .addGroup( gl_contentPane .createParallelGroup( Alignment.BASELINE) .addComponent( lblDestino) .addComponent( txtDestino, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) .addComponent(button_1)) .addPreferredGap( ComponentPlacement.RELATED) .addComponent(tglbtnIniciar) .addPreferredGap( ComponentPlacement.RELATED) .addComponent(scrollPane, GroupLayout.DEFAULT_SIZE, 143, Short.MAX_VALUE) .addContainerGap())); textArea = new JTextArea(); scrollPane.setViewportView(textArea); contentPane.setLayout(gl_contentPane); } }
package sample.filewatcher.controller; import java.io.File; import java.io.IOException; import java.util.logging.Logger; import sample.filewatcher.business.TheTxtFileBusiness; public class MyFileWatcher { private File source, destiny; private boolean watching; private TheTxtFileBusiness business = new TheTxtFileBusiness(); private static final Logger LOG = Logger.getAnonymousLogger(); public void setSource(String src) throws Exception { this.source = getFolder(src); } public void setDestiny(String dst) throws Exception { this.destiny = getFolder(dst); } public void startWatch() throws Exception { LOG.info("starting watch thread"); watching = true; if (source == null) throw new Exception("Origem não informada"); if (destiny == null) throw new Exception("Destino não informado"); new Thread() { public void run() { while (watching) { try { business.moveTxts(source, destiny); } catch (IOException e) { e.printStackTrace(); watching = false; } slp(); } }; }.start(); } public void stopWatch() { LOG.info("stopping watcher"); watching = false; } private void slp() { try { Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } } private void valida(File folder) throws Exception { if (!folder.exists()) throw new Exception("Diretório não existe"); if (!folder.isDirectory()) throw new Exception("Diretório não é diretório"); } private File getFolder(String path) throws Exception { File folder = new File(path); valida(folder); return folder; } }
package sample.filewatcher.business; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.logging.Logger; public class TheTxtFileBusiness { private static final Logger LOG = Logger.getAnonymousLogger(); public void moveTxts(File src, File dst) throws IOException { File[] files = src.listFiles(new FilenameFilter() { @Override public boolean accept(File arg0, String arg1) { return arg1.endsWith(".txt"); } }); LOG.info("Files found: "+files.length); for (File f : files) { Path destiny = Paths.get(dst.getCanonicalPath(), f.getName()); Files.move(f.toPath(), destiny); } } }
Adicionamos uns poucos registros de log. Se você testar, verá que já temos funcionamento básico... Ocorre que algo ainda não está funcionando: como notificar, a partir do controle e do model, a camada de visão?
A primeira saída é escrever uma estrutura de listeners (acessores de registro, implementação de interface, por aí vai). Esta saída envolve, infelizmente uma boa mão de obra no tocante a código e manutenção das instâncias. Para nosso pequeno exemplo serviria, mas e em um projeto de pequeno porte? E em um projeto maior?
Falemos do guice agora.
Faça o download do mesmo e dicione-o como dependência no projeto:
http://code.google.com/p/google-guice/downloads/detail?name=guice-3.0.zip&can=2&q=
Em seguida, façamos as seguintes modificações de código:
package sample.filewatcher.business; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.logging.Logger; import javax.inject.Inject; import sample.filewatcher.view.TheFrame; public class TheTxtFileBusiness { private static final Logger LOG = Logger.getAnonymousLogger(); @Inject private TheFrame frame; public void moveTxts(File src, File dst) throws IOException { File[] files = src.listFiles(new FilenameFilter() { @Override public boolean accept(File arg0, String arg1) { return arg1.endsWith(".txt"); } }); String s = "Files found: " + files.length; LOG.info(s); frame.log(s); for (File f : files) { Path destiny = Paths.get(dst.getCanonicalPath(), f.getName()); Files.move(f.toPath(), destiny); } } }
package sample.filewatcher.controller; import java.io.File; import java.io.IOException; import java.util.logging.Logger; import javax.inject.Inject; import sample.filewatcher.business.TheTxtFileBusiness; import sample.filewatcher.view.TheFrame; public class MyFileWatcher { private File source, destiny; private boolean watching; @Inject private TheTxtFileBusiness business;// = new TheTxtFileBusiness(); @Inject private TheFrame frame; private static final Logger LOG = Logger.getAnonymousLogger(); public void setSource(String src) throws Exception { this.source = getFolder(src); } public void setDestiny(String dst) throws Exception { this.destiny = getFolder(dst); } public void startWatch() throws Exception { String s = "starting watch thread"; LOG.info(s); frame.log(s); watching = true; if (source == null) throw new Exception("Origem não informada"); if (destiny == null) throw new Exception("Destino não informado"); new Thread() { public void run() { while (watching) { try { business.moveTxts(source, destiny); } catch (IOException e) { e.printStackTrace(); watching = false; } slp(); } }; }.start(); } public void stopWatch() { String s = "stopping watcher"; LOG.info(s); frame.log(s); watching = false; } private void slp() { try { Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } } private void valida(File folder) throws Exception { if (!folder.exists()) throw new Exception("Diretório não existe"); if (!folder.isDirectory()) throw new Exception("Diretório não é diretório"); } private File getFolder(String path) throws Exception { File folder = new File(path); valida(folder); return folder; } }
package sample.filewatcher.view; import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.text.SimpleDateFormat; import java.util.Date; import java.util.logging.Logger; import javax.inject.Inject; import javax.swing.GroupLayout; import javax.swing.GroupLayout.Alignment; import javax.swing.JButton; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.JToggleButton; import javax.swing.LayoutStyle.ComponentPlacement; import javax.swing.border.EmptyBorder; import sample.filewatcher.controller.MyFileWatcher; import com.google.inject.Guice; import com.google.inject.Injector; public class TheFrame extends JFrame { /** * */ private static final long serialVersionUID = -6742814419164072401L; private JPanel contentPane; private JTextField txtOrigem; private JTextField txtDestino; private JTextArea textArea; private JToggleButton tglbtnIniciar; private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @Inject private MyFileWatcher watcher;// = new MyFileWatcher(); private static final Logger LOG = Logger.getAnonymousLogger(); /** * Launch the application. */ public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { try { Injector in = Guice.createInjector(); // TheFrame frame = new TheFrame(); TheFrame frame = in.getInstance(TheFrame.class); frame.setVisible(true); } catch (Exception e) { LOG.info(e.getMessage()); e.printStackTrace(); } } }); } public void log(Object message) { String tst = "[" + sdf.format(new Date()) + "] "; textArea.append(tst + message + "\n"); } private String getPath() { JFileChooser jfc = new JFileChooser(); jfc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); int i = jfc.showOpenDialog(this); if (i == JFileChooser.APPROVE_OPTION) return jfc.getSelectedFile().getAbsolutePath(); return null; } private void msg(Object message) { JOptionPane.showMessageDialog(this, message); LOG.info(message.toString()); } /** * Create the frame. */ public TheFrame() { setTitle("Filewatcher"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setBounds(100, 100, 450, 300); contentPane = new JPanel(); contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); setContentPane(contentPane); JLabel lblOrigem = new JLabel("Origem"); txtOrigem = new JTextField(); txtOrigem.setColumns(10); JButton button = new JButton("..."); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { String orig = getPath(); if (orig != null) txtOrigem.setText(orig); } }); JLabel lblDestino = new JLabel("Destino"); txtDestino = new JTextField(); txtDestino.setColumns(10); JButton button_1 = new JButton("..."); button_1.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { String orig = getPath(); if (orig != null) txtDestino.setText(orig); } }); tglbtnIniciar = new JToggleButton("Iniciar"); tglbtnIniciar.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { try { String orig = txtOrigem.getText(); String dest = txtDestino.getText(); if (orig.equals(dest)) { String s = "Origem não pode ser igual ao destino"; throw new Exception(s); } watcher.setSource(orig); watcher.setDestiny(dest); if (tglbtnIniciar.isSelected()) { watcher.startWatch(); tglbtnIniciar.setText("Parar"); } else { watcher.stopWatch(); tglbtnIniciar.setText("Iniciar"); } } catch (Exception ex) { ex.printStackTrace(); tglbtnIniciar.setSelected(false); watcher.stopWatch(); msg(ex); log(ex.getMessage()); } } }); JScrollPane scrollPane = new JScrollPane(); GroupLayout gl_contentPane = new GroupLayout(contentPane); gl_contentPane .setHorizontalGroup(gl_contentPane .createParallelGroup(Alignment.LEADING) .addGroup( gl_contentPane .createSequentialGroup() .addContainerGap() .addGroup( gl_contentPane .createParallelGroup( Alignment.LEADING) .addComponent( scrollPane, GroupLayout.DEFAULT_SIZE, 404, Short.MAX_VALUE) .addGroup( gl_contentPane .createSequentialGroup() .addGroup( gl_contentPane .createParallelGroup( Alignment.TRAILING) .addComponent( lblDestino) .addComponent( lblOrigem)) .addPreferredGap( ComponentPlacement.RELATED) .addGroup( gl_contentPane .createParallelGroup( Alignment.LEADING) .addComponent( txtDestino, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 313, Short.MAX_VALUE) .addComponent( txtOrigem, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 315, Short.MAX_VALUE)) .addPreferredGap( ComponentPlacement.RELATED) .addGroup( gl_contentPane .createParallelGroup( Alignment.LEADING) .addComponent( button, Alignment.TRAILING) .addComponent( button_1, Alignment.TRAILING))) .addComponent( tglbtnIniciar, Alignment.TRAILING)) .addContainerGap())); gl_contentPane .setVerticalGroup(gl_contentPane .createParallelGroup(Alignment.LEADING) .addGroup( gl_contentPane .createSequentialGroup() .addContainerGap() .addGroup( gl_contentPane .createParallelGroup( Alignment.BASELINE) .addComponent(lblOrigem) .addComponent( txtOrigem, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) .addComponent(button)) .addPreferredGap( ComponentPlacement.RELATED) .addGroup( gl_contentPane .createParallelGroup( Alignment.BASELINE) .addComponent( lblDestino) .addComponent( txtDestino, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) .addComponent(button_1)) .addPreferredGap( ComponentPlacement.RELATED) .addComponent(tglbtnIniciar) .addPreferredGap( ComponentPlacement.RELATED) .addComponent(scrollPane, GroupLayout.DEFAULT_SIZE, 143, Short.MAX_VALUE) .addContainerGap())); textArea = new JTextArea(); scrollPane.setViewportView(textArea); contentPane.setLayout(gl_contentPane); } }
Observe como a anotação é basta para que tenhamos uma instância para nosso uso.
Não há necessidade sequer de get/set, apenas precisamos da anotação.
Adicionalmente, dou foco na modificação feita no entry point da aplicação:
/** * Launch the application. */ public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { try { Injector in = Guice.createInjector(); // TheFrame frame = new TheFrame(); TheFrame frame = in.getInstance(TheFrame.class); frame.setVisible(true); } catch (Exception e) { LOG.info(e.getMessage()); e.printStackTrace(); } } }); }
No lugar de instanciarmos a classe diretamente, pedimos ao injetor de dependências uma instância da mesma.
E assim temos resolvidas de forma transparente todas as referências.
Caso você seja purista e decida descer para as camadas mais profundas (controller/business) uma interface em vez da referência para a view, o injetor permite que você forneça um módulo, e num módulo você pode dizer quem implementa qual interface, conforme você vai ler na documentação oficial do guice.
Sem mais, e boa sorte.
Nenhum comentário :
Postar um comentário