2017-07-10 26 views
2

我试图提供一个通用javax.cache兼容适配器类javax.cache.configuration.FactoryBuilder检索工厂,然后由ignite实例化缓存。所描述的问题可能会使用Apache Ignite,但是,我相信它不一定与Ignite相关,但更多的是泛型和闭包在Java中的工作方式。工厂从泛型高速缓存适配器类指针使用FactoryBuilder

Ignite CacheStoreAdapter接口继承自javax.cache.CacheLoaderjavax.cache.CacheWriter,我提供了一个适配器实现。该实现需要缓存键和值的两个(通用)类型以及值类引用,以便能够实例化适配器中的值。请参阅下面的部分课程MyCacheAdapter

public class MyCacheAdapter<K,V extends StorableModel> extends CacheStoreAdapter<K,V> implements LifecycleAware { 
    private final Class<V> valueClazz; 
    public MyCacheAdapter(Class<V> valueClass) { 
     this.valueClazz = valueClass; 
    } 
    @Override 
    public V load(K key) throws CacheLoaderException { 
     // load from database 
     return valueClazz.newInstance(); // dummy instantiation 
    } 
    @Override 
    public void write(Cache.Entry<? extends K, ? extends V> entry) throws CacheWriterException { 
     // write to database 
    } 
} 

现在,当我明确声明的适配器,并将其提供给FactoryBuilder一切正常......

public class MyPersonAdapter extends MyCacheAdapter<String,Person> { 
    public MyPersonAdapter() { 
     super(Person.class); 
    } 
} 

...当缓存我的服务启动被实例化。

// run init cache (on 1st node only) 
public <K,V extends StorableModel> void init() { 
    CacheConfiguration cacheConfiguration = new CacheConfiguration<K,V>(); 
    cacheConfiguration.setCacheStoreFactory(FactoryBuilder.factoryOf(MyPersonAdapter.class)); 
    // add node filter to prevent other nodes from failing on cache distribution 
    cacheConfiguration.setNodeFilter((node) -> { // exclude 2nd node }); 
    ignite.getOrCreateCache(cacheConfiguration); 
} 

到目前为止的工作实例!现在我不想明确声明MyPersonAdapter(还有几十个),而是让我的初学者根据提供的类型和键/值类来处理适配器细节。所以,我可以提供我自己的工厂......

public static class AdapterFactory<K,V extends StorableModel> implements Factory<CacheStore<? super K, ? super V>> { 
    private final Class<V> valueClass; 
    public AdapterFactory(Class<V> valueClass) { 
     this.valueClass = valueClass; 
    } 
    @Override public CacheStore<? super K, ? super V> create() { 
     return new MyCacheAdapter<K,V>(valueClass); 
    } 
} 

...,然后在高速缓存初始化像这样使用:

// run init cache (on 1st node only) 
public <K,V extends StorableModel> void init(Class<V> valueClass) { 
    CacheConfiguration cacheConfiguration = new CacheConfiguration<K,V>(); 
    cacheConfiguration.setCacheStoreFactory(new AdapterFactory<K,V>(valueClass)); 
    // add node filter to prevent other nodes from failing on cache distribution 
    cacheConfiguration.setNodeFilter((node) -> { // exclude 2nd node }); 
    ignite.getOrCreateCache(cacheConfiguration); 
} 

但是,这将引发我java.lang.ClassNotFoundException对我的第二个值类因为该类不在第二个节点的类路径中,所以点燃节点。我绝对不想提供那个类,我在问自己,第一个实现有什么不同。我知道工厂在需要的时候确实创建了一个实例(当缓存分发到另一个节点时),它必须知道它没有的值类。所以我尝试了另一个实现来接近第一个(工作)的实现。相反,提供我自己的工厂,我想我有明确的适配器初始化嵌套类前右声明:

public <K,V extends StorableModel> void init(Class<V> valueClass) { 
    class DynamicAdapter extends MyCacheAdapter<K,V> { 
     public DynamicAdapter() { 
      super(valueClass); 
     } 
    } 
    CacheConfiguration cacheConfiguration = new CacheConfiguration<K,V>(); 
    cacheConfiguration.setCacheStoreFactory(FactoryBuilder.factoryOf(DynamicAdapter.class)); 
    // add node filter to prevent other nodes from failing on cache distribution 
    cacheConfiguration.setNodeFilter((node) -> { // exclude 2nd node }); 
    ignite.getOrCreateCache(cacheConfiguration); 
} 

这再次引发了我的InstationException因为类是超出范围的工厂,我猜。

java.lang.RuntimeException: Failed to create an instance of DynamicAdapter 
    Caused by: java.lang.InstantiationException: DynamicAdapter 
    Caused by: java.lang.NoSuchMethodException: DynamicAdapter.<init>() 

所以,我想知道如果有一种方法来实现我的目标没有一个自定义的工厂和类分布跨越服务(这是没办法),但仍然有一些动态适配器声明。

UPDATE

堆栈跟踪的InstantiationException上课时由静态方法返回

private static <X,Y extends StorableModel> Class getAdapterClass(Class<Y> valueClass) { 
    class MyClass extends MongoIgniteCacheAdapter<X,Y> { 
     MyClass() { 
      super(valueClass); 
     } 
    } 
    return MyClass.class; 
} 

// run init cache (on 1st node only) 
public <K,V extends StorableModel> void init() { 
    CacheConfiguration cacheConfiguration = new CacheConfiguration<K,V>(); 
    cacheConfiguration.setCacheStoreFactory(FactoryBuilder.factoryOf(getAdapterClass(valueClass))); 
    // add node filter to prevent other nodes from failing on cache distribution 
    cacheConfiguration.setNodeFilter((node) -> { // exclude 2nd node }); 
    ignite.getOrCreateCache(cacheConfiguration); 
} 

// output 

class org.apache.ignite.IgniteCheckedException: Failed to create an instance of IgniteServiceStarter$1MyClass 
at org.apache.ignite.internal.util.IgniteUtils.cast(IgniteUtils.java:7242) 
at org.apache.ignite.internal.util.future.GridFutureAdapter.resolve(GridFutureAdapter.java:258) 
at org.apache.ignite.internal.util.future.GridFutureAdapter.get0(GridFutureAdapter.java:206) 
at org.apache.ignite.internal.util.future.GridFutureAdapter.get(GridFutureAdapter.java:158) 
at org.apache.ignite.internal.processors.cache.GridCachePartitionExchangeManager$ExchangeWorker.body(GridCachePartitionExchangeManager.java:1812) 
at org.apache.ignite.internal.util.worker.GridWorker.run(GridWorker.java:110) 
at java.lang.Thread.run(Thread.java:745) 
Caused by: java.lang.RuntimeException: Failed to create an instance of IgniteServiceStarter$1MyClass 
at javax.cache.configuration.FactoryBuilder$ClassFactory.create(FactoryBuilder.java:134) 
at org.apache.ignite.internal.processors.cache.GridCacheProcessor.createCache(GridCacheProcessor.java:1458) 
at org.apache.ignite.internal.processors.cache.GridCacheProcessor.prepareCacheStart(GridCacheProcessor.java:1931) 
at org.apache.ignite.internal.processors.cache.GridCacheProcessor.prepareCacheStart(GridCacheProcessor.java:1833) 
at org.apache.ignite.internal.processors.cache.CacheAffinitySharedManager.onCacheChangeRequest(CacheAffinitySharedManager.java:379) 
at org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsExchangeFuture.onCacheChangeRequest(GridDhtPartitionsExchangeFuture.java:688) 
at org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsExchangeFuture.init(GridDhtPartitionsExchangeFuture.java:529) 
at org.apache.ignite.internal.processors.cache.GridCachePartitionExchangeManager$ExchangeWorker.body(GridCachePartitionExchangeManager.java:1806) 
... 2 more 
Caused by: java.lang.InstantiationException: IgniteServiceStarter$1MyClass 
at java.lang.Class.newInstance(Class.java:427) 
at javax.cache.configuration.FactoryBuilder$ClassFactory.create(FactoryBuilder.java:132) 
... 9 more 
Caused by: java.lang.NoSuchMethodException: IgniteServiceStarter$1MyClass.<init>() 
at java.lang.Class.getConstructor0(Class.java:3082) 
at java.lang.Class.newInstance(Class.java:412) 
... 10 more 
+0

您应该发布'ClassNotFoundException'的堆栈跟踪。 – Radiodef

回答

0

这可能是部分答案,但我认为第二个例子(与本地类)失败的原因本地班级是一个内部班级。内部类隐式地将外部类的实例作为构造函数的第0个参数,因此它们没有无参数构造函数。

class NewInstanceExample { 
    public static void main(String[] args) throws Exception { 
     try { 
      staticMethod(); 
      new NewInstanceExample().instanceMethod(); 
     } catch (Exception x) { 
      System.out.println("4. " + x); 
      System.out.println(" caused by " + x.getCause()); 
     } 
    } 
    static void staticMethod() throws Exception { 
     class StaticInner {} 
     System.out.println("1. " + StaticInner.class.newInstance()); 
    } 
    void instanceMethod() throws Exception { 
     class InstanceInner {} 
     System.out.println("2. " + java.util.Arrays.toString(InstanceInner.class.getDeclaredConstructors())); 
     System.out.println("3. " + InstanceInner.class.newInstance()); 
    } 
} 

的输出如下:

1. [email protected] 
2. [mcve.NewInstanceExample$1InstanceInner(mcve.NewInstanceExample)] 
4. java.lang.InstantiationException: mcve.NewInstanceExample$1InstanceInner 
    caused by java.lang.NoSuchMethodException: mcve.NewInstanceExample$1InstanceInner.<init>()

注意的粗线,其示出了对于InstanceInner构造函数接受的NewInstanceExample一个实例,因此,为什么Class.newInstance()抛出InstantiationException

InstantiationException - 如果此Class表示抽象类,接口,数组类,基元类型或void;或如果该类没有空的构造函数;或者由于其他原因实例化失败。

的解决办法是,以确保嵌套类没有外围实例,或者通过使用static修改(如在你的例子其中工程)或以其他方式声明类的static背景下(如在我的例子与方法static)。

我不认为我可以在没有更多信息的情况下对ClassNotFoundException进行大量猜测。

+0

有趣的一点,我现在用静态方法试了一下。不幸的是,我不得不返回这个类本身,而不是将它传递给工厂。 – Ben

+0

请参阅我在“InstantiationException”的完整堆栈跟踪的初始文章中的更新。请注意,这是安静点燃具体。 – Ben

+0

那么,你可以在完整的堆栈跟踪中看到它仍然是一个问题。 'Class.newInstance'找不到0参数的构造函数。所有其他的东西只是Ignite重新抛出该异常。你可以尝试使构造函数为'public',但在测试了一些东西之后,似乎本地类在这里就不起作用了。一旦你将构造函数改为'public',你将会得到一个'IllegalAccessException',因为这个类本身并不是公有的,在写这个答案的时候我昨天没有发现它。 – Radiodef

0

不知道为什么你的第一个例子工作,它实际上也失败了。

但是无论如何,整个方法都没有意义。如果你没有部署类,你将无法使用这些类的实例,最终你的商店将失败。例如,load方法将在newInstance()调用失败。

为避免部署类,您需要在商店中使用BinaryObject s,并将CacheConfiguration#keepBinaryInStore标志设置为true,以便Ignite在调用CacheStore时不会序列化/反序列化对象。有关更多详细信息,请参阅此页面:https://apacheignite.readme.io/docs/binary-marshaller#binaryobject-and-cachestore

+0

关键是缓存仅在特定节点(由缓存节点过滤器选择)上使用/实例化(cacheConfiguration.setNodeFilter((node) - > {...});'这就是为什么我的第一个例子工作我猜。我知道这个问题没有提供。我不希望将服务内部类分发到集群的所有节点,因为这会破坏CI/CD为首要任务的面向服务方法的目的。 – Ben

+0

我也试过'keepBinaryOnStore(true)',但它没有任何区别。不支持指定缓存的节点仍然在classpath中请求该类。 – Ben

+0

'keepBinaryInStore'当然不会改变任何东西。您还需要更改商店的实现,以便它可以与“BinaryObject”API而不是您的类一起使用。一旦从存储和存储工厂实现中删除对类的所有引用,就可以解决问题。 –