Archive for August, 2008

Poxa vida, tava preparando um post sobre RecordProcessor, e sai a versão nova do Jboss Seam ontem, e com ela um novo Identity Manager, e desta vez com suporte a LDAP!!!!
Bem, desta vez vou dividir em mais partes o post, nesta primeira parte vou focar apenas na configuração do Apache Directory Server (LDAP) e na autenticação com seam.

Pré requisitos:

Configurando o LDAP

Bem, o primeiro passo é criar uma aplicação com Seam, usando o bom e velho seam-gen. Veja este post para maiores informações.
Instale o Apache Directory Studio usando o update-site para eclipse e marque as opções Studio e Directory Server (isso vai lhe permitir iniciar um servidor LDAP de dentro do eclipse)

Bem, com o plugin configurado, abra a perspectiva de Servers do Directory Studio e crie um novo servidor, assim como na figura abaixo:

Servidor de LDAP do Directory Studio (clique para fullsize)

Servidor de LDAP do Directory Studio (clique para fullsize)

Inicie o servidor, clique com o botão direito sobre o mesmo, e então LDAP Browser -> New Connection. Isto vai conecta-lo ao servidor de LDAP, na aba esquerda “LDAP Browser” você pode agora criar os usuários e roles.

Bem, vamos criar uma árvore onde abaixo de nosso domain (dc), existe uma Organizational Unit (ou) para users e outra para roles.

Clique com o botão da direita em dc=example,dc=com e depois new entry. Selecione uma nova entrada e na tela de object classes “Organization Unit” e Add…

Adicionando uma OU (clique para fullsize)

Adicionando uma OU (clique para fullsize)

Na próxima tela no RDN use ou e roles como na figura abaixo:

Definindo o OU (clique para fullsize)

Definindo o OU (clique para fullsize)

Bem, agora repita este passo e crie uma ou com nome de users.

Bem, agora é preciso criar um usuário, basta seguir as telas abaixo:

Criando um usuário (clique para fullsize)

Criando um usuário (clique para fullsize)

Você deve adicionar os objetos Person e extensibleObject em sua entrada:

Adicionando extensible Object (clique para fullsize)

Adicionando extensible Object (clique para fullsize)

Na próxima tela é importante que seu DN fique da seguinte maneira : uid=$USER,ou=users,dc=example,dc=com pois esta é a string de pesquisa usada pelo componente do Seam no LDAP, então adicione apenas uid no seu RDN como na figura abaixo:

Cuidado com RDN... (clique para fullsize)

Cuidado com RDN... (clique para fullsize)

Na próxima tela você terá que adicionar dois campos a mais que são obrigatórios (CN=Common Name e SN=Surname) adicione um nome e sobrenome. Além disto, você deve adicionar um atributo member que vai apontar para o role que seu usuário participa.

Para adicionar um atributo clique no ícone mostrado na figura abaixo:

Clique no ícone em destaque (segundo da esquerda pra direita)

Clique no ícone em destaque (segundo da esquerda pra direita)

Agora escolha member:

Adicionando um member em nossa entrada (clique para fullsize)

Adicionando um member em nossa entrada (clique para fullsize)

Além disto você vai precisar de um userpassword:

Adicionando atributo userPassword

Adicionando atributo userPassword

No final, sua entrada deve se paracer com a figura abaixo:

Resultado final da entrada de um usuário (clique para fullsize)

Resultado final da entrada de um usuário (clique para fullsize)

Ok, o usuário esta configurado (a propósito, criei o usuário bob com senha bob :P )

Para cada novo role de um usuário, basta adicionar um novo atributo member para o usuário em questão

Agora podemos partir para o seam :)

Configurando o Identity Management

Configura o identityManagement é muito simples. IdentityManagement é uma classe que delega a um IdentityStore (no nosso caso LDAP), então você precisa configura o ldapIdentityStore e um security-identity-manager

Agora basta adicionar o ldapIdentityStore e security-identity-manager:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<security:ldap-identity-store name="ldapIdentityStore"
   			server-address="localhost"
   			server-port="10389"
   			user-DN-prefix="uid="
   			user-DN-suffix=",ou=users,dc=example,dc=com"
   			user-context-DN="ou=users,dc=example,dc=com"
   			user-password-attribute="userPassword"
   			role-context-DN="ou=roles,dc=example,dc=com"
   			role-DN-prefix="cn="
   			role-DN-suffix=",ou=roles,dc=example,dc=com"
   			bind-DN="uid=admin,ou=system"
   			bind-credentials="secret"
   			user-role-attribute="member"
   			user-object-classes="person,uidObject">
   </security:ldap-identity-store>
 
   <security:identity-manager identity-store="#{ldapIdentityStore}"></security:identity-manager>

Pronto, você pode inclusive remover seu authenticator agora :) O Seam cuida desta nova forma de autenticação para você.

Dos descritores acima quero falar apenas de dois pontos (ta tarde pra caralho e quero dormir :| ). Bem, o server-port não existe no XSD do seam, parece ser um bug pois o componente tem o atributo e você pode usar, eu já fui fominha e abri um bug mas podem usar sem problemas. Outro ponto é o atributo enabled que quando usado (nao foi nosso caso), deve ter o valor TRUE e não true, sabe por que? Abaixo um trecho do LDAPIdentityStore

431
if (LDAP_BOOLEAN_TRUE.equals(value)) return true;

O valor LDAP_BOOLEAN_TRUE é “TRUE” ou seja, se você usar “true” minúsculo vai levar graxa na cara.

Bom, agora, quando voce acessar sua aplicação e for na tela de login, ja vai estar usando o LDAP. Não precisou mexer em xhtml, nem em classe alguma. Não é uma beleza?

Pessoal, esse post eu fiz questão de varar a noite, primeiro porque só esta feature já é um motivo para vocês usarem Seam, segundo porque sou fominha e queria postar antes de qualquer um :D

O próximo post vou continuar daqui, vou mostrar como usar o IdentityStore para adicionar users via web no LDAP (sem você escrever uma linha de código), como funciona o novo mecanismo de permissions e como mesclar LDAP e banco de dados para armazenar permissions no banco de dados.

Inté

Poxa vida, essa aqui me deixou fora do sério. Então resolvi fazer um pequeno post de forma que outras pessoas que passem pelo mesmo problema possam achar uma informação relevante na web.

Bem, estou rodando o JBoss 4.2.3.GA e JBossWS 3.0.3 (sim, sou bleeding edge), e estava tudo perfeito :) . Então hoje, estava na Unimed BH prestando consultoria e para fazer alguns testes modifiquei meu script de init do jboss, adicionando o parâmetro: -Dcom.sun.management.jmxremote, para poder monitorar meu jboss com jconsole. Nada mais comum no dia a dia de um desenvolvedor certo?

Ok, voltei pra casa, e fui testar a aplicação do próximo post, e do nada: uma mensagem de erro surge:

20:57:05,922 ERROR [MainDeployer] Could not start deployment: file:/java/servers/jboss-4.2.3.GA/server/default/deploy/bobsam.ear/bobsam.jar/
java.lang.NullPointerException
at org.jboss.wsf.framework.deployment.WebAppGeneratorDeploymentAspect.generatWebDeployment(WebAppGeneratorDeploymentAspect.java:104)

NullpointerException numa classe do jboss??? Que será que eu fiz de errado? Poxa, vou voltar a versão para a 3.0.2… Mesmo erro. Re-instalei a versão do jboss, peguei uma 4.2.3 novinha em folha, mesmo erro (eu rodo de dentro do eclipse…).

Então, depois de achar que era erro na minha aplicação. Resolvi rodar o jboss por fora do eclipse…. Funcionou. Opa, como assim? Funciona fora do eclipse mas quando inicio pelo eclipse não? Tem coisa errada ai. Então abri meu launch configuration e vi o parâmetro -Dcom.sun.management.jmxremote, bem, não custa tentar né? Removi o danado, e não é que funcionou… uma pequena pesquisa no google e achei:
http://jira.jboss.com/jira/browse/JBWS-1800, acho que vou ter que esperar a próxima versão para usar jconsole com JBossWS.
Bem, um pequeno post para aqueles que pretendem usar JBossWS e se depararem com este NPE.
Inté

Bem, foi uma semana complicada, por isso o hiato tão grande desde meu último post, uma nova proposta consumiu todo meu tempo na empresa que trabalho, mas pelo menos tive a oportunidade de testar um conceito que vou apresentar em um próximo post :)

Bem, todo mundo fala de SOA e junto com essa sigla dos infernos, vem mais um monte de outras siglas e aparentemente a preferida de todos é BAM (Business Activity Monitoring). Muitos dos clientes que visito, reclamam de a suite JBoss não tem um BAM integrado, como outras ferramentas. O grande problema de se usar BAM, é que o cliente deve primeiro conhecer o seu negócio antes mesmo de querer monitorar algo, e isso sabemos que não reflete a realidade na maioria das vezes.

Bem, não satisifeitos em não ter BAM, começam a reclamar do SAM (Service Activity Monitoring). Bem, monitoramento de serviços o JBoss WS tem, e em dois nívels: Endpoint management (coberto neste post) e Records management (próximo post).

Se você deseja monitorar algo no seu JBoss eu recomendo que adquira a versão com subscrição e com isso o JON, que apesar de ainda não possuir um monitoramento para webservices (funcionalidade prevista para setembro deste ano), é muito simples criar um plugin para monitorar os webservices do jboss através de JMX.

Bem, apresentei então o console de métricas do JBoss WS:

Métricas do JBoss WS (clique para fullsize)

Métricas do JBoss WS (clique para fullsize)

A reação do cliente foi de repúdio claro, afinal de contas o Oracle tem um console “bonitinho”, e então me apresentaram a seguinte tela:

Oracle SAM (clique para fullsize)

Oracle SAM (clique para fullsize)

PQP, É foda viu, escolher OC4J como broker de webservices por causa dessa telinha? So por que tem esse gráficozinho de velocímetro? Bem, cheguei em casa puto, e gastei cerca de duas horas para produzir a aplicação que vou apresentar abaixo. Peço singelas desculpas pela falta de conhecimento em JFreeChart, e principalmente pelos componentes de imagem do richfaces, mas acredito que o que vale é provar um conceito: É possível termos um console bacana de SAM no jboss, e bem mais barato que pagar uma licensa absurda por causa deste consolezinho de OC4J.

Bem, mãos na massa. A primeira classe é minha MeterImage, que gera o gráfico usando bibliotecas do JFreeChart:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package com.furiousbob.ws;
 
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.awt.image.WritableRaster;
import java.io.Serializable;
 
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.DialShape;
import org.jfree.chart.plot.MeterInterval;
import org.jfree.chart.plot.MeterPlot;
import org.jfree.chart.plot.Plot;
import org.jfree.chart.plot.dial.ArcDialFrame;
import org.jfree.chart.plot.dial.DialBackground;
import org.jfree.chart.plot.dial.DialPlot;
import org.jfree.chart.plot.dial.DialPointer;
import org.jfree.chart.plot.dial.StandardDialScale;
import org.jfree.data.Range;
import org.jfree.data.general.ValueDataset;
 
public class MeterImage implements Serializable{
 
	private ValueDataset dataset;
	private JFreeChart chart;
	private String title;
 
 
 
	public void  createChart(){
		Plot plot = createPlot();
		chart = new JFreeChart(title,plot);
		chart.setBackgroundPaint(Color.WHITE);
	}
 
 
 
	private MeterPlot createPlot(){
		MeterPlot plot = new MeterPlot(dataset);
		plot.setBackgroundAlpha(0.0f);
        plot.setMeterAngle(180); 
        plot.setDialShape(DialShape.CHORD); 
        plot.setDialBackgroundPaint(Color.white);
        plot.setTickLabelsVisible(false);
        plot.setDialOutlinePaint(Color.black);
        plot.setOutlineStroke(new BasicStroke(6.0f));
        plot.setNeedlePaint(Color.black);
        plot.setDrawBorder(false);
 
        plot.addInterval(new MeterInterval("Good",new Range(0,55.0),new Color(0,0,0),null,Color.GREEN));
        plot.addInterval(new MeterInterval("Warn",new Range(55,80.0),new Color(0,0,0),null,Color.YELLOW));
        plot.addInterval(new MeterInterval("Bad",new Range(80,100.0),new Color(0,0,0),null,Color.RED));
        plot.setValuePaint(Color.black);
        plot.setUnits("%");
        return plot;
	}
 
	public BufferedImage getImage(){
		createChart();
		return chart.createBufferedImage(300, 300);
	}
 
	public ValueDataset getDataset() {
		return dataset;
	}
 
	public void setDataset(ValueDataset dataset) {
		this.dataset = dataset;
	}
 
	public String getTitle() {
		return title;
	}
 
	public void setTitle(String title) {
		this.title = title;
	}
}

Bem, preciso de um webservice para ser monitorado, então um SampleService quase HelloWorld:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.furiousbob.services;
 
import java.util.Random;
 
import javax.ejb.Stateless;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
 
import org.jboss.wsf.spi.annotation.WebContext;
 
@Stateless
@WebService
@WebContext(contextRoot="/furiousbob/services",urlPattern="/SampleService")
@SOAPBinding(use = SOAPBinding.Use.LITERAL, style = SOAPBinding.Style.DOCUMENT)
public class SampleServiceBean implements SampleService {
 
	@WebMethod
	public String sayHello(String name) {
		int delay = 0;
		try {
			Random r = new Random();
			delay = r.nextInt(3000);
			Thread.sleep(delay);
		} catch (Exception e) {
			// TODO: handle exception
		}
		return "Hello " + name +"! I've waited " + delay + " ms before saying hello back!";
	}
 
}

Notem que o método realiza um sleep de até 3s de forma randomica, fiz isto, para gerar valores alternados para poder coletar dados na minha aplicação: bobsam

A primeira tela, bem simples, lista todos serviços disponibilizados no servidor:

Listando serviços disponíveis no servidor (clique para fullsize)

Listando serviços disponíveis no servidor (clique para fullsize)

<ui:define name="body">
 
    <h:messages globalOnly="true" styleClass="message"/>
 
    <rich:panel>
    <f:facet name="header">Welcome!</f:facet>
		<rich:dataTable value="#{sam.registredEndpoints}" var="_endpoint">
			<rich:column>
				<f:facet name="header">Service name</f:facet>
				<s:link value="#{_endpoint}" view="/sam.xhtml">
					<f:param name="endpoint" value="#{_endpoint}"/>
				</s:link>
 
			</rich:column>
 
		</rich:dataTable>   
    </rich:panel>
 
</ui:define>

Agora, como vamos buscar nossos endpoints? JMX, o JBoss WS expõe seus endpoints através de um objeto EndpointRegistry onde é possível ver todos serviços listados, a classe WebServiceManagement possui o método responsável por isso, note o trecho de código nas linhas 83-97.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package com.furiousbob.ws;
 
 
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
import javax.imageio.ImageIO;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.Query;
 
import org.jboss.mx.util.MBeanProxyExt;
import org.jboss.mx.util.MBeanServerLocator;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Out;
import org.jboss.seam.annotations.Scope;
import org.jboss.wsf.framework.management.ManagedEndpointMBean;
import org.jboss.wsf.framework.management.ManagedEndpointRegistry;
import org.jboss.wsf.framework.management.ManagedEndpointRegistryMBean;
import org.jboss.wsf.spi.deployment.Endpoint;
import org.jfree.data.general.DefaultValueDataset;
import org.jfree.data.general.ValueDataset;
 
@Name("sam")
@Scope(ScopeType.CONVERSATION)
public class WebServiceManagementAction implements Serializable {
 
	private MeterImage meterImage = new MeterImage();
 
	private ValueDataset dataset;
 
 
	private String endpoint;
 
	@Out(required=false,scope=ScopeType.CONVERSATION)
	@In(required=false)
	private ManagedEndpointMBean mep;
 
	public void paint(OutputStream out, Object data){
		updateDataset();
		meterImage.setDataset(dataset);
		Pattern p = Pattern.compile("jboss.ws:context=(.*),endpoint=(.*)");
		Matcher matcher = p.matcher(endpoint);
		matcher.find();
		meterImage.setTitle("Performance indicator for: " + matcher.group(2));
		try {
			ImageIO.write(meterImage.getImage(), "png", out);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
 
 
	private void updateDataset(){
		long average = mep.getAverageProcessingTime();
		average = (average*100)/3000;
		this.dataset = new DefaultValueDataset(average);
	}
 
 
 
 
	public String getEndpoint() {
		return endpoint;
	}
 
 
 
 
 
	public void setEndpoint(String endpoint) {
		this.endpoint = endpoint;
	}
 
 
	public List<String> getRegistredEndpoints(){
		List<String> endpoints = new LinkedList<String>();
		try {
			MBeanServer server = MBeanServerLocator.locate();
			ManagedEndpointRegistryMBean registry = (ManagedEndpointRegistryMBean)MBeanProxyExt.create(ManagedEndpointRegistryMBean.class, "jboss.ws:service=EndpointRegistry");
			Set<ObjectName> names = registry.getEndpoints();
			for(ObjectName o : names){
				endpoints.add(o.getCanonicalName());
			}
 
		} catch (Exception e) {
			e.printStackTrace();
		}
		return endpoints;
	}
 
 
	public void create(){
		try {
			MBeanServer server = MBeanServerLocator.locate();
			this.mep = (ManagedEndpointMBean)MBeanProxyExt.create(ManagedEndpointMBean.class, endpoint);
		} catch (Exception e) {
 
		}
	}
 
}

O método public List getRegistredEndpoints() retorna uma lista com os nomes de todos webservices registrados em nosso servidor. Agora, basta selecionarmos nosso serviço para apresentarmos o seu gráfico. A listagem abaixo mostra nossa página sam.xhtml

1
2
3
4
5
6
7
8
9
10
11
12
13
<ui:define name="body">
 
    <h:messages globalOnly="true" styleClass="message"/>
 
    <rich:panel>
    <f:facet name="header">Service Activity Monitoring for #{sam.endpoint}</f:facet>
	<a4j:mediaOutput element="img" cacheable="false" createContent="#{sam.paint}" value="#{meterImage}" mimeType="image/png" >
		<a4j:actionparam name="cid" value="#{conversation.id}"></a4j:actionparam>
	</a4j:mediaOutput>
 
    </rich:panel>
 
</ui:define>

Bem, pelo pouco que aprendi de suporte a imagens com richfaces é que preciso implementar o método paint. Este método é o responsável por invocar o ManagedEndpoint, buscar as métricas que desejo plotar (no caso estou verificando o tempo médio de processamento do serviço) e então gerar a imagem. O resultado final é a imagem abaixo:

Gráfico de monitoramento (clique para fullsize)

Gráfico de monitoramento (clique para fullsize)

Claro que se não gerarmos alguns acessos ao nosso serviço não será possível ver algum resultado. Para isso eu uso o soap-ui que dentre outras inúmeras funcionalidades , possui um assistente para gerar testes de carga, a figura abaixo mostra um teste de carga feito com o soap-ui:

Testes de carga com SOAP-ui (clique para fullsize)

Testes de carga com SOAP-ui (clique para fullsize)

Após 1 min, com 100 threads no modelo de variância (que randomiza o número de acessos concorrentes) o soap-ui gerou 4174 requisições no meu webservice (Alguém ainda duvida que JBoss WS é escalável????).

Ao acessar a página de monitoramento do meu webservice, temos agora uma figura que representa o estado de nosso serviço:

Gráfico de monitoramento (clique para fullsize)

Gráfico de monitoramento (clique para fullsize)

Bem, agora não tem mais motivo para ficar achando que não dá pra ter SAM com JBoss não é? Eu (que sou um cara TOSCO assim como meu amigo e mentor Edgar Silva) consegui criar em 2 horas, isso porque apanhei do JFreeChart :P , uma aplicação simples para SAM.

Bem, é isso ai pessoal, Inté a próxima.