Eclipse RCP:如何在编织时添加内部类

时间:2016-02-20 07:50:52

标签: osgi eclipse-rcp classloader equinox load-time-weaving

在我的RCP应用程序中,我正在使用WeavingHook服务在加载时修改第三方类。我无法对第三方代码进行任何更改,因此我的编织实现必须按原样使用。

除了一个问题,我有它的工作。对于我制作的其中一个mod,我实例化了一个内部类。它必须是一个内部类,因为它依赖于其中一个超类中定义的内部类。

现在,当第三方代码加载修改后的类时,它的类路径上没有内部类的文件,这只在我实现编织的插件中可用。如果第三方插件声明了好友策略hashcode,我可以向清单添加registered指令,但事实并非如此。

是否有一些编织方法可以使修改后的类能够访问通过二进制编织添加的内部匿名类?

我能想到的唯一解决方案是在二进制编织发生时将内部类文件复制到第三方代码的bin /目录。看起来像是一个黑客,所以我希望有更好的方法。

2 个答案:

答案 0 :(得分:0)

使用getDynamicImports()方法通过WeavingHook编织类时,可以动态添加Import-Package:

wovenClass.getDynamicImports().add("my.package.to.import");

此包显然应该由另一个包导出。

答案 1 :(得分:0)

这是一个有效的解决方案,将内部类的类文件复制到包含编织目标的第三方插件的bin /目录。如果有人知道这样做的方式不那么苛刻,请发一个单独的答案。

编辑:使原始解决方案更加通用,发现正在编织的类的所有内部类文件,并将它们复制到编织目标插件(如果在那里丢失或者尺寸不同)。

public class Activator extends Plugin {

    ...

   public static final String CLASS_FILE_ROOT = "bin/";

/**
 * Binary weaving of a class file may result in the addition or modification of
 * inner class files in this plug-in which need to be placed on the classpath
 * of the plug-in containing the weave target. This method compares inner class
 * files for a given class in this plug-in with inner class files in the plug-in
 * that contains the weave target. Any inner class files missing from the weave
 * target or with a different size than the corresponding file in this plug-in
 * are copied to the appropriate package directory in the weave target plug-in.
 * 
 * @param bundle the bundle of the class being woven
 * @param className the fully qualified name of the class being woven
 * @return true if no errors were encountered in copying the inner class files
 */
private boolean checkInnerClassFileSync(Bundle bundle, String className) {
    className = className.replaceAll("\\.", "/");
    String classDir = CLASS_FILE_ROOT + className.substring(
            0, FilenameUtils.indexOfLastSeparator(className));
    for (String innerClass : getInnerClassNames(classDir, FilenameUtils.getName(className))) {
        try {
            URL srcUrl = getBundle().getEntry(classDir + "/" + innerClass);
            File srcFile = new File(FileLocator.resolve(srcUrl).toURI());
            URL destUrl = bundle.getEntry(classDir);
            File destDir = new File(FileLocator.resolve(destUrl).toURI());
            File destFile = FileUtils.getFile(destDir, innerClass);
            if (srcFile.isFile() && (!destFile.exists() || destFile.length() != srcFile.length())) {
                FileUtils.copyFile(srcFile, destFile);
            }
        } catch (IOException | URISyntaxException e) {
            logger.log(Level.ERROR, "An error occurred while trying to copy an inner class file to the weave target bundle", e);
            return false;
        }
    }
    return true;
}

/**
 * Get the class files representing inner classes defined in a specified class and
 * found in a specified directory.
 * 
 * @param dir a sub-directory containing a class file for <code>className</code>
 * and possibly one or more class files representing inner classes defined in
 * <code>className</code>
 * @param className a class whose class file is found in <code>dir</code>
 * @return class files names representing every inner class defined in
 * <code>className</code> and found in <code>dir</code>.
 */
private String[] getInnerClassNames(String dir, String className) {
    List<String> classNames = new ArrayList<String>();
    try {
        File classDir = new File(FileLocator.resolve(getBundle().getEntry(dir)).toURI());
        for (String fName : classDir.list()) {
            Pattern p = Pattern.compile("^" + className + "\\$.+$");
            Matcher m = p.matcher(fName);
            if (m.matches()) {
                classNames.add(fName);
            }
        }
        return classNames.toArray(new String[0]);
    } catch (URISyntaxException | IOException e) {
        logger.log(Level.ERROR, "An error occured while scanning for inner class files", e);
        return classNames.toArray(new String[0]);
    }
}    

    ....

}