2013-08-12 3 views
3

Я пытаюсь выполнить экстернализацию конфигурации aop.xml, поэтому я удалил aop.xml с META-INF и сделал его доступным на сервере для ручной настройки администраторами sys.AspectJ: проблема с ClassLoading при попытке использовать внешний файл aop.xml

Когда я пытаюсь использовать внешний aop.xml с помощью

-Dorg.aspectj.weaver.loadtime.configuration="file:D:\Workspace\tomcat7\shared\lib\aop.xml" 

я получаю java.lang.RuntimeException: Cannot register non aspect: aspectclass.... в основном потому, что А.Я. casses не загружены AppClassLoader еще в то время. И в следующий раз, когда он попытается зарегистрировать аспекты из WebAppClassLoader (после того, как все классы будут загружены), он отлично работает, но я все равно получаю исключения, записанные с первой попытки зарегистрировать его.

Исключение перехватывается и вошел в ClassLoaderWeavingAdaptor.java линии 307.

, когда следующая строка называется: успеха = registerAspects (Weaver, погрузчик, определение);

исключение поймано и зарегистрировано.

try { 
     registerOptions(weaver, loader, definitions); 
     registerAspectExclude(weaver, loader, definitions); 
     registerAspectInclude(weaver, loader, definitions); 
     success = registerAspects(weaver, loader, definitions); 
     registerIncludeExclude(weaver, loader, definitions); 
     registerDump(weaver, loader, definitions); 
    } catch (Exception ex) { 
     trace.error("register definition failed", ex); 
     success = false; 
     warn("register definition failed", (ex instanceof AbortException) ? null : ex); 
    } 

исключение выбрасывается excactly в следующей строке в BcelWeaver.java

if (type.isAspect()) { 
     ...... 
} else { 
     // FIXME AV - better warning upon no such aspect from aop.xml 
     RuntimeException ex = new RuntimeException("Cannot register non aspect: " + type.getName() + " , " + aspectName); 
     if (trace.isTraceEnabled()) { 
      trace.exit("addLibraryAspect", ex); 
     } 
     throw ex; 
    } 

Как я могу предотвратить ClassLoader от входа ошибку в консоль, когда аспекты еще не загружены. Я думал о комментировании строки, которая регистрирует исключение из исходного файла и перестраивает файл jar-файла aspectjweaver, но искала лучшее решение без изменения источника aspectj.

+0

Пожалуйста, укажите, какие версии вы используете, и предоставить ссылку на 'ClassLoaderWeavingAdaptor.java' или рассмотреть возможность включения кода в вашем вопросе –

+0

добавил фрагмент кода, который бросает исключение. – Sammy

+0

(не слишком знакомы с AspectJ). Я не понимаю, какая разница в том, где находится файл конфигурации (в разделе «META-INF» или внешнее местоположение). Как это может изменить найденные классы или нет? Или вы получаете это сообщение, когда вы также не используете системное свойство? –

ответ

5

Я не уверен, что есть простой выход из вашей проблемы. Как я уже сказал, я раньше не работал с AspectJ, но считаю, что это неправильное поведение ткача.

Описания проблемы: Во время загрузки агент пытается применить плетение других не только WebAppClassLoader, но и всю цепочку загрузчика классов (один раз в загрузчик классов), т.е. на: sun.misc.Launcher$AppClassLoader, sun.misc.Launcher$ExtClassLoader, org.apache.catalina.loader.StandardClassLoader (загрузчик классов кота в). Когда вы используете подход META-INF/aop.xml, он отключает плетение для вышеперечисленных загрузчиков классов, потому что «файл конфигурации недоступен» (если вы активируете режим verbose, вы можете видеть эти сообщения на консоли). Когда вы используете подход к конфигурации файлов, конфигурация доступна для всех загрузчиков классов в цепочке. Поскольку он находит файл конфигурации, агент анализирует определения, он не находит класс аспектов и показывает ошибку.

Странная вещь состоит в том, как это описано в configuration documentation, если вы используете WeavingURLClassLoader подход к времени загрузки ткачество, «... это также позволяет пользователю явно ограничить с помощью загрузчика классов, какие классы могут быть тканой». Таким образом, на самом деле это функция (!), Которую может иметь подход classloader, но агентский подход не делает этого. (К сожалению, я не был в состоянии использовать этот подход)

Хороший (и плохие) новости: Хорошая новость заключается в том, что вы можете легко создать свой собственный агент, который будет игнорировать плетение для вышеупомянутых загрузчиков классов.Плохая новость заключается в том, что ограничение переплетения для каждого загрузчика классов недостаточно, потому что если у вас есть другие приложения на одном сервере, Tomcat все равно будет использовать WebAppClassLoader для их загрузки, чтобы вы все равно получали сообщения об ошибках для этих приложений. (Возможно, вы можете расширить классы ниже, чтобы фильтровать пакеты/классы, в этом случае).

Ниже вы можете найти два класса для модифицированного агента. Для того, чтобы использовать их, то Вам необходимо будет сделать следующее:

  • Ун встряхивайте aspectjweaver.jar в папку
  • Под org/aspectj/weaver/loadtime создать новую папку filter совпадать с именем пакета и поставить там два новых класса после компиляции их.
  • Редактировать файл META-INF/MANIFEST.MF и измените строку

    Premain-Class: org.aspectj.weaver.loadtime.Agent в
    Premain-Class: org.aspectj.weaver.loadtime.filter.FilterAgent

  • Re-банку и у вас есть новый агент готов.

  • При запуске JVM вы можете передать новое системное свойство с разделенным запятой списком загрузчиков классов, которые вы хотели бы игнорировать, например, -Dorg.aspectj.weaver.loadtime.filter=sun.misc.Launcher$AppClassLoader,sun.misc.Launcher$ExtClassLoader,org.apache.catalina.loader.StandardClassLoader (для этого я установил CATALINA_OPTS).

Классы модифицированную копию исходного агента классов Agent и ClassPreProcessorAgentAdapter. Единственный код, который я добавил это та часть, которая разбирает выше свойством системы, если она существует, и игнорировать вызовы для загрузчиков классов мы не заинтересованы в.

Используйте на свой страх и риск :) Я надеюсь, что помогает

package org.aspectj.weaver.loadtime.filter; 

import java.lang.instrument.ClassFileTransformer; 
import java.lang.instrument.Instrumentation; 

public class FilterAgent { 

    private static Instrumentation s_instrumentation; 

    // Use our own version of ClassFileTransformer that would filter out selected classloaders 
    private static ClassFileTransformer s_transformer = new ClassPreprocessorFilteredAdapter(); 

    /** 
    * JSR-163 preMain Agent entry method 
    * 
    * @param options 
    * @param instrumentation 
    */ 
    public static void premain(String options, Instrumentation instrumentation) { 
     /* Handle duplicate agents */ 
     if (s_instrumentation != null) { 
      return; 
     } 
     s_instrumentation = instrumentation; 
     s_instrumentation.addTransformer(s_transformer); 
    } 

    public static Instrumentation getInstrumentation() { 
     if (s_instrumentation == null) { 
      throw new UnsupportedOperationException("Java 5 was not started with preMain -javaagent for AspectJ"); 
     } 
     return s_instrumentation; 
    } 
} 
//----------------------------------------------------------------------------------- 
package org.aspectj.weaver.loadtime.filter; 

import java.lang.instrument.ClassFileTransformer; 
import java.lang.instrument.IllegalClassFormatException; 
import java.security.ProtectionDomain; 
import java.util.HashMap; 
import java.util.Map; 

import org.aspectj.weaver.loadtime.Aj; 
import org.aspectj.weaver.loadtime.ClassPreProcessor; 


public class ClassPreprocessorFilteredAdapter implements ClassFileTransformer { 

    /** 
    * Concrete preprocessor. 
    */ 
    private static ClassPreProcessor s_preProcessor; 

    private static Map<String, String> ignoredClassloaderNames = new HashMap<String, String>(); 

    static { 
     try { 
      s_preProcessor = new Aj(); 
      s_preProcessor.initialize(); 


      String ignoredLoaders = System.getProperty("org.aspectj.weaver.loadtime.filter", ""); 
      if (ignoredLoaders.length() > 0) { 
       String[] loaders = ignoredLoaders.split(","); 

       for (String s : loaders) { 
        s = s.trim(); 
        ignoredClassloaderNames.put(s, s); 
        System.out.println("---> Will filtered out classloader: " + s); 
       } 
      } 

     } catch (Exception e) { 
      throw new ExceptionInInitializerError("could not initialize JSR163 preprocessor due to: " + e.toString()); 
     } 
    } 

    /** 
    * Invokes the weaver to modify some set of input bytes. 
    * 
    * @param loader the defining class loader 
    * @param className the name of class being loaded 
    * @param classBeingRedefined is set when hotswap is being attempted 
    * @param protectionDomain the protection domain for the class being loaded 
    * @param bytes the incoming bytes (before weaving) 
    * @return the woven bytes 
    */ 
    @Override 
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] bytes) throws IllegalClassFormatException { 
     if (classBeingRedefined != null) { 
      System.err.println("INFO: (Enh120375): AspectJ attempting reweave of '" + className + "'"); 
     } 

     String loaderName = loader.getClass().getName(); 
     if (shouldIgnoreClassLoader(loaderName)) { 
      return bytes; 
     } 
     return s_preProcessor.preProcess(className, bytes, loader, protectionDomain); 
    } 

    private boolean shouldIgnoreClassLoader(String loaderName) { 
     boolean result = false; 
     String ignoredLoader = ignoredClassloaderNames.get(loaderName); 
     if (ignoredLoader != null) { 
      result = true; // if the loader name exists in the map we will ignore weaving 
     } 
     return result; 
    } 
} 
+0

Вау, спасибо за детальную рекоммендацию. так как эти исключения только попадают и регистрируются и не влияют на поток приложения, что делать, если я просто закомментирую строку кода, которая регистрирует ошибку и перестраивает аниматор aspectj? Я искал решение, которое было бы переносимым на нескольких серверах приложений, а не привязывать его к tomcat или отдельному серверу приложений. – Sammy

+0

@Sammy это не привязано к определенному серверу. Вы просто передадите другое свойство, то есть измените загрузчики классов, чтобы игнорировать вместо использования «StandardClassLoader», вы добавили бы любой загрузчик классов, используемый другим сервером (или вы могли бы добавить оба). Вы можете, конечно, прокомментировать сообщение, но это также скроет вам значимые ошибки, например. если некоторые из ваших классов аспектов действительно отсутствуют при ткачестве 'WebAppClassLoader'. Я считаю, что эта функция будет добавлена ​​в AspectJ в какой-то момент, поэтому такой хак не понадобится. Ошибка, как сейчас, не имеет смысла –

+0

@ Sammy также во время расследования этой проблемы возникла, что Spring использует нечто подобное. Возможно, вы сможете использовать это, если используете Spring –

3

Если вам нужна функция, позволяющая исключить загрузчики классов от плетения с помощью агента, существует developer build, предоставляющая новый переключатель командной строки -Daj.weaving.loadersToSkip. Тема обсуждается на thread of the AspectJ users mailing list. Функция, вероятно, превратится в AspectJ 1.7.4, но пока недоступна в 1.7.3.

Update:

feature делали это в AspectJ 1.7.4, даже если она не упоминается в release notes, но перечисленные в resolved issues для данного выпуска.

0

Что я в итоге сделал, это изменение уровня LOG для сообщения об ошибке от ERROR до DEBUG, поскольку я не вижу в этом ERROR (по крайней мере, в моем случае). в этом случае я все еще вижу ошибку при включении уровня DEBUG. поэтому я изменил исходный файл ниже и восстановить мой aspectjweaver-1.7.1.jar

try { 
    registerOptions(weaver, loader, definitions); 
    registerAspectExclude(weaver, loader, definitions); 
    registerAspectInclude(weaver, loader, definitions); 
    success = registerAspects(weaver, loader, definitions); 
    registerIncludeExclude(weaver, loader, definitions); 
    registerDump(weaver, loader, definitions); 
} catch (Exception ex) { 
    //(CHANGE 1) trace.error("register definition failed", ex); 
    trace.debug("register definition failed" + ex.getMessage()); 
    success = false; 
    // (CHANGE 2) warn("register definition failed", (ex instanceof AbortException) ? null : ex); 
    debug("register definition failed" + ((ex instanceof AbortException) ? null : ex)); 
} 
Смежные вопросы