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:

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:

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:

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!