Translate

terça-feira, 26 de junho de 2012

TDC 2012

No próximo dia 4 até 8/07/2012, ocorrerá em São Paulo, na Universidade Anhembi Morumbi a sexta edição TDC (The Developers Conference), evento organizado pela GlobalCode para profissionais e estudantes iniciantes em tecnologia.

Você pode se inscrever e selecionar uma trilha de palestra por dia. Por exemplo, se na segunda-feira você decidir participar da trilha sobre Java, passará o "dia inteiro" de palestra vendo somente Java. Além do Java, outras tecnologias atuais serão palestradas com Ruby, Python, PHP, C/C++, Scala, .NET, Arduino, Testes, Marketing Digital, Robótica, Banco de Dados, Android, iOS, WindowsPhone e muito mais.

As inscrições já estão abertas no site do evento.

Para mais informações, acesse:

 The Developers Conference 2012, um evento organizado pela Globalcode

sábado, 23 de junho de 2012

Java - Clonagem de objetos

Durante sua vida um programador pode lidar com a necessidade de manter um objeto matriz e reproduzir cópias deste objeto para fins de alterar estas cópias sem perder a originalidade da matriz.

Posso descrever uma situação onde tive uma rotina de envio de mensagem para um determinado host, mas esta rotina alterava propriedades da minha mensagem original porque seu comportamento normal era este. Foi desenhado para envio de mensagem sempre para um destinatário. Efeito:

  d - Destino da mensagem (host).
  m.o. - Mensagem original.
  m.a. - Mensagem alterada.
  send_to_some (d, m.o. -> m.a.) - Função de envio para d onde m.o. passa a ser m.a. ao final do processamento.

Acrescentei ao sistema uma função a mais para enviar a mensagem destinada para mais de um host através de clonagem para preservar a original. Efeito:

  m.o. - Mensagem original.
  m.c. - Mensagem clonada.
  m.a. - Mensagem alterada.
  send_to_all (m.o. -> m.a.) - Função de envio para todos m.c. passa a ser m.a. ao final do processamento, mas m.o. é preservado.

Após implementar send_to_all, simplismente esta função passou a varrer a lista ativa de conexões de hosts e para cada iteração clona a mensagem original e passa o clone como parâmetro de send_to_some.

Vamos para a prática!

WTByteArrayInputStream

Esta classe representa um mecanismo para transformar uma série de bytes em um objeto. Será utilizada no processo de clonagem.

import java.io.InputStream;

public class WTByteArrayInputStream extends InputStream {

    protected byte[] buf = null;

    protected int count = 0;

    protected int pos = 0;

    public WTByteArrayInputStream(byte[] buf, int count) {
        this.buf = buf;
        this.count = count;
    }

    public final int available() {
        return count - pos;
    }

    public final int read() {
        return (pos < count) ? (buf[pos++] & 0xff) : -1;
    }

    public final int read(byte[] b, int off, int len) {
        if (pos >= count)
            return -1;

        if ((pos + len) > count)
            len = (count - pos);

        System.arraycopy(buf, pos, b, off, len);
        pos += len;
        return len;
    }

    public final long skip(long n) {
        if ((pos + n) > count)
            n = count - pos;
        if (n < 0)
            return 0;
        pos += n;
        return n;
    }

}

WTByteArrayOutputStream

Esta classe representa um mecanismo para transformar um objeto em uma série de bytes e oferece uma função para obter uma conexão com os bytes do objeto.

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

public class WTByteArrayOutputStream extends OutputStream {

    protected byte[] buf = null;
    protected int size = 0;

    public WTByteArrayOutputStream() {
        this(5 * 1024);
    }

    public WTByteArrayOutputStream(int initSize) {
        this.size = 0;
        this.buf = new byte[initSize];
    }

    private void verifyBufferSize(int sz) {
        if (sz > buf.length) {
            byte[] old = buf;
            buf = new byte[Math.max(sz, 2 * buf.length )];
            System.arraycopy(old, 0, buf, 0, old.length);
            old = null;
        }
    }

    public int getSize() {
        return size;
    }

    public byte[] getByteArray() {
        return buf;
    }

    public final void write(byte b[]) {
        verifyBufferSize(size + b.length);
        System.arraycopy(b, 0, buf, size, b.length);
        size += b.length;
    }

    public final void write(byte b[], int off, int len) {
        verifyBufferSize(size + len);
        System.arraycopy(b, off, buf, size, len);
        size += len;
    }

    public final void write(int b) {
        verifyBufferSize(size + 1);
        buf[size++] = (byte) b;
    }

    public void reset() {
        size = 0;
    }

    public InputStream getInputStream() {
        return new WTByteArrayInputStream(buf, size);
    }

}

WTCloneObject

Esta classe é a própria rotina de clonagem.

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class WTCloneObject {

    public static Object copy(Object orig) {

        Object obj = null;

        try {

            WTByteArrayOutputStream fbos = new WTByteArrayOutputStream();
            ObjectOutputStream out = new ObjectOutputStream(fbos);

            out.writeObject(orig);
            out.flush();
            out.close();

            ObjectInputStream in = new ObjectInputStream(fbos.getInputStream());
            obj = in.readObject();

        }
        catch(IOException e) {
            e.printStackTrace();
        }
        catch(ClassNotFoundException cnfe) {
            cnfe.printStackTrace();
        }
        return obj;
    }

}

DadosPessoais

Esta classe é um bean muito simples que auxiliará apenas o teste. Como será o tipo do objeto para clonar em nosso teste, esta deve implementar a interface Serializable. Observe a palavra-chave transient no parâmetro senha_Bancaria, pois indica que o atributo não será gravado quando o objeto de DadosPessoais for serializado em bytes. Esta mesma técnica pode ser utilizada se um dos atributos for tão complexo ou fechado que não te permite fazer suas classes ou sub-classes implementarem a interface Serializable.

import java.io.Serializable;
 

class DadosPessoais implements Serializable {

    private String nome;
    private int idade;
    private String telefone;
    private String sexo;
    private transient String senha_Bancaria;

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public int getIdade() {
        return idade;
    }

    public void setIdade(int idade) {
        this.idade = idade;
    }

    public String getTelefone() {
        return telefone;
    }

    public void setTelefone(String telefone) {
        this.telefone = telefone;
    }

    public String getSexo() {
        return sexo;
    }

    public void setSexo(String sexo) {
        this.sexo = sexo;
    }

    public String getSenha_Bancaria() {
        return senha_Bancaria;
    }

    public void setSenha_Bancaria(String senha_Bancaria) {
        this.senha_Bancaria = senha_Bancaria;
    }

    @Override
    public String toString() {
        return "DadosPessoais [nome=" + nome + ", idade=" + idade
                + ", telefone=" + telefone + ", sexo=" + sexo
                + ", senha_Bancaria=" + senha_Bancaria + "]";
    }

}

Testando

Crie uma classe de teste com um método main para testar a clonagem:

        DadosPessoais matriz = new DadosPessoais();

        matriz.setNome("Jose");
        matriz.setIdade(30);
        matriz.setTelefone("+55 11 88888888");
        matriz.setSexo("M");
        matriz.setSenha_Bancaria("123#%!");

        System.out.println("Esta é a matriz");
        System.out.println(matriz.toString());
        System.out.println("");

        for (int i = 0; i < 10; i++) {
           
            DadosPessoais clone = (DadosPessoais) WTCloneObject.copy(matriz);

            System.out.println("Este é o clone id = " + i);
            System.out.println(clone.toString());
            System.out.println("");
           
        }

Fique atento, a senha não foi clonada em nenhum momento porque foi marcada como transient na classe DadosPessoais.

Até a próxima.

quarta-feira, 20 de junho de 2012

Java - Limpeza automática de coleções

Um dos problemas principais em aplicações do tipo serviço é a alocação e devolução de memória, porque aplicações do tipo serviço tendem a ter um ciclo de vida de horas, dias ou meses por terem como premissa uma longa disponibilidade e robustez.

A depender da complexidade do serviço em relação ao volume de requisições à processar, é costume do arquiteto definir o uso de pilhas sofisticadas (coleções) para enfileirar e controlar o fluxo do processamento de requisições. Não obstante, estas pilhas podem naturalmente receber novos elementos, porém por uma eventual falha (ou não tratada) fará alguns de seus elementos ficarem empilhados "para sempre" até o fim do serviço. O pior é que a gc (garbage collector) nesta situação não fara a limpeza porque os elementos perdidos estão vinculados a um objeto de pilha (elemento).

No exemplo deste artigo, coloco a disposição especializações das coleções ConcurrentHashMap e ConcurrentLinkedQueue por se tratar das pilhas mais comuns numa arquitetura de serviço com paralelismo para alta produtividade. As especializações a seguir tem o mesmo propósito, expirar elementos após 120 segundos (2 minutos) em fila. Perceba na constante KEY_TIMEOUT_VALUE, encontrada em cada uma das especializações, que este tempo pode ser aumentado ou diminuído.

Importante: O mecanismo proposto como base expira elementos da pilha e destrói objetos envelopados por WTTimeObject através de anulamento. Porém, se o objeto núcleo armazenado na pilha for muito complexo, como é o caso de um Socket ou File, este será removido do empilhamento, mas ficará em aberto mesmo com o anulamento enquanto o serviço estiver ativo. Para este caso, recomendo um controle transacional conciente para o correto encerramento do objeto complexo.

WTTimeObject

Esta classe é um invólucro usado por ambas as especializações citadas anteriormente. O WTTimeObject contém atributos para a guarda do tempo de último acesso ao elemento núcleo empilhado (aquele que realmente se pretente armazenar) generalizado em <V>. Se você não entende de Generics em Java, terá dificuldades de compreender este artigo.

public class WTTimeObject<V> {

    private static long nextVal = 0L;
   
    private long lastAccessTime = 0L;
    private V value;
    private long objectId;
   
    public WTTimeObject() {
        this.objectId = this.getNextVal();
    }
   
    public WTTimeObject(V value) {

        this();

        this.lastAccessTime = System.currentTimeMillis();
        this.value = value;

    }

    public long getLastAccessTime() {
        return lastAccessTime;
    }

    public V getValue() {
        return this.getValue(true);
    }

   
    public V getValue(boolean doUpdateAccessTime) {
        if (doUpdateAccessTime) {
            this.lastAccessTime = System.currentTimeMillis();
        }
        return value;
    }

    public void setValue(V value) {
        this.lastAccessTime = System.currentTimeMillis();
        this.value = value;
    }
   
    public synchronized long getNextVal() {
       
        if (WTTimeObject.nextVal < Long.MAX_VALUE) {
            WTTimeObject.nextVal++;
        } else {
            WTTimeObject.nextVal=1;
        }
       
        return WTTimeObject.nextVal;
       
    }

    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + (int) (objectId ^ (objectId >>> 32));
        return result;
    }

    public boolean equals(Object obj) {

        if (this == obj)
            return true;

        if (obj == null)
            return false;

        if (getClass() != obj.getClass())
            return false;

        WTTimeObject<?> other = (WTTimeObject<?>) obj;

        if (objectId != other.objectId)
            return false;

        return true;

    }

}

WTConcurrentLinkedQueue

Esta é a especialização da coleção ConcurrentLinkedQueue, perceba que existe uma inner class na especialização chamada ClearExpiredRunnable que é do tipo Runnable, porque a cada adição a instância de ClearExpiredRunnable será acionada à verificar e eliminar elementos não acessados a mais de 120 segundos.

import java.util.Iterator;
import java.util.concurrent.ConcurrentLinkedQueue;

public class WTConcurrentLinkedQueue<V> {

    public static final long CLEAR_EXPIRED_INTERVAL_VALUE = 60; // A cada 1 minuto verifica
    public static final long KEY_TIMEOUT_VALUE = 120; // Elementos mais velhos do que 2 minutos 

    private ConcurrentLinkedQueue<WTTimeObject<V>> queue;

    private long keyTimeout;
    private String id;
    private long clearExpiredTimeout;
    private ClearExpiredValues clearExpiredRunnable;

    public WTConcurrentLinkedQueue(String id) {
        this(id, KEY_TIMEOUT_VALUE);
    }

    public WTConcurrentLinkedQueue(String id, Long keyTimeout) {
        this.id = id;
        this.queue = new ConcurrentLinkedQueue<WTTimeObject<V>>();
        updateTimeoutOfClearExpired();
        this.keyTimeout = keyTimeout;
        clearExpiredRunnable = new ClearExpiredValues(this);
    }

    public boolean add(V value) {

        boolean result = false;

        result = this.queue.add(new WTTimeObject<V>(value));
   
        // Chama evento para excluir elementos expirados
        clearExpiredValues();
       
        return result;

    }

    public V peek() {

        WTTimeObject<V> result = null;

        result = this.queue.peek();

        if (result == null) {
            return null;
        } else {
            return result.getValue();
        }
    }
   
    public V poll() {

        WTTimeObject<V> result = null;

        result = this.queue.poll();

        if (result == null) {
            return null;
        } else {
            return result.getValue();
        }
    }
   
   
    public boolean remove(V value) {

        boolean result = false;
       
        WTTimeObject<V> timeObj = null;

        synchronized(queue) {
       
            Iterator<WTTimeObject<V>> it = this.queue.iterator();
   
            while (it.hasNext()) {
   
                timeObj = it.next();
   
                // A comparação deve ser feita com o conteúdo de DGTimeObject e nunca direto
                if (value instanceof String) {
                    if (timeObj.getValue(false).equals(value)) {
                        result = this.queue.remove(timeObj);
                        it.remove();
                        break;
                    }
                }else {
                    if (timeObj.getValue(false) == value) {
                        result = this.queue.remove(timeObj);
                        it.remove();
                        break;
                    }
                }
   
            }
           
        }

        return result;
       
    }

    public Iterator<WTTimeObject<V>> iterator() {
        return this.queue.iterator();
    }

    public int size() {
        return this.queue.size();
    }
   
    public void clearExpiredValues() {

        if (clearExpiredTimeout < System.currentTimeMillis()) {
            updateTimeoutOfClearExpired();
            Thread r = new Thread(clearExpiredRunnable);
            r.start();
        }

    }

    public void clear() {

        this.queue.clear();

    }
   
   
    class ClearExpiredValues implements Runnable {

        private WTConcurrentLinkedQueue<V> queue;

        public ClearExpiredValues(WTConcurrentLinkedQueue<V> queue) {
            this.queue = queue;
        }

        public void run() {

            int countRemove = 0;
           
            Iterator<WTTimeObject<V>> it = this.queue.iterator();

            WTTimeObject<V> value;

            while (it.hasNext()) {

                value = it.next();

                // Verifica se o objeto venceu
                if (value != null && value.getLastAccessTime() + (keyTimeout * 1000) < System.currentTimeMillis()) {

                    // ATENÇÃO: Nunca anular o valor dentro de value, porque o remove encontra
                    // o elemento para remoção através de value.getValue().
                    this.queue.remove(value.getValue());
                    it.remove();

                    value.setValue(null);
                    value = null;

                    countRemove++;

                }
   
            }

            if (countRemove > 0)
                System.out.println(String.format("Removido(s) %d elemento(s) vencidos de %s.", countRemove, id));

        }

    }

    public void updateTimeoutOfClearExpired() {
        clearExpiredTimeout = System.currentTimeMillis() + (CLEAR_EXPIRED_INTERVAL_VALUE * 1000);
    }   

}

WTConcurrentHashMap

Já esta é a outra especialização, agora da coleção ConcurrentHashMap, perceba que também existe uma inner class na especialização chamada ClearExpiredRunnable que é do tipo Runnable, porque a cada adição a instância de ClearExpiredRunnable será acionada à verificar e eliminar elementos não acessados a mais de 120 segundos.


import java.util.Enumeration;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public class WTConcurrentHashMap<K, V> {

    public static final long CLEAR_EXPIRED_INTERVAL_VALUE = 60; // A cada 1 minuto verifica
    public static final long KEY_TIMEOUT_VALUE = 120; // Elementos mais velhos do que 2 minutos 

    private ConcurrentHashMap<K, WTTimeObject<V>> map;
    private long keyTimeout;
    private String id;
    private long clearExpiredTimeout;
    private ClearExpiredKeys clearExpiredRunnable;

    public WTConcurrentHashMap(String id) {
        this(id, KEY_TIMEOUT_VALUE);
    }

    public WTConcurrentHashMap(String id, Long keyTimeout) {
        this.id = id;
        map = new ConcurrentHashMap<K, WTTimeObject<V>>();
        updateTimeoutOfClearExpired();
        this.keyTimeout = keyTimeout;
        clearExpiredRunnable = new ClearExpiredKeys(map);
    }

    public V put(K key, V value) {

        WTTimeObject<V> result = null;

        synchronized(map) {
            result = map.put(key, new WTTimeObject<V>(value));
        }

        if (this.keyTimeout > 0L) {
            // Chama evento para excluir elementos expirados
            clearExpiredKeys();
        }
       
        if (result == null) {
            return null;
        } else {
            return value;
        }
    }

    public V get(K key) {

        WTTimeObject<V> result = null;

        synchronized(map) {
            result = map.get(key);
        }

        if (result == null) {
            return null;
        } else {
            return result.getValue();
        }
    }
   
    public V remove(K key) {

        WTTimeObject<V> result = null;

        synchronized(map) {
            result = map.remove(key);
        }

        if (result == null) {
            return null;
        } else {
            return result.getValue();
        }
    }
   
    public void clearExpiredKeys() {

        if (clearExpiredTimeout < System.currentTimeMillis()) {
            updateTimeoutOfClearExpired();
            Thread r = new Thread(clearExpiredRunnable);
            r.start();
        }

    }

    public void clear() {
        synchronized(map) {
            map.clear();
        }
    }
   
    public Set<K> keySet() {
        synchronized(map) {
            return map.keySet();
        }
    }

    public int size() {
        synchronized(map) {
            return map.size();
        }
    }
   
    class ClearExpiredKeys implements Runnable {

        private ConcurrentHashMap<K, WTTimeObject<V>> map;

        public ClearExpiredKeys(ConcurrentHashMap<K, WTTimeObject<V>> map) {
            this.map = map;
        }
       
        public void run() {

            int countRemove = 0;

            synchronized(map) {
               
                Enumeration<K> en = map.keys();
   
                K key;
                WTTimeObject<V> value;
   
                while (en.hasMoreElements()) {
   
                    key = en.nextElement();
   
                    value = map.get(key);
   
                    // Verifica se o objeto venceu
                    if (value != null && value.getLastAccessTime() + (keyTimeout * 1000) < System.currentTimeMillis()) {
   
                        value.setValue(null);
                        value = null;
                        map.remove(key);
                        countRemove++;
   
                    }
   
                }
               
            }

            if (countRemove > 0)
                System.out.println(String.format("Removido(s) %d elemento(s) vencidos de %s.", countRemove, id));

        }

    }
   
    public void updateTimeoutOfClearExpired() {
        clearExpiredTimeout = System.currentTimeMillis() + (CLEAR_EXPIRED_INTERVAL_VALUE * 1000);
    }

}


Testando as coleções especializadas

Para finalizar, vamos testar as coleções para observar a limpeza de elementos com mais de 120 segundos sem acesso:

        WTConcurrentLinkedQueue<String> map = new WTConcurrentLinkedQueue<String>("ColecaoTeste");
        WTConcurrentHashMap<Integer, String> map1 = new WTConcurrentHashMap<Integer, String>("ColecaoTeste");
        int contador = 0;

        while (true) {
            contador++;
            map.add("Teste " + contador);
            map1.put(contador, "Teste " + contador);
            try {
                Thread.sleep(1L);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }       




segunda-feira, 11 de junho de 2012

Recuperar senha Oracle


Este método funciona somente se você tiver uma conexão local com o servidor do SGBD. Remotamente não funciona por questão de segurança.

C:\Users\Administrador>sqlplus /nolog
SQL> conn / as sysdba
SQL> alter user SYS identified by novaSenha;
SQL> quit

quinta-feira, 31 de maio de 2012

Java - Threads

Uma thread nada mais é do que a execução de instruções em série para cumprir o objetivo de uma determinada rotina.

Seu termo simplificado, é quando você escreve um programa simples e o executa, gera uma linha de execução (thread) no processador.

Se você escrever duas rotinas distintas e solicitar a execução simultânea destas rotinas, gera duas linhas de execução (duas threads) no processador.

Calma ai, se você se perguntou "como um processador pode executar dois trabalhos ao mesmo tempo se ele é apenas um?", já fez um grande avanço. Mas se você não se fez esta pergunta, considere-a por enquanto.

Para responder a pergunta, é preciso levar em conta dois aspectos dos processadores:

Processador sem suporte para múltiplas threads

Um processador sem suporte para múltiplas threads normalmente é utilizado pelo sistema operacional de forma compartilhada se você solicitar a execução de dois processos ao mesmo tempo. Esta técnica é chamada de time slice, ou seja, simboliza o ato de compartilhamento do tempo do processador para executar instruções de duas ou mais rotinas ativas.

Em termos de desempenho, o time slice não traz nenhuma vantagem, pois o tempo de processamento é dividido entre as rotinas em atividade e quanto mais rotinas em atividade paralela mais inviável torna-se o  programa para um processador sem suporte múlti threads.

Processador com suporte para múltiplas threads

Felizmente vivenciamos um momento propício para a utilização de threads porque os processadores modernos disponíveis na maior parte dos computadores pessoais são capazes de executar em tempo real pelo menos três instruções ao mesmo tempo. A tecnologia de processadores para servidores possui uma capacidade maior do que os processadores pessoais.

Em computadores como notebooks, desktops e servidores todos os processadores oferecem suporte a múltiplas threads o que viabiliza este artigo.

Thread em Java


Há duas formas básicas para executar sua própria thread em Java, a primeira é herdar a classe java.lang.Thread e implementar seu método run(), a segunda forma é implementar a classe java.lang.Runnable e utilizá-la em conjunto com a classe java.lang.Thread.

Utilizando somente java.lang.Thread

Crie a classe MinhaThread conforme o seguinte código:

public class MinhaThread extends Thread {

    private static int numeroInstancias;

    static {
        numeroInstancias = 0;
    }
   
    public MinhaThread(String nomeInstancia) {
        super(nomeInstancia);
        numeroInstancias++;
    }
   
    @Override
    public void run() {
       
        System.out.println("Executando instância [ID: " + super.getId() + ", Nome: " + super.getName() + "] de MinhaThread.");
        System.out.println("Instâncias criadas " + numeroInstancias + ".");

    }
   
}


Agora crie a classe TesteThread para executar instâncias da classe MinhaThread:

public class TesteThread {

    public static void main(String[] args) throws InterruptedException {
       
        MinhaThread t1 = new MinhaThread("ObjetoTh1");

        MinhaThread t2 = new MinhaThread("ObjetoTh2");

        // Abre execução paralela de t1
        t1.start();

        // Abre execução paralela de t2
        t2.start();

        // Aguarda término de t1
        t1.join();

        // Aguarda término de t2
        t2.join();

        // Encerra aplicação (instância JVM)
        System.exit(0);
       
    }
   
}


Ao criar as threads t1 e t2, observe que ao construí-las com o comando new, foram informados nomes distintos para cada instância de MinhaThread.

Ao invocar o método start() de t1, uma nova linha de execução é criada em paralelo a linha de execução de TesteThread iniciada pelo método main(). Outra linha de execução é criada em paralelo ao invocar o método start() de t2.

Agora "O Grande Truque" para realçar o entendimento, veja que para t1 e t2, foi executado o método join(). Isso faz a linha de execução de main() aguardar o término de uma outra linha, se não fosse isso, ao executar o System.exit(0), todas as thread seriam encerradas imediatamente com a aplicação. Faça o teste sem a chamada do join() e observe que t1 ou t2 são interrompidas durante ou antes de suas execuções.

Utilizando java.lang.Thread e java.lang.Runnable

Crie a classe MinhaRotina conforme o seguinte código:

class MinhaRotina implements Runnable {

    private String meuNome;
  
    public MinhaRotina(String nomeInstancia) {
        this.meuNome = nomeInstancia;
    }
  
    @Override
    public void run() {

        System.out.println("Executando a rotina [Nome: " + this.meuNome + "] do tipo MinhaRotina.");

    }
  
}


Crie a classe TesteThread2 para executar instâncias da classe MinhaRotina:

public class TesteThread2 {

    public static void main(String[] args) throws InterruptedException {
       
        MinhaRotina r1 = new MinhaRotina("Rotina1");

        MinhaRotina r2 = new MinhaRotina("Rotina2");

        Thread t1 = new Thread(r1, "Thread da Rotina1");

        Thread t2 = new Thread(r2, "Thread da Rotina2");
       
        // Abre execução paralela de r1
        t1.start();

        // Abre execução paralela de t2
        t2.start();

        // Aguarda término de r1
        t1.join();

        // Aguarda término de r2
        t2.join();

        // Encerra aplicação (instância JVM)
        System.exit(0);
       
    }
   
}


Ao criar as rotinas r1 e r2, observe que ao construí-las com o comando new, foram informados nomes distintos para cada instância de MinhaRotina. Já ao criar t1 e t2, desta vez foram informados dois parâmetros no construtor de Thread, pois o primeiro parâmetro é a instância de uma classe que implementa Runnable e o segundo parâmetro é o nome da thread que deve ser exclusivo.

Ao invocar o método start() de t1, uma nova linha de execução é criada em paralelo a linha de execução de TesteThread2 iniciada pelo método main(). Outra linha de execução é criada em paralelo ao invocar o método start() de t2.

O entendimento de join() é o mesmo já explicado no exemplo anterior. 

Conclusão

Neste artigo abordei os exemplos fundamentais para aplicação de threads em Java, mas este assunto não se resume a isso, porque existe o aspecto de concorrência que deve ser considerado antes de se tomar a decisão de criar uma aplicação multithread.

sábado, 7 de janeiro de 2012

Oracle - Restaurar banco de dados

Não é incomum uma instância de banco de dados Oracle corromper após uma repentina queda de energia.

Quando seu banco de dados sofre algum nível de corrupção e não abre por isso, a primeira saída é remeter o problema para o DBA responsável.

Jamais remova, modifique ou mova de lugar qualquer arquivo do banco de dados porque o Oracle possui muitas referências à estes arquivos para seu controle e, inclusive, para permitir a recuperação de falhas.

Se você não tiver um DBA em sua empresa e for você mesmo o responsável, segue uma receita para tentar restaurar o banco de dados.

1) Diagnóstico

O diagnóstico pode ser realizado através do log de abertura do banco de dados. Na máquina do banco de dados, utilize um prompt do MS-DOS:

   C:\>set ORACLE_SID=nomeinstancia
   C:\>sqlplus /nolog
   SQL>conn / as sysdba
   SQL>shutdown immediate
   SQL>startup

Após executar estes comandos, caso a sua instância tenha problema para iniciar, o erro será apresentado.

2) Solução para CONTROLFILE corrompido

Avalie se o problema tem relação com CONTROLFILE corrompido, caso tenha ocorrido isso, vá até a pasta do CONTROLFILE e copie o control01.ctl como control03.ctl. Depois execute um novo SHUTDOWN IMMEDIATE seguido de um novo STARTUP.

Se quiser entender melhor sobre CONTROLFILES, acesse: http://docs.oracle.com/cd/B10500_01/server.920/a96521/control.htm

3) Solução para outras corrupções

Há situações de corrupção relacionadas a logs ou datafiles, diferente de CONTROLFILE, neste caso, pode ser uma saída indicar ao Oracle para tentar recuperar a instância automaticamente na abertura.

Os comandos abaixo podem restaurar a instância se o nível de corrupção não for extremamente severo.


   C:\>set ORACLE_SID=nomeinstancia
   C:\>sqlplus /nolog
   SQL>conn / as sysdba
   SQL>shutdown immediate
   SQL>startup mount
   SQL>alter database recover until cancel using backup controlfile;
   SQL>alter database recover continue default;
   SQL>alter database recover cancel;
   SQL>alter database open resetlogs;
   SQL>exit

 Boa sorte!

sábado, 17 de dezembro de 2011

Java NIO - Acesso randômico a arquivo

O Java NIO é uma das melhorias mais significantes para manipulação de I/O. A sigla NIO é de New I/O, ou seja, renovação do pacote java.io.* em relação ao acesso a recursos de memórias externas como um arquivo ou um cliente remoto (host). O pacote NIO está aninhado em java.nio.*.

Nota: I/O é o termo utilizado para denominar a troca de dados de uma área para outra em computação. Esta operação pode ser a comunicação ministrada por uma CPU para transitar dados da sua RAM para um HD, CD R/W, DVD R/W, pasta de rede, um PenDrive (flash), outro computador ou banco de dados. Numa linguagem de programação, este acesso é classificado de alto risco, pois quando os dados saem ou chegam de um recurso externo, muitas coisas podem ocorrer como o desligamento deste recurso, queda do meio físico da comunicação, falha no sistema lógico etc.

Durante minha experiência com Java NIO, me despertou atenção a sua capacidade de acesso não bloqueante. Isso mesmo, você escreve os dados em um recurso I/O como é um arquivo e pode programar para o gerenciador da escrita não deixar em espera sua linha de execução (thread). A mesma espera não é necessária se os dados enviados pelo recurso ainda não estiverem prontos.

O acesso não bloqueante torna possível a terceirização do controle de múltiplos I/O para o Java NIO sem requerer de você a criação de múltiplas threads (linhas de execução paralelas) para gerenciar este acesso. Entretanto, esta terceirização requer cuidados e domínio do tipo de recurso I/O referentes as cerimônias para abertura da comunicação, transferência de dados e encerramento da comunicação. O Java NIO não abstrai estes controles e caberá a você entender bem como funciona o tipo de recurso a operar.

Segue um exemplo de acesso randômico a arquivo para escrita (criação automática quando não existe) e posterior leitura. É necessário ter uma pasta "c:\temp" em seu PC ou mude este caminho no código:

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;


public class TesteFileChannel {


    public static void main(String[] args) {

        TesteFileChannel tfc = new TesteFileChannel();
       
        tfc.writeText();
       
        tfc.readText();
       
       
    }
   
    public void writeText() {

        String text = null;
        FileChannel rwChannel = null;
        MappedByteBuffer rwBuffer = null;

        try {

            // Texto para guardar
            text = "Olá mundo";

            // Abre canal de comunicação
            rwChannel = new RandomAccessFile("c:/temp/datafile.txt", "rw").getChannel();

            // Aloca uma fatia bytes para guardar o texto a partir do canal do arquivo
            rwBuffer = rwChannel.map(FileChannel.MapMode.READ_WRITE, 0, text.length());

            // Coloca o ponteiro na posição ZERO da memória mapeada
            rwBuffer.position(0);

            // Escreve os bytes do texto no arquivo
            rwBuffer.put(text.getBytes(), 0, text.getBytes().length);

            // Como o acesso é não bloqueante, neste caso é forçada
            // a efetivação escrita caso esteja pendente
            rwChannel.force(true);

            // Encerra canal de comunicação
            rwChannel.close();

            // Destrói o objeto representante da memória alocada 
            rwBuffer.clear();

            System.out.println("Concluída a escrita com sucesso: " + text);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public void readText() {

        byte[] bytes = null;
        String text = null;
        FileChannel rwChannel = null;
        MappedByteBuffer rwBuffer = null;

        try {

            // Abre canal de comunicação
            rwChannel = new RandomAccessFile("c:/temp/datafile.txt", "rw").getChannel();

            // Aloca uma fatia bytes para guardar o texto a partir do canal do arquivo
            rwBuffer = rwChannel.map(FileChannel.MapMode.READ_WRITE, 0, rwChannel.size());

            // Coloca o ponteiro na posição ZERO da memória mapeada
            rwBuffer.position(0);

            // Vetor para leitura
            bytes = new byte[(int) rwChannel.size()];

            // Lê os bytes do texto no arquivo
            rwBuffer.get(bytes, 0, bytes.length);

            // Converte bytes para texto
            text = new String(bytes);

            // Encerra canal de comunicação
            rwChannel.close();

            // Destrói o objeto representante da memória alocada 
            rwBuffer.clear();

            System.out.println("Concluída a leitura com sucesso: " + text);
           

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}