Sessão serializável e explorável

Aproveitando o embalo do post: http://www.thiagovespa.com.br/blog/2011/03/31/violando-o-encapsulamento/ resolvi criar uma aplicação para explorar a sessão de uma aplicação Web e verificar quais atributos são Serializáveis ou não. Para quem já precisou rodar uma aplicação em cluster com replicação de sessão sabe que é fundamental que todos os atributos que estão na sessão implemente a interface Serializable. Caso contrário, o erro NotSerializableException irá ocorrer.

Infelizmente existem bibliotecas de terceiros e de frameworks que não temos o código e nem documentação suficiente para saber se o atributo é serializável ou não, mas felizmente, temos maneiras de verificar isso utilizando código. Para resolver esse problema criei uma servlet que permiti navegarmos nas classes utilizadas nos atributos de sessão e explorar o conteúdo dos objetos.

Para recuperar as informações das classes, utilizei um map que associa a classe com suas informações. Nesse map estarão todas as classes envolvidas, seja por herança (linha 14) ou por composição (linha 17).

	public void getAllFields(Class c, String fieldName) {
		if (mapOfClasses.containsKey(c)) {
			return;
		}
		Field[] fields = c.getDeclaredFields();
		List fieldInfoList = new ArrayList();
		for (Field f : fields) {
			f.setAccessible(true);
			fieldInfoList.add(new FieldInfo(f.getName(), f.getType(), f
					.getModifiers()));
		}
		mapOfClasses.put(c, fieldInfoList);
		if (c.getSuperclass() != null) {
			getAllFields(c.getSuperclass(), "extends");
		}
		for (FieldInfo aFieldInfo : fieldInfoList) {
			getAllFields(aFieldInfo.getFieldClass(), aFieldInfo.getFieldName());
		}
	}

Esse método é chamado recursivamente para todos os tipos de atributos e para a classe pai. Dessa forma, o map fica composto por todas as classes envolvidas na sessão Web. Essas informações são auxiliares para verificar quais classes implementam a interface Serializable, e quais são os atributos envolvidos e se eles implementam também.

Os recursos mais bacanas implementados foram: navegar pelos atributos de sessão, calcular o tamanho que cada atributo fica em memória (isso permite análise para otimização de uso de sessão) e verificar quais atributos são serializáveis de maneira interativa. E para isso necessitamos verificar se o objeto é serializável ou não. Com isso seria necessário verificar se todos os seus atributos (não estáticos e blabla :D) acessíveis  são serializáveis, se a classe pai não for serializável, ela tem que ter um construtor padrão, entre outros. Existem vários aspectos a serem verificados. Mais informações você pode encontrar no próprio javadoc da interface Serializable. Para facilitar essa análise e realizá-la de maneira mais simples, basta serializar o objeto :D. Se tivermos um NotSerializableException significa que a classe não é serializável, caso contrário ela é serializável e como já serializamos ela, podemos recuperar o tamanho que ficará em memória. Podemos fazer isso utilizando um ByteArrayOutputStream e gravar o objeto nele. O código é bem simples:

	public int getSerialSize(Object o) throws IOException {
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		ObjectOutputStream oos;
		oos = new ObjectOutputStream(out);
		oos.writeObject(o);
		oos.close();
		return out.toByteArray().length;
	}

Dessa maneira fica muito simples analisar os objetos na sessão. Coloquei algumas cores para sinalizar cada objeto e o resultado final é o seguinte:

Session Serializable Checker
Session Serializable Checker

Dessa forma sabemos que o objeto da classe LocaleMB não é serializável. Podemos clicar no nome da classe para verificar quais atributos estão envolvidos e se ela implementa a interface serializable conforme mostrado abaixo:

Session Serializable Checker - 2
Session Serializable Checker - 2

Como podemos ver, a classe não está em amarelo, o que significa que ela implementa a interface Serializable, e podemos ver que apenas colocando implements Serializable não resolve o nosso problema. Se navegarmos dentro dos valores do objeto iremos ver algo interessante:

Session Serializable Checker - 3
Session Serializable Checker - 3

Embora a classe implemente a interface Serializable, um dos seus atributos não é serializável. Só conseguimos ver essa informação de maneira simples, graças a esse utilitário. E por isso vou disponibilizá-lo, caso alguém tenha problemas de NotSerializableException.

Para colocá-lo no seu projeto Web é muito simples. Copie o jar desse projeto para a pasta WEB-INF/lib (ou bibliotecas, build path, depende da IDE de sua preferência). Se o seu projeto utilizar Servlet 3.0, não é necessária nenhuma configuração adicional, pois ele já possui o mapeamento para Servlet  - @WebServlet(urlPatterns = { "/serialcheck" }). Caso você deseja sobreescrever esse mapeamento ou utiliza uma especificação de Servlet inferior, abra o seu arquivo web.xml e adicione as seguintes linhas:

...
 <servlet>
  <servlet-name>SerializableChecker</servlet-name>
  <servlet-class>br.com.thiagovespa.serializablechecker.SerializableChecker</servlet-class>
 </servlet>
 <servlet-mapping>
  <servlet-name>SerializableChecker</servlet-name>
  <url-pattern>/serialcheck</url-pattern>
 </servlet-mapping>
...

Aí é só acessar o endereço:  http[s]://<ip>:<porta>/<contexto>/serialcheck (p. ex: http://localhost:7001/portal/serialcheck) e brincar um pouco. Sugestões são bem vindas!

Sobre: Thiago Galbiatti Vespa

Thiago Galbiatti Vespa é mestre em Ciências da Computação e Matemática Computacional pela USP e bacharel em Ciências da Computação pela UNESP. Coordenador de projetos do JavaNoroeste, membro do JCP (Java Community Process), consultor Oracle, arquiteto de software de empresas de médio e grande porte, palestrante de vários eventos e colaborador de projetos open source. Possui as certificações: Oracle Certified Master, Java EE 5 Enterprise Architect – Step 1, 2 and 3; Oracle WebCenter Portal 11g Certified Implementation Specialist; Oracle Service Oriented Architecture Infrastructure Implementation Certified Expert; Oracle Certified Professional, Java EE 5 Web Services Developer; Oracle Certified Expert, NetBeans Integrated Development Environment 6.1 Programmer; Oracle Certified Professional, Java Programmer; Oracle Certified Associate, Java SE 5/SE 6