No passado eu escrevi três artigos sobre opções. A ideia era falar de algumas estratégias interessantes, principalmente Credit Option Spreads, que são as minhas preferidas. Estou disponibilizando no GitHub umas classes em Ruby para trabalhar com opções. O legal é que elas acessam o Yahoo!Finance para manter as cotações atualizadas. No passado eu utilizei essas classes para me alertar sobre boas possibilidades de negócios com credit spreads, ou seja, quando o spread aumentava e o retorno ficava atraente em relação ao risco. Quem quiser dar uma olhada pode acessar os links abaixo:
UPDATE: A documentação completa e o download da última versão pode ser encontrado aqui: http://www.mentacontainer.org
Inspirado na implementação de IoC do framework Mentawai, estou liberando a primeira versão do MentaContainer. Sem mais falação então vamos ao que interessa:
1) Suporte a construtores e setters para iniciar os seus beans:
Container c = new MentaContainer();
c.ioc("myString1", String.class);
String myString1 = c.get("myString1");
System.out.println(myString1); // ==> "" ==> default constructor new String() was used
c.ioc("myString2", String.class).addInitValue("saoj");
String myString2 = c.get("myString2");
System.out.println(myString2); // ==> "saoj" ==> constructor new String("saoj") was used
c.ioc("myDate1", Date.class).addProperty("hours", 15)
.addProperty("minutes", 10)
.addProperty("seconds", 45);
Date myDate1 = c.get("myDate1");
System.out.println(myDate1); // ==> a date with time 15:10:45
2) Singleton:
Container c = new MentaContainer();
c.ioc("myString", String.class, true /* singleton */).addInitValue("saoj");
String s1 = c.get("myString");
String s2 = c.get("myString");
System.out.println(s1 == s2); // ==> true ==> same instance
System.out.println(s1.equals(s2)); // ==> true => of course
3) Wiring: (caso clássico DAO depende de uma Connection)
public interface UserDAO {
public String getUsername(int id);
}
public class Connection {
}
public class JdbcUserDAO implements UserDAO {
private Connection conn;
public void setConn(Connection conn) { this.conn = conn; }
public String getUsername(int id) {
// connection will be injected by the container...
if (conn == null) throw new IllegalStateException("conn is null!");
// use the connection to get the username...
return "saoj";
}
}
Container c = new MentaContainer();
c.ioc("userDAO", JdbcUserDAO.class);
c.ioc("connection", Connection.class); // in real life this would be a connection pool
// or the hibernate SessionFactory (see Factories below)
// "conn" = the name of the property
// Connection.class = the type of the property
// "connection" = the source from where the dependency will come from
c.autowire("conn", Connection.class, "connection");
UserDAO userDAO = c.get("userDAO");
// the container detects that userDAO has a dependency: name = "conn" and type = "Connection.class"
// where does it go to get the dependency to insert?
// In itself: it does a Container.get("connection") => "connection" => the source
System.out.println(userDAO.getUsername(11)); // ==> "saoj" ==> connection is not null as expected...
4) Injection: (populando um objeto qualquer com os beans do container)
public class SomeService {
private UserDAO userDAO;
public void setUserDAO(UserDAO userDAO) {
this.userDAO = userDAO;
}
public void doSomething() {
System.out.println(userDAO.getUsername(11));
}
}
Container c = new MentaContainer();
c.ioc("userDAO", JdbcUserDAO.class);
c.ioc("connection", Connection.class);
c.autowire("conn", Connection.class, "connection");
SomeService service = new SomeService();
c.populate(service); // populate (inject) all properties of SomeService with
// beans from the container
service.doSomething(); // ==> "saoj"
5) Factories: (utilizando qualquer factory dentro do container)
public class SomeFactory {
public String giveMeSomething() {
return String.valueOf(System.nanoTime());
}
}
// in real life this will be a SessionFactory, a ConnectionPool or any factory...
SomeFactory factory = new SomeFactory();
Container c = new MentaContainer();
// giveMeSomething => method that will be called to return object
Component generic = new GenericComponent(factory, "giveMeSomething");
c.ioc("myFactory", generic);
String s1 = c.get("myFactory");
Thread.sleep(5); // so strings are different... my cpu is fast!
String s2 = c.get("myFactory");
Assert.assertNotNull(s1);
Assert.assertNotNull(s2);
Assert.assertTrue(s1 != s2);
Assert.assertTrue(!s1.equals(s2));
Quando adquiri o IPad fui logo checar as questões fundamentais, pelo menos para mim. Entre elas a principal dúvida era: É o IPad um bom leitor portátil de livros? Ele pode ser confortavelmente manuseado na cama ou no sofá? Ninguém vai utilizar um IPad sentado na sua escrivaninha, na frente do seu computador. A não ser que a pessoa não saiba usar o mouse. É muito mais confortável surfar a Internet com um mouse e digitar num teclado real do que segurar um dispositivo de meio quilo e digitar no teclado virtual. Então vamos às questões:
Lendo livros com o IPad:
Primeira coisa que fiz foi instalar o iBooks e baixar alguns livros gratuitos. Há bastante clássicos de Shakespeare, Tolstói, etc. Baixei o Walden de Thoreau, que ainda não tinha lido. Minha conclusão: O aplicativo iBooks é fabuloso! O texto é bem legível, a navegação é perfeita com o padrão Apple de usabilidade e a função para consultar qualquer palavra no dicionário é killer. Uma pena que só dê para consultar palavras na lingua inglesa mas futuramente a Apple deve dar um jeito nisso. O iBooks te informa também quantas páginas ainda restam para terminar um capítulo, pois é sempre bom se programar para interromper a leitura no início de um novo capítulo.
Para não ser parcial, baixei e dei uma olhada no Kindle para IPad. Não dá para comparar com o iBooks. Não tem dicionário e a usabilidade é bem inferior. Também não tem aquele efeito maroto da página virando como em um livro real.
Meu único problema com o iBooks foi que não consegui encontrar na Apple Store nenhum livro em português. Talvez porque eu estou acessando a Apple Store americana, mas deveria ter um jeito de ter acesso a livros em outras línguas. Alguém aí que possui acesso a Apple Store brasileira pode informar se há bons livros em português?
Lendo PDFs no IBooks:
Esse é outro ponto crítico, pois a maioria dos meus livros estão em PDF. Minha primeira tentativa foi converter de PDF para ePub, de forma que eu pudesse ler esses livros no iBooks e usar o seu fantástico dicionário embutido. Para isso baixei o software gratuito Caliber. Fracasso total! A conversão fica horrível, bagunçando o livro todo. O Caliber oferece dezenas de opções de configuração, todas muito complexas, logo não sei se há alguma maneira de obter um output melhor. Após ler num forum que a conversão de PDF para ePub é mais uma arte do que uma ciência, decidi por abandonar essa abordagem e partir para os PDF readers.
Lendo PDFs via PDF readers:
Minha primeira aposta foi na app GoodReader, do qual eu já tinha lido bons comentários. Paguei $0.99, baixei, instalei, adicionei um PDF via iTunes, sincronizei, abri o aplicativo e… Excelente só um probleminha: o texto fica bem miúdo, praticamente ilegível. Vale lembrar que a tela do IPad é grande se comparada a do Iphone. Mas se comparada a de um desktop é bem pequena. Então pensei comigo: Basta dar um zoom aqui para melhorar isso. Dei dois cliques no centro da tela e para minha surpresa o texto ficou grande demais, extrapolando a borda do IPad. Então tive que ajeitar no dedo o zoom (usando dois dedos no tal do pinch) para que o texto ficasse no tamanho ideal para caber na tela do IPad. Segunda surpresa desagradável: o pinch do GoodReader é sensível demais. Fica difícil encontrar o zoom correto assim. Bom, após alguma concentração consegui colocar a página no tamanho ideal, maximizando o tamanho do texto sem extrapolar a borda do IPad. Li a primeira página e para mudar para a segunda tem que dar um toque na parte inferior (por que não na lateral?), mas como a página estava com zoom ela sofreu um scroll. Foi necessário dar um segundo toque para finalmente mudar para a próxima página que também não veio na posição certinha, exigindo mais toques e ajustes. Conclusão: quem disse que o GoodReader é bom não é tão exigente como eu quando o assunto é qualidade e comodidade. O GoodReader tem outras funcionalidades interessantes mas se o BÁSICO, que é possibilitar uma boa leitura, não tem qualidade então não me interessa aquela outra funcionalidade ninja para dar a volta ao mundo.
Após desistir do GoodReader paguei a fortuna de $3.99 e baixei o PDF Reader Pro Edition for the IPad. Esse era tão ruim que aceitei o prejuízo e removi do meu IPad.
Quando estava quase desistindo me deparei com o app MyPDFs num forum qualquer sobre IPad. Esse é o cara. Ao invés de fazer um monte de firula, os desenvolvedores se preocuparam em fazer o básico bem feito. Dando dois toques no meio da tela faz com que o texto se ajuste perfeitamente em torno da borda do Ipad, maximizando o zoom. Wow! Clicando na lateral do IPad você muda de página e adivinhe: a próxima página entra com o tamanho certinho, já pronta para ser lida. Você pode também esconder o menu superior para obter mais espaço de tela, podendo assim dar um zoom maior. Não tem o contador regressivo de páginas por capítulo, mas eu posso viver sem isso. Conclusão: se quer ler PDFs sem dor, use o MyPDFs. “Less is more!”
Sobre o peso do IPad:
Sim, o IPad não é leve. Pesa 680 gramas. A não ser que você esteja lendo o livro “A Lanterna na Popa” de Roberto Campos, a sensação inicial será a de estar segurando um tijolo. Se você apoia ele sobre as pernas ou em cima de uma almofada então não há qualquer problema. A dificuldade surge quando você segura ele no ar e apenas com uma mão. É necessária alguma experimentação para encontrar a melhor maneira de segurá-lo, sem deixar o polegar dentro da tela. Ainda não tenho uma opinião formada sobre essa questão. O que posso afirmar é que ontem li meu Google Reader na cama, segurando o IPad com uma das mãos, por quase uma hora e sobrevivi. Acho que a academia tem me ajudado nesse sentido.
Sobre a luz de fundo do IPad:
Acho que isso é uma coisa extremamente subjetiva. É claro que a luz de fundo cansa a vista mais rápidamente do que ePaper ou um livro impresso. A questão será quem se cansará primeiro: a sua vista ou você de ler alguma coisa. Concordo que ficar lendo no IPad por mais de 3 horas pode cansar a vista. Entretanto eu provavelmente vou ficar cansado de ler antes disso e interromper a leitura anyways. Vale notar que o IPad te permite regular o brilho. Então se você pretende ler por 5 horas sem parar é melhor colocar no brilho mínimo.
Sobre o teclado virtual do IPad:
Isso não tem muito haver com livros, mas vou deixar minha opinião aqui, pois durante a leitura você pode desejar fazer uma anotação ou responder um email que acabou de chegar. Para quem cata milho não vai fazer muita diferença. Mas para quem sabe digitar com os 10 dedos há um problema intransponível aqui: A sua mão tem que flutuar no ar. Aquele estado de relaxamento onde sua mão fica apoiada sobre o teclado, tocando as teclas, enquanto vc digita ou faz uma pausa simplesmente não existe. Qualquer esbarro na tela do IPad vai pressionar teclas por engano. Acho que provavelmente farei como o Iphone: Usarei apenas uma mão e catarei milho com o indicador. Ou melhor: vou levantar da cama e ir para a escrivaninha para responder o email via desktop mesmo.
Nesse screencast eu crio uma aplicação simples para comentar videos do YouTube com o Ruby on Rails. Na segunda parte eu utilizarei Ajax para não precisar dar um reload na página a cada novo comentário evitando assim que o video seja interrompido.
Nesse primeiro screencast inicio a codificação da classe Gene que será utilizada para resolver o problema descrito no site http://www.ai-junkie.com. Faço uma breve introdução sobre algoritmos genéticos e sobre o problema que iremos resolver, começando a codificar uma solução em Ruby com a ajuda do RSpec.
Eu já tinha blogado sobre isso, mas o tema é realmente complicado. Então resolvi fazer esse screencast onde tento desmistificar esse assunto com vários exemplos diretamente no IRB. Você verá a diferença fundamental entre uma eigenclass e uma metaclass através de sacadas de metaprogramação do Ruby. Também deixo algumas questões para o debate pois esse tema é controverso.
* Os primeiros 5 segundos da minha fala, onde eu falo “olá” e me apresento, foram cortados pelo programa que faz o screencast. Whatever…
Esse é o meu primeiro screencast onde falo sobre como criar os seus próprios RubyGems e disponibilizá-los para o mundo através do site http://rubygems.org.
A classe HelloPublisher.java ,descrita na parte 1 dessa série sobre SOA, serve apenas para testar o nosso web service, não sendo viável para o ambiente de produção. Veremos então como publicar o nosso serviço num web container como o Tomcat.
Crie um projeto novo para o Tomcat e copie o pacote ws.hello junto com o código para dentro do diretório onde fica o seu código-fonte, no meu caso WEB-INF\src.
Crie o arquivo WEB-INF\web.xml com o seguinte código XML:
A classe com.sun.xml.ws.transport.http.servlet.WSServlet é o servlet que vai publicar e servir para o mundo o serviço. Já a classe com.sun.xml.ws.transport.http.servlet.WSServletContextListener é responsável por ler o arquivo sun-jaxws.xml com a especificação da implementação do serviço. Crie o arquivo WEB-INF\sun-jaxws.xml abaixo:
Até o momento temos utilizado a implementação do JAX-WS que vem junto com o Java 6, mas agora teremos que utilizar a última versão desse framework pois as classes acima não estão presentes na implentação que vem junto com a distribuição da JSE. Para baixar e instalar o JAX-WS é fácil:
Execute o comando java -jar jaxwsXXXXXX.jar para instalá-lo.
O diretório jaxws-ri será criado no mesmo diretório onde você baixou o jar do jax-ws. Dentro desse diretório, na pasta lib, estão os jars que precisaremos adicionar na nossa aplicação web. Coloque os jars a seguir dentro do seu diretório WEB-INF\lib: jaxb-impl.jar, jaxws-rt.jar, stax-ex.jar e streambuffer.jar.
Estamos quase prontos para publicar o nosso serviço, faltando ainda utilizar o programa wsgen para gerar as classes auxiliares do nosso web service. Rode o comando abaixo:
Repare que fizemos isso dentro do diretório WEB-INF\classes para que as classes geradas fiquem no lugar correto dentro do pacote ws.hello.jaxws.
Nota: Temos utilizado até agora a versão do wsgen que vem junto com o Java 6. Caso queira utilizar a última versão do wsgen e do wsimport basta colocar o diretório jaxws-ri\bin que você baixou no seu path.
Feito isso você está pronto para rodar a sua aplicação web e publicar o seu serviço para o mundo. A estrutura do seu projeto web deve ter ficado assim:
Assim como no caso do Ruby, vamos utilizar o programa wsimport do JAX-WS para gerar automaticamente todas as classes que precisamos a partir do WSDL e codificar nosso cliente de maneira muito fácil.
-p ws.hello.client.wsdl Pacote onde as classes serão geradas
-d classes Diretório onde as classes (*.class) serão copiadas
-s src Diretório onde o código fonte (*.java) será gerado
Resumindo: O código gerado terá a linha package ws.hello.client.wsdl em todos as classes Java. O código fonte (arquivos *.java) vai ficar em src\ws\hello\client\wsdl e as correspondentes classes (ele gera *.class também) em classes\ws\hello\client\wsdl.
Feito isso basta você pegar o port do serviço que nada mais é que uma interface com todos os métodos do serviço para você chamar como se estivesse fazendo chamadas locais de uma API. Veja como é fácil:
package ws.hello.client;
import java.util.List;
import ws.hello.client.wsdl.Hello;
import ws.hello.client.wsdl.HelloImplService;
import ws.hello.client.wsdl.User;
public class HelloClient {
public static void main(String[] args) {
// É pegar o port do serviço e partir para o abraço...
HelloImplService service = new HelloImplService();
Hello hello = service.getHelloImplPort();
// Agora tudo é fácil...
System.out.println(hello.sayHello());
System.out.println(hello.sayHelloTo("Sergio"));
// Cria um objeto ws.hello.client.wsdl.User
User u = new User();
u.setUsername("saoj");
u.setEmail("saoj@saoj.com.br");
System.out.println(hello.sayHelloToUser(u));
u = hello.getUserFromEmail("saoj@saoj.com.br");
System.out.println("User: " + u.getUsername());
List<User> list = hello.getAllUsers();
for(int i = 0; i < list.size(); i++) {
u = list.get(i);
System.out.println("U" + i + ": " + u.getUsername());
}
}
}
Note que o objeto User (ws.hello.client.wsdl.User) também é criado de forma automática pelo wsimport.
No próximo artigo iremos analisar como disponibilizar o seu serviço através do Tomcat.
Continuando o nosso tutorial vamos implementar agora um cliente em Ruby para consumir todos os métodos do nosso serviço. Utilizaremos o Soap4R que é a biblioteca padrão do Ruby para SOAP.
Atenção: A distribuição padrão do Ruby já vem com o Soap4R. Entretanto queremos utilizar a última versão dessa biblioteca e para isso temos que instalar o seu gem.
c:\>gem install soap4r
Successfully installed httpclient-2.1.5
Successfully installed soap4r-1.5.8
2 gems installed
Installing ri documentation for httpclient-2.1.5...
Installing RDoc documentation for httpclient-2.1.5...
Quando você instala o Soap4R você ganha de presente o script wsld2ruby.rb que pode ser utilizado para gerar as classes auxiliares em Ruby a partir do WSDL do serviço publicado. Essas classes serão de grande ajuda na hora de codificar o cliente, como veremos mais adiante.
Gere as classes auxiliares com o comando abaixo:
C:\ruby>wsdl2ruby.rb --wsdl http://127.0.0.1:54321/hello?wsdl --type client --force
I, [2009-06-16T17:57:11.015000 #4760] INFO -- app: Creating class definition.
I, [2009-06-16T17:57:11.015000 #4760] INFO -- app: Creates file 'HelloImplService.rb'.
I, [2009-06-16T17:57:11.031000 #4760] INFO -- app: Creating mapping registry definition.
I, [2009-06-16T17:57:11.031000 #4760] INFO -- app: Creates file 'HelloImplServiceMappingRegistry.rb'.
I, [2009-06-16T17:57:11.046000 #4760] INFO -- app: Creating driver.
I, [2009-06-16T17:57:11.046000 #4760] INFO -- app: Creates file 'HelloImplServiceDriver.rb'.
I, [2009-06-16T17:57:11.062000 #4760] INFO -- app: Creating client skelton.
I, [2009-06-16T17:57:11.062000 #4760] INFO -- app: Creates file 'HelloImplServiceClient.rb'.
I, [2009-06-16T17:57:11.062000 #4760] INFO -- app: End of app. (status: 0)
Veja agora como podemos codificar um cliente para os nossos serviços em Ruby:
# Cliente em Ruby para acessar o serviço http://localhost:54321/hello?wsdl
# Precisamos dessas duas linhas abaixo para
# forçar a utilização do gem do Soap4R ao
# invés da distribuição desatualizada
# que vem dentro do Ruby
require 'rubygems'
gem 'soap4r'
# Importa a classe gerada pelo wsdl2ruby.rb
require 'HelloImplServiceDriver'
# Então só precisamos fazer isso:
service = Hello.new
# O método sayHello não recebe nenhum parâmetro
puts service.sayHello(nil).m_return
# Mesmo quando um método não recebe nenhum parâmetro
# o mais certo é passar o objeto SOAP corresopndente
puts service.sayHello(SayHello.new).m_return
# Para cada serviço (método) é criado um objeto correspondente
# para servir como um transportador dos parâmetros do serviço
puts service.sayHelloTo(SayHelloTo.new("Sergio")).m_return
# O wsdl2ruby também cria o objeto User esperado como parâmetro
# pelo serviço sayHelloToUser
puts service.sayHelloToUser(SayHelloToUser.new(User.new("saoj@saoj.com.br", "saoj"))).m_return
# Para o serviço que retorna um User recebemos um User como tem que ser
u = service.getUserFromEmail(GetUserFromEmail.new("saoj@saoj.com.br")).m_return
puts "User: #{u.username} / #{u.email} / #{u}"
# E para o serviço que retorna um array, recebemos um Ruby array
# OBS: Nesse caso não precisamos chamar o método m_return pois o
# valor retornado já é um Array de Users
list = service.getAllUsers(GetAllUsers.new)
list.each_with_index do |v,k|
puts "U#{k}: #{v.username} / #{v.email}"
end
Caso o código acima não tenha ficado claro pra você, dê uma olhada no arquivo HelloImplService.rb que foi gerado automaticamente pelo wsdl2ruby. Nesse arquivo você encontrará a definição das classes utilizadas pelo nosso cliente tais como SayHelloTo, SayHelloToUserResponse, User, etc.
require 'xsd/qname'
# OBS: Para o código não ficar muito grande, algumas partes foram omitidas.
# {http://hello.ws/}sayHelloToUser
# arg0 - User
class SayHelloToUser
attr_accessor :arg0
def initialize(arg0 = nil)
@arg0 = arg0
end
end
# {http://hello.ws/}user
# email - SOAP::SOAPString
# username - SOAP::SOAPString
class User
attr_accessor :email
attr_accessor :username
def initialize(email = nil, username = nil)
@email = email
@username = username
end
end
# {http://hello.ws/}sayHelloToUserResponse
# m_return - SOAP::SOAPString
class SayHelloToUserResponse
def m_return
@v_return
end
def m_return=(value)
@v_return = value
end
def initialize(v_return = nil)
@v_return = v_return
end
end
# {http://hello.ws/}sayHelloTo
# arg0 - SOAP::SOAPString
class SayHelloTo
attr_accessor :arg0
def initialize(arg0 = nil)
@arg0 = arg0
end
end
# {http://hello.ws/}sayHelloToResponse
# m_return - SOAP::SOAPString
class SayHelloToResponse
def m_return
@v_return
end
def m_return=(value)
@v_return = value
end
def initialize(v_return = nil)
@v_return = v_return
end
end
# {http://hello.ws/}getAllUsers
class GetAllUsers
def initialize
end
end
# {http://hello.ws/}getAllUsersResponse
class GetAllUsersResponse < ::Array
end
Nada te impede por exemplo de “abrir” a classe User que foi gerada automaticamente no arquivo acima para melhorá-la adicionando novos métodos:
class User
def to_s
"#{username}: #{email}"
end
end
No próximo artigo iremos analisar a fundo como implementar um cliente em Java para o nosso serviço.
Destaque