Unloading Java Class

Estava a rever algumas classes que escrevi para um projeto ejb e me deparei com a seguinte situação:

Uso direto e reto bundles e constant object [hardcore java 2004] em meus componentes contudo ainda fico fulo da vida por ter de reiniciar as instâncias dos meus servers quando algum bundle é alterado (duvido que niguém aqui não tenha se deparado com o mesmo problema).

Enfim: preciso de uma forma de reinicializar essas “constantes” só que isso fica difícil com membros estáticos.

Pensei em descarregar as classes com as constantes mas vi que o buraco é mais embaixo (classloader).

Achei o seguinte na web:
http://blog.taragana.com/index.php/archive/how-to-unload-java-class/

http://portal.acm.org/citation.cfm?id=609750

Alguém aqui já fez algo semelhante?

Woody

Não é bem mais fácil reler os arquivos com as mensagens? Se sim, tem isso pronto já aqui.

vou dar uma olhada e te digo.

valeu

cara, continuo na mesma.

Posso usar a classe que vc indicou (ReloadableResourceBundleMessageSource) pra carregar minhas constantes numa boa mas e depois que elas forem carregadas? Lembre que estou usando membros estáticos (isso pra poder identificar qq erro de digitação em tempo de compilação e não ter de procurar nada em cache - fica tudo em mémória).

Essa abordagem precisa ser usada com cautela - eu sei. Carregar muita coisa em memória em vez de só carregar qdo necessário é sempre perigoso. Tento balancear o uso desse artifício limitando-o a alguns casos bem específicos.

Vamos aos exemplos, acho que vai ficar mais fácil de me fazer entender:

ServiceConstants.properties [code]
user.adm = adm
user.pwd = adm

SLGerenteFilmes.local = com.gec.ejb.service.SLGerenteFilmesLocalHome
SLGerenteFilmes.remote = com.gec.ejb.service.SLGerenteFilmesHome
SLGerenteFilmes.use = remote

SLGerenteLocacoes.local = com.gec.ejb.service.SLGerenteLocacoesLocalHome
SLGerenteLocacoes.remote = com.gec.ejb.service.SLGerenteLocacoesHome
SLGerenteLocacoes.use = remote

SLGerenteUsuarios.local = com.gec.ejb.service.SLGerenteUsuariosLocalHome
SLGerenteUsuarios.remote = com.gec.ejb.service.SLGerenteUsuariosHome
SLGerenteUsuarios.use = remote

SLSequence.local = com.gec.ejb.util.SLSequenceLocalHome

Categoria.local = com.gec.ejb.model.CategoriaLocalHome
Filme.local = com.gec.ejb.model.FilmeLocalHome
Copia.local = com.gec.ejb.model.CopiaLocalHome
Locacao.local = com.gec.ejb.model.LocacaoLocalHome
Usuario.local = com.gec.ejb.model.UsuarioLocalHome
[/code]

[code]package com.gec.ejb.service;

import com.gec.util.ConstantBundle;

/**

  • Constantes para identificação e localização.
  • Carregado apenas na primeira utilização.
  • @author Anaximandro de Godinho
  • @version $Revision: 1.0 $
  • Be carefully: keys in resource bundle are case sensitive!
    */
    final class ServicesConstants {

// INTERNAL BUNDLE ------------------------------------------------------- //
private static ConstantBundle cb = new ConstantBundle( Constants.class.getName() );

// PUBLIC STATIC CONSTANTS - value defined inside resource bundle. ------- //
public static final String sUSER_ADM = cb.getString( “user.adm” ); //$NON-NLS-1$
public static final String sUSER_PWD = cb.getString( “user.pwd” ); //$NON-NLS-1$

public static final String SLGerenteFilmes_LOCALHOME = cb.getString( “SLGerenteFilmes.local” ); //$NON-NLS-1$
public static final String SLGerenteFilmes_HOME = cb.getString( “SLGerenteFilmes.remote” ); //$NON-NLS-1$
public static final String SLGerenteFilmes_USE = cb.getString( “SLGerenteFilmes.use” ); //$NON-NLS-1$

public static final String SLGerenteLocacoes_LOCALHOME = cb.getString( “SLGerenteLocacoes.local” ); //$NON-NLS-1$
public static final String SLGerenteLocacoes_HOME = cb.getString( “SLGerenteLocacoes.remote” ); //$NON-NLS-1$
public static final String SLGerenteLocacoes_USE = cb.getString( “SLGerenteLocacoes.use” ); //$NON-NLS-1$

public static final String SLGerenteUsuarios_LOCALHOME = cb.getString( “SLGerenteUsuarios.local” ); //$NON-NLS-1$
public static final String SLGerenteUsuarios_HOME = cb.getString( “SLGerenteUsuarios.remote” ); //$NON-NLS-1$
public static final String SLGerenteUsuarios_USE = cb.getString( “SLGerenteUsuarios.use” ); //$NON-NLS-1$

public static final String SLSequence_LOCALHOME = cb.getString( “SLSequence.local” ); //$NON-NLS-1$

public static final String CATEGORIA_LOCALHOME = cb.getString( “Categoria.local” ); //$NON-NLS-1$
public static final String COPIA_LOCALHOME = cb.getString( “Copia.local” ); //$NON-NLS-1$
public static final String FILME_LOCALHOME = cb.getString( “Filme.local” ); //$NON-NLS-1$
public static final String LOCACAO_LOCALHOME = cb.getString( “Locacao.local” ); //$NON-NLS-1$
public static final String USUARIO_LOCALHOME = cb.getString( “Usuario.local” ); //$NON-NLS-1$

// Load resource bundle data, RUN ONLY ONCE and free used resources. ----- //
static {
cb = null;
}

// ONLY can be used as singleton ----------------------------------------- //
private ServicesConstants() {
}
}
[/code]

[code]package com.gec.util;

import java.io.Serializable;

import org.apache.log4j.Logger;

public abstract class LogAbstract implements Serializable {

private static final long serialVersionUID = -1461088044990375670L;

public final Logger _log;

public LogAbstract( final String className ) {
_log = Logger.getLogger( className );
}

/////////////////////////////////////////
// debug < info < warn < error < fatal //
/////////////////////////////////////////

/**

  • Delegar as chamadas do log aqui não será uma boa opção pois perderemos as informações de log (linha, classe, método, etc …) das chamadas originais.
    */
    }
    [/code]

[code]package com.gec.util;

import java.util.MissingResourceException;
import java.util.ResourceBundle;

public final class ConstantBundle extends LogAbstract {

// Constante interna para controle de serialização //
private static final long serialVersionUID = -3804649032724591482L;

// ResourceBundle local //
private final ResourceBundle _rb;

/**
*
* @param className
*/
public ConstantBundle( final String className ) {
super( className );
_log.debug( IM1 + className + IM2 );
_rb = ResourceBundle.getBundle( className );
}

/**
*
*/
protected void finalize() throws Throwable {
_log.debug( IM3 );
super.finalize();
}

/**
*
* @param key
* @return
*/
public String getString( final String key ) {
try {
final String s = _rb.getString( key ).trim();
_log.debug( IE11 + key + IE12 + s + IE13 );
return s;
} catch( final MissingResourceException e ) {
_log.error( IE11 + key + IE14 );
return null;
}
}

/**
*
* @param key
* @return
*/
public int getInt( final String key ) {
try {
final int i = new Integer( _rb.getString( key ).trim() ).intValue();
_log.debug( IE11 + key + IE12 + i + IE13 );
return i;
} catch( final MissingResourceException e ) {
_log.error( IE11 + key + IE14 );
return -1;
}
}

// INTERNAL CONSTANTS //
private static final String IM1 = “# ConstantBundle Loading[”; //$NON-NLS-1$
private static final String IM2 = “] #”; //$NON-NLS-1$
private static final String IM3 = “# ConstantBundle Finalized #”; //$NON-NLS-1$

private static final String IE11 = “”; //$NON-NLS-1$
private static final String IE12 = " = ‘"; //$NON-NLS-1$
private static final String IE13 = "’."; //$NON-NLS-1$
private static final String IE14 = “’ not found.”; //$NON-NLS-1$
}
[/code]

E o problema persiste: como recarregar as constantes definidas na classe ServiceConstants ?

Woody

Você pode usar reflection ou então usar outro objeto que não String para tuas constantes.

ou então faz elas deixarem de ser constantes (ja que é exatamente isto que tu quer, alterar elas, e uma constante não pode ser alterada … )
ai cria um metodo refresh que le novamente todos os valores.

pode protegelas contra escrita usando AOP …

isso resolveria é verdade. Mas eu não quero que elas deixem de ser constantes - deve ter alguma forma de fazer isso - caracas.

Se esse lance do classload não fosse tão arriscado eu mandava ver nele (não conheço ninguém que o tenha usado) - resolveria tudo, bastaria descarregar a classes “constante” e vua-lá (na sua próxima utilização ela seria carregada novamente).

AOP? Tô fora - já vio o peso que essa parada agrega? não conheço ninguém ninguém mesmo usando AOP em produção (só vejo bobeirinhas ou demos pra tentar vender o peixe)

Woody

Primeiro que com classloading você não resolveria o seu problema, pois teria que recarregar TODAS classes do teu sistema.

Segundo, todas maneira que existem são gambiarras porque quer ir contra algo que você mesmo estabeleceu, quer que tuas constantes deixem de ser constantes.

É como querer uma classe tua não possua os métodos wait() de Object, vai contra o sistema de tipos da linguagem.

Em resumo, seja esperto e crie um wrapper para essas Strings e seja feliz. Caso contrario não existe nem garantia que reloading vai funcionar, pois a JVM é livre para propagar os valores de constantes estáticas diretamente para onde bem entender.

[quote=louds]Primeiro que com classloading você não resolveria o seu problema, pois teria que recarregar TODAS classes do teu sistema.

Segundo, todas maneira que existem são gambiarras porque quer ir contra algo que você mesmo estabeleceu, quer que tuas constantes deixem de ser constantes.

É como querer uma classe tua não possua os métodos wait() de Object, vai contra o sistema de tipos da linguagem.

Em resumo, seja esperto e crie um wrapper para essas Strings e seja feliz. Caso contrario não existe nem garantia que reloading vai funcionar, pois a JVM é livre para propagar os valores de constantes estáticas diretamente para onde bem entender. [/quote]

Primeiro, segundo, terceiro? lista de compras?

Desculpe mas vc chegou a ler os dois artigos nos links que postei? Please. Eles falam justamente sobre como resolver esse problema: reescrevendo um classload mais simples.

As constantes continuariam sendo cosntantes (são REFerências tá? não são strings imutáveis) sendo que a “inicialização” destas é que é responsável pela leitura de seus valores do bundle.

Descarregá-las iria facilitar minha vida (se não fosse arriscado), elas não deixariam de ser “constantes”. Na verdade essa solução (descarregar classes) já é necessidade de containers de aplicação (jboss, tomcat)

Não sou esperto louds, gosto de pesquisar e um wrapper (ou vários) não iriam resolver sem ter de alterar muita coisa no meu código legado - sorry.

Woody

[quote=agodinhost][quote=louds]Primeiro que com classloading você não resolveria o seu problema, pois teria que recarregar TODAS classes do teu sistema.

Segundo, todas maneira que existem são gambiarras porque quer ir contra algo que você mesmo estabeleceu, quer que tuas constantes deixem de ser constantes.

É como querer uma classe tua não possua os métodos wait() de Object, vai contra o sistema de tipos da linguagem.

Em resumo, seja esperto e crie um wrapper para essas Strings e seja feliz. Caso contrario não existe nem garantia que reloading vai funcionar, pois a JVM é livre para propagar os valores de constantes estáticas diretamente para onde bem entender. [/quote]

Primeiro, segundo, terceiro? lista de compras?

Desculpe mas vc chegou a ler os dois artigos nos links que postei? Please. Eles falam justamente sobre como resolver esse problema: reescrevendo um classload mais simples.

As constantes continuariam sendo cosntantes (são REFerências tá? não são strings imutáveis) sendo que a “inicialização” destas é que é responsável pela leitura de seus valores do bundle.

Descarregá-las iria facilitar minha vida (se não fosse arriscado), elas não deixariam de ser “constantes”. Na verdade essa solução (descarregar classes) já é necessidade de containers de aplicação (jboss, tomcat)

Não sou esperto louds, gosto de pesquisar e um wrapper (ou vários) não iriam resolver sem ter de alterar muita coisa no meu código legado - sorry.

Woody[/quote]

Eu olhei, por curiosidade, o segundo é muito superficial e não mostra todos problema envolvidos com classloading. O primeiro cai em uma página de erro da acm.

Eu conheço como funciona classloading em Java e te digo, não da para fazer oque você quer dessa forma.

Uma classe só pode ser descarregada se e somente e ela for coletavel, isto é, ela não estar inclusa no fechamento transitivo de todos objetos tangíveis a partir do root set da aplicação. Todo classloader mantem referência para as classes que ele carrega, logo ela só pode ser coletada quando o classloader dela também for.

Dito isso, quando um método que usa tua classe de constantes for executado, o classloader da classe dele vai tentar carregá-la. Então para você poder fazer tua idéia funcionar o classloader do teu AS vai precisar ser alterado, uma ótima idéia.

Depois disso, você vai ter que contornar o fato que depois que o classloader das classes da tua aplicação encontrar essa classe de constantes ele anota isso internamente. Ou seja, para tua classe ser recarregada, você vai precisar fazer o classloader da tua aplicação ser coletado também.

Moral da história, isso não funciona se você precisa recarregar classes que são usadas por outras que não serão recarregadas. Não dá, simples assim.

é, alterar código do servidor de aplicação é loucura - já vi na prática gente fazendo isso e as conseqüencias são desastrosas (uma grande revendedora de petróleo do rio fez isso e até hoje usa jboss 2 por causa dessa alteração, alteração tal desnecessária nas versões mais recentes do jboss - era um lance pra corba na vpn).

poxa, isso deveria ser trivial - várias vezes já me deparei com situações em que descarregar um grafo de classes poderia decidir em parar ou não parar meu server.

concordo até a parte do não dá, pode não valer o risco, mas que dá dá (www.platonos.org).

a página da acm tá abrindo direitinho aqui (precisa cadastro).

gostaria de discutir mais esse tema.

Woody