Detectando invocações do método System.setProperty

Estou enfrentando um enigma aqui.

Um dos aplicativos que desenvolvi está carregando uma implementação incorreta da class DocumentBuilderFactory do JAXP. Esse comportamento foi posteriormente deduzido como resultado de outra class em diferentes aplicativos criados por uma equipe / empresa diferente. A referida class alterou a class DocumentBuilderFactory preferida ao carregar, pela inclusão de um bloco estático semelhante ao seguinte:

static { System.setProperty("javax.xml.parsers.DocumentBuilderFactory", "a new factory"); } 

Se alguém usar os Javadocs do método DocumentBuilderFactory.newInstance , seria bastante óbvio que o código acima era responsável por alterar a implementação do analisador retornada para todos os aplicativos, quando o método newInstance é invocado.

Um patch foi aplicado, o que corrigiu esse problema, mas isso me levou a fazer essa pergunta – como alguém determina qual class está executando a chamada System.setProperty em tempo de execução?

Produzimos uma compilation personalizada do OpenJDK com uma class System modificada que foi responsável por acionar o culpado, pela simples razão de que não tínhamos access a todas as fonts de todos os aplicativos implantados no servidor. Mas isso só foi possível devido ao fato de que o ambiente de produção foi replicado em sua totalidade. A pergunta, portanto, também pode ser interpretada como – como alguém determina qual class está executando a chamada System.setProperty no tempo de execução, em um ambiente de produção?

System.setProperty é verificado por um SecurityManager , se instalado.

Você pode criar seu próprio MySecurityManager e implantar em tempo de execução. Seu próprio SecurityManager pode registrar algumas informações, como o checkPropertyAccess atual, quando o método checkPropertyAccess é chamado:

 public class MySecurityManager extends SecurityManager { public MySecurityManager() { super(); } @Override public void checkPropertyAccess(String key) { if ("javax.xml.parsers.DocumentBuilderFactory".equals(key)) { System.err.println("checkPropertyAccess(String :" + key + "): "); Thread.currentThread().dumpStack(); // or anything useful for // logging the context. new Throwable().printStackTrace(); // whatever, or use it with // PrintStream/PrintWriter, or some logging framework if configured. } super.checkPropertyAccess(key); } @Override public void checkPermission(Permission perm) { if (perm instanceof PropertyPermission) { PropertyPermission propPerm = (PropertyPermission) perm; System.err.println("checkPropertyAccess(String:" + propPerm.getName() + "):"); Thread.currentThread().dumpStack(); // or anything useful for // logging the context. new Throwable().printStackTrace(); // whatever, or use it with // PrintStream/PrintWriter, or some logging framework if configured. } super.checkPermission(perm); } } 

Os literais de string são codificados em UTF8 no formato de arquivo de class e como não há razão para supor que o invocador incorreto esteja usando código para concatenar o nome da propriedade, eu teria simplesmente descompactado todos os JARs do caminho de class em um diretório e tentado um grep recursivo para “javax.xml.parsers.DocumentBuilderFactory” através dos arquivos de class. Você teria pelo menos encontrado todos os arquivos de class contendo essa String como um literal e esperamos que não para muitos falsos positivos.

Se o nome da class da implementação incorreta for mais fácil de identificar, talvez fosse mais inteligente procurar por isso.

Basta iniciar seu aplicativo no modo de debugging, conectar-se a ele com o eclipse, definir o ponto de interrupção e procurar a class na hierarquia de chamadas. http://www.eclipsezone.com/eclipse/forums/t53459.html

Eu conheço 2 soluções

  1. usar estrutura de aspectos (aspectJ ou so)
  2. Modifique a class System (adicione logging ao método setProperty ()) new Throwable().printStacktrace(); irá denunciá-lo de onde o método foi chamado.

Agora execute seu aplicativo com a opção -Xbootclasspath/p e coloque sua class System da versão lá.

-Xbootclasspath/p: prepend na frente do caminho da class de bootstrap

Eu prefiro a segunda solução porque é muito simples.

Parece algum caso de uso para estruturas de aspecto , não?