これは、なにをしたくて書いたもの?
前に、WildFlyのHTTPセッションの保存先をInfinispan Serverにするエントリーを書きました。
WildFlyのHTTPセッションの保存先をInfinispan Serverに変更する - CLOVER🍀
この時、Marshallerについては割と軽く流してしまったのですが、よくよく見ると思っていたのとけっこう違ったので、ちゃんと
見ていくことにしました。
WildFlyのInfinispanサブシステムのMarshaller定義
とりあえず、現在のWildFlyの最新バージョン(26.1.1.Final)のInfinispanサブシステムの定義を見てみましょう。
$ curl -OL https://github.com/wildfly/wildfly/releases/download/26.1.1.Final/wildfly-26.1.1.Final.zip $ unzip wildfly-26.1.1.Final.zip $ cd wildfly-26.1.1.Final
standalone/configuration/standalone.xmlの該当箇所を見てみます。
<subsystem xmlns="urn:jboss:domain:infinispan:13.0"> <cache-container name="ejb" default-cache="passivation" marshaller="PROTOSTREAM" aliases="sfsb" modules="org.wildfly.clustering.ejb.infinispan"> <local-cache name="passivation"> <expiration interval="0"/> <file-store passivation="true" purge="false"/> </local-cache> </cache-container> <cache-container name="web" default-cache="passivation" marshaller="PROTOSTREAM" modules="org.wildfly.clustering.web.infinispan"> <local-cache name="passivation"> <expiration interval="0"/> <file-store passivation="true" purge="false"/> </local-cache> <local-cache name="sso"> <expiration interval="0"/> </local-cache> </cache-container> <cache-container name="server" default-cache="default" marshaller="PROTOSTREAM" modules="org.wildfly.clustering.server"> <local-cache name="default"> <expiration interval="0"/> </local-cache> </cache-container> <cache-container name="hibernate" marshaller="JBOSS" modules="org.infinispan.hibernate-cache"> <local-cache name="entity"> <heap-memory size="10000"/> <expiration max-idle="100000"/> </local-cache> <local-cache name="local-query"> <heap-memory size="10000"/> <expiration max-idle="100000"/> </local-cache> <local-cache name="timestamps"> <expiration interval="0"/> </local-cache> <local-cache name="pending-puts"> <expiration max-idle="60000"/> </local-cache> </cache-container> </subsystem>
standalone/configuration/standalone-ha.xmlの場合。
<subsystem xmlns="urn:jboss:domain:infinispan:13.0"> <cache-container name="ejb" default-cache="dist" marshaller="PROTOSTREAM" aliases="sfsb" modules="org.wildfly.clustering.ejb.infinispan"> <transport lock-timeout="60000"/> <distributed-cache name="dist"> <locking isolation="REPEATABLE_READ"/> <transaction mode="BATCH"/> <expiration interval="0"/> <file-store/> </distributed-cache> </cache-container> <cache-container name="server" default-cache="default" marshaller="PROTOSTREAM" aliases="singleton cluster" modules="org.wildfly.clustering.server"> <transport lock-timeout="60000"/> <replicated-cache name="default"> <transaction mode="BATCH"/> <expiration interval="0"/> </replicated-cache> </cache-container> <cache-container name="web" default-cache="dist" marshaller="PROTOSTREAM" modules="org.wildfly.clustering.web.infinispan"> <transport lock-timeout="60000"/> <replicated-cache name="sso"> <locking isolation="REPEATABLE_READ"/> <transaction mode="BATCH"/> <expiration interval="0"/> </replicated-cache> <replicated-cache name="routing"> <expiration interval="0"/> </replicated-cache> <distributed-cache name="dist"> <locking isolation="REPEATABLE_READ"/> <transaction mode="BATCH"/> <expiration interval="0"/> <file-store/> </distributed-cache> </cache-container> <cache-container name="hibernate" marshaller="JBOSS" modules="org.infinispan.hibernate-cache"> <transport lock-timeout="60000"/> <local-cache name="local-query"> <heap-memory size="10000"/> <expiration max-idle="100000"/> </local-cache> <local-cache name="pending-puts"> <expiration max-idle="60000"/> </local-cache> <invalidation-cache name="entity"> <heap-memory size="10000"/> <expiration max-idle="100000"/> </invalidation-cache> <replicated-cache name="timestamps"> <expiration interval="0"/> </replicated-cache> </cache-container> </subsystem>
standalone/configuration/standalone-microprofile.xmlなどもありますが、これらから数が減ったりするだけなので、割愛。
Marshallerの定義は、marshaller属性の部分ですね。
<cache-container name="web" default-cache="dist" marshaller="PROTOSTREAM" modules="org.wildfly.clustering.web.infinispan">
Cache自体はLocal CacheなのかDistributed CacheなのかReplicated Cacheなのか、Cacheの定義に差はありますが、各Cache Containerの
Marshallerの定義にフォーカスして見るとこんな感じですね。
ejb…PROTOSTREAMweb…PROTOSTREAMserver…PROTOSTREAMhibernate…JBOSS
WildFly Model Referenceのcache-containerおよびremote-cache-containerの定義を見ると、marshaller属性にはLEGACY、
JBOSS、PROTOSTREAMのいずれかを指定できます。なにも指定していない場合はLEGACYを選択したことになります。
- WildFly Full 26.1 Model Reference / subsystem=infinispan / cache-container / Attributes / marshaller
- WildFly Full 26.1 Model Reference / subsystem=infinispan / remote-cache-container / Attributes / marshaller
それぞれの値の説明は、WildFlyのドキュメントのどこにも書かれていないのですが…。
いつからProtoStreamが入ったんでしょうね?少なくとも、WildFly 24.0.0.Finalにはこの設定はあったようです。
ProtoStreamが入ったタイミング自体は、どうやらWildFly 21.0.0.Finalのようです。
[WFLY-13426] Optimize marshalling in clustering subsystems using ProtoStream - Red Hat Issue Tracker
Marshallerの設定が可能になったのが、もう少し後、という感じですね。
InfinispanのMarshaller
ここで挙がっているMarshallerというのは、Infinispanにデータを格納する際にJavaのオブジェクトをどのようにマーシャリングするのか
という話になります。
というわけで、Infinispanのドキュメントを見てみます。
InfinispanでJavaのオブジェクトをマーシャリングする際には、以下の3つから選ぶことができます。
Infinispanの標準のMarshallerは、ProtoStreamです。ProtoStreamのマーシャリングの方法としては、Protocol Buffersになります。
また、この中でもJBoss Marshallingは非推奨扱いになっています。
JBoss Marshalling is deprecated. You should use it only as a temporary measure while migrating your applications from an older version of Infinispan.
となると、LEGACYはJava Serializationのことなのかな?と短絡的に思ったりもしたのですが、そうではないようです。
WildFlyでのInfinispanサブシステムのMarshallerの実体
話をWildFlyのInfinispanサブシステムに戻して。
Embedded、Hot RodそれぞれのMarshallerの定義(LEGACY、JBOSS、PROTOSTREAM)に応じてどのようなMarshallerが
選択されるのかを見てみます。
Embedded。
Hot Rod。
両者で微妙に定義が異なりますが、PROTOSTREAMはProtoStream、JBOSSはJBoss Marshalling、そしてLEGACYは
条件によってPROTOSTREAMかJBOSSを選択する、ということになっています。
ここで使われているMarshallerFactory。
そして、各Marshaller。
- ProtoStream
- clustering/infinispan/marshalling/src/main/java/org/wildfly/clustering/infinispan/marshalling/protostream/ProtoStreamMarshaller.java
- clustering/infinispan/spi/src/main/java/org/wildfly/clustering/infinispan/spi/marshalling/InfinispanProtoStreamMarshaller.java
- Embeddedのみ(
ProtoStreamMarshallerを継承したもの)
- Embeddedのみ(
- 実体
- JBoss Marshalling
WildFlyでProtoStreamを使う?
ProtoStreamといえば、Protocol Buffersのスキーマ定義とMarshallerを書く、もしくはアノテーションからスキーマ定義を自動生成して使う
ことになるわけですが。なので、前のエントリーを書いた時に、PROTOSTREAMを選ぶとスキーマ定義やMarshallerを書かないと
いけなくなると思ってしまいました。
ですが、デフォルトの設定ファイルがPROTOSTREAMとなっているということは、特に意識しない場合はProtoStreamでマーシャリングを
行っていることになります。どうなっているんでしょう?
こちらに、WildFlyの持つProtoStreamのモジュールがありそうです。
https://github.com/wildfly/wildfly/tree/26.1.1.Final/clustering/marshalling/protostream
このモジュール内を見ると、その量にけっこう驚いたりするのですが。
Javaの標準ライブラリの一部をカバーするMarshallerが書かれています。以下は、その一部です。
以下が含まれていそうですね。
java.langjava.mathjava.netjava.sqljava.utiljava.util.concurrentjava.util.concurrent.atomicjava.time
Collectionに対するMarshaller。
public class CollectionMarshaller<T extends Collection<Object>> extends AbstractCollectionMarshaller<T> {
各パッケージ内にMarshallerProviderというenumがあり、こちらでMarshallerをまとめて取り扱っているようです。
public enum UtilMarshallerProvider implements ProtoStreamMarshallerProvider { ARRAY_DEQUE(new CollectionMarshaller<>(ArrayDeque::new)), ARRAY_LIST(new CollectionMarshaller<>(ArrayList::new)), BIT_SET(new FunctionalScalarMarshaller<>(Scalar.BYTE_ARRAY.cast(byte[].class), BitSet::new, BitSet::isEmpty, BitSet::toByteArray, BitSet::valueOf)), CALENDAR(new CalendarMarshaller()), CURRENCY(new FunctionalScalarMarshaller<>(Currency.class, Scalar.STRING.cast(String.class), Functions.constantSupplier(getDefaultCurrency()), Currency::getCurrencyCode, Currency::getInstance)), DATE(new FunctionalMarshaller<>(Date.class, Instant.class, Date::toInstant, Date::from)), 〜省略〜 SYNCHRONIZED_SET(new SynchronizedDecoratorMarshaller<>(Set.class, Collections::synchronizedSet, Collections.emptySet())), SYNCHRONIZED_SORTED_MAP(new SynchronizedDecoratorMarshaller<>(SortedMap.class, Collections::synchronizedSortedMap, Collections.emptySortedMap())), SYNCHRONIZED_SORTED_SET(new SynchronizedDecoratorMarshaller<>(SortedSet.class, Collections::synchronizedSortedSet, Collections.emptySortedSet())), TIME_ZONE(new FunctionalScalarMarshaller<>(TimeZone.class, Scalar.STRING.cast(String.class), Functions.constantSupplier(TimeZone.getDefault()), TimeZone::getID, TimeZone::getTimeZone)), TREE_MAP(new SortedMapMarshaller<>(TreeMap::new)), TREE_SET(new SortedSetMarshaller<>(TreeSet::new)), UNMODIFIABLE_COLLECTION(new DecoratorMarshaller<>(Collection.class, Collections::unmodifiableCollection, Collections.emptyList())), UNMODIFIABLE_LIST(new DecoratorMarshaller<>(List.class, Collections::unmodifiableList, new LinkedList<>())), UNMODIFIABLE_MAP(new DecoratorMarshaller<>(Map.class, Collections::unmodifiableMap, Collections.emptyMap())), UNMODIFIABLE_NAVIGABLE_MAP(new DecoratorMarshaller<>(NavigableMap.class, Collections::unmodifiableNavigableMap, Collections.emptyNavigableMap())), UNMODIFIABLE_NAVIGABLE_SET(new DecoratorMarshaller<>(NavigableSet.class, Collections::unmodifiableNavigableSet, Collections.emptyNavigableSet())), UNMODIFIABLE_RANDOM_ACCESS_LIST(new DecoratorMarshaller<>(List.class, Collections::unmodifiableList, Collections.emptyList())), UNMODIFIABLE_SET(new DecoratorMarshaller<>(Set.class, Collections::unmodifiableSet, Collections.emptySet())), UNMODIFIABLE_SORTED_MAP(new DecoratorMarshaller<>(SortedMap.class, Collections::unmodifiableSortedMap, Collections.emptySortedMap())), UNMODIFIABLE_SORTED_SET(new DecoratorMarshaller<>(SortedSet.class, Collections::unmodifiableSortedSet, Collections.emptySortedSet())), UUID(new ProtoStreamBuilderFieldSetMarshaller<>(UUIDMarshaller.INSTANCE)), ;
ProtoStreamByteBufferMarshaller#isMarshallableを見たりすると、クラスに対応するMarshallerを親クラスをたどって探していたりするので、
その中ですでに用意されているMarshallerを利用するような感じなのかなと思ったり。
@Override public boolean isMarshallable(Object object) { if ((object == null) || (object instanceof Class)) return true; Class<?> targetClass = object.getClass(); if (AnyField.fromJavaType(targetClass) != null) return true; if (targetClass.isArray()) { for (int i = 0; i < Array.getLength(object); ++i) { if (!this.isMarshallable(Array.get(object, i))) return false; } return true; } if (Proxy.isProxyClass(targetClass)) { return this.isMarshallable(Proxy.getInvocationHandler(object)); } while (targetClass != null) { if (this.context.canMarshall(targetClass)) { return true; } targetClass = targetClass.getSuperclass(); } return false; }
で、このモジュール内のソースコードを見ていると、.protoファイルを扱っている箇所がありました。
protected AbstractSerializationContextInitializer(String resourceName, ClassLoader loader) { this.resourceName = (resourceName == null) ? this.getClass().getPackage().getName() + ".proto" : resourceName; this.loader = (loader == null) ? WildFlySecurityManager.getClassLoaderPrivileged(this.getClass()) : loader; }
ということは、.protoファイルが含まれていそうですね。ちょっと探してみましょう。
$ find modules/system/layers/base/org/wildfly/clustering/ -name '*.jar' | xargs -I {} unzip -l {} | grep proto$
843 2022-05-18 11:57 org.wildfly.clustering.ee.cache.function.proto
371 2022-05-18 12:00 org.wildfly.clustering.ee.infinispan.scheduler.proto
139 2022-05-18 11:56 org.wildfly.clustering.marshalling.spi.proto
1049 2022-05-18 11:56 org.wildfly.clustering.marshalling.protostream.proto
459 2022-05-18 11:55 java.net.proto
658 2022-05-18 11:55 java.sql.proto
362 2022-05-18 11:55 java.util.concurrent.atomic.proto
3198 2022-05-18 11:55 java.time.proto
665 2022-05-18 11:55 java.lang.proto
495 2022-05-18 11:55 java.math.proto
1067 2022-05-18 11:55 java.util.concurrent.proto
4706 2022-05-18 11:55 java.util.proto
270 2022-05-18 12:02 io.undertow.security.api.proto
242 2022-05-18 12:02 org.wildfly.clustering.web.undertow.sso.elytron.proto
158 2022-05-18 12:01 org.wildfly.clustering.web.cache.sso.proto
152 2022-05-18 12:01 org.wildfly.clustering.web.cache.sso.coarse.proto
420 2022-05-18 12:01 org.wildfly.clustering.web.cache.session.proto
716 2022-05-18 12:01 org.wildfly.clustering.web.cache.session.fine.proto
319 2022-05-18 12:02 org.wildfly.clustering.web.infinispan.session.fine.proto
149 2022-05-18 12:02 org.wildfly.clustering.web.infinispan.sso.proto
163 2022-05-18 12:02 org.wildfly.clustering.web.infinispan.session.coarse.proto
158 2022-05-18 12:02 org.wildfly.clustering.web.infinispan.routing.proto
211 2022-05-18 12:02 org.wildfly.clustering.web.infinispan.sso.coarse.proto
533 2022-05-18 12:02 org.wildfly.clustering.web.infinispan.session.proto
207 2022-05-18 12:01 org.wildfly.clustering.web.hotrod.sso.coarse.proto
315 2022-05-18 12:01 org.wildfly.clustering.web.hotrod.session.fine.proto
244 2022-05-18 12:01 org.wildfly.clustering.web.hotrod.session.proto
159 2022-05-18 12:01 org.wildfly.clustering.web.hotrod.session.coarse.proto
145 2022-05-18 12:01 org.wildfly.clustering.web.hotrod.sso.proto
133 2022-05-18 11:57 org.infinispan.commons.io.proto
745 2022-05-18 11:57 org.infinispan.metadata.proto
254 2022-05-18 12:00 org.wildfly.clustering.ejb.infinispan.proto
401 2022-05-18 12:00 org.wildfly.clustering.ejb.infinispan.bean.proto
266 2022-05-18 12:00 org.jboss.as.network.proto
365 2022-05-18 12:00 org.wildfly.clustering.ejb.infinispan.group.proto
125 2022-05-18 12:00 org.jboss.ejb.client.proto
445 2022-05-18 12:01 org.wildfly.clustering.server.singleton.proto
125 2022-05-18 12:01 org.wildfly.clustering.server.dispatcher.proto
714 2022-05-18 12:01 org.jboss.msc.service.proto
110 2022-05-18 12:01 org.infinispan.remoting.transport.proto
353 2022-05-18 12:01 org.infinispan.remoting.transport.jgroups.proto
170 2022-05-18 12:01 org.jgroups.util.proto
135 2022-05-18 12:01 org.wildfly.clustering.server.registry.proto
313 2022-05-18 12:01 org.jgroups.stack.proto
480 2022-05-18 12:01 org.wildfly.clustering.server.group.proto
1070 2022-05-18 12:01 org.wildfly.clustering.server.provider.proto
…たくさんありました。
java.〜パッケージの分は、こちらに置かれていましたね。
https://github.com/wildfly/wildfly/tree/26.1.1.Final/clustering/marshalling/api/src/main/resources
例として、java.util.protoファイルの中身を見てみましょう。java.utilパッケージ内のクラスに対する定義がずらっと並んでいます。
$ unzip -p modules/system/layers/base/org/wildfly/clustering/marshalling/api/main/wildfly-clustering-marshalling-api-26.1.1.Final.jar java.util.proto
package java.util;
import "java.lang.proto";
// IDs: 10 - 59
// Empty collections
/**
* @TypeId(10)
*/
message EmptyList {
}
/**
* @TypeId(11)
*/
message EmptyMap {
}
/**
* @TypeId(12)
*/
message EmptyNavigableMap {
}
/**
* @TypeId(13)
*/
message EmptyNavigableSet {
}
/**
* @TypeId(14)
*/
message EmptySet {
}
// Singleton collections
/**
* @TypeId(15)
*/
message SingletonList {
optional bytes value = 1;
}
/**
* @TypeId(16)
*/
message SingletonMap {
optional bytes key = 1;
optional bytes value = 2;
}
/**
* @TypeId(17)
*/
message SingletonSet {
optional bytes value = 1;
}
// Synchronized collections
/**
* @TypeId(20)
*/
message SynchronizedCollection {
optional bytes value = 1;
}
/**
* @TypeId(21)
*/
message SynchronizedList {
optional bytes value = 1;
}
〜省略〜
message UnmodifiableSortedMap {
optional bytes collection = 1;
}
message UnmodifiableSortedSet {
optional bytes collection = 1;
}
// Collections
/**
* @TypeId(30)
*/
message ArrayDeque {
repeated bytes element = 1;
}
/**
* @TypeId(31)
*/
message ArrayList {
repeated bytes element = 1;
}
/**
* @TypeId(32)
*/
message BitSet {
optional bytes value = 1;
}
/**
* @TypeId(33)
*/
message Calendar {
optional string type = 1;
optional Date time = 2;
optional bool lenient = 3;
optional string zone = 4;
optional uint32 firstDayOfWeek = 5;
optional uint32 minDaysInFirstWeek = 6;
}
/**
* @TypeId(34)
*/
message Currency {
optional string value = 1;
}
/**
* @TypeId(35)
*/
message Date {
optional uint64 postEpochSeconds = 1;
optional uint64 preEpochSeconds = 2;
optional uint32 millisOfSecond = 3;
optional uint32 nanosOfSecond = 4;
}
/**
* @TypeId(36)
*/
message EnumMap {
optional java.lang.Class enumClass = 1;
optional java.lang.Class complementClass = 2;
optional bytes bits = 3;
repeated uint32 element = 4;
repeated bytes value = 5;
}
/**
* @TypeId(37)
*/
message EnumSet {
optional java.lang.Class enumClass = 1;
optional java.lang.Class complementClass = 2;
optional bytes bits = 3;
repeated uint32 element = 4;
}
/**
* @TypeId(38)
*/
message HashMap {
repeated bytes key = 1;
repeated bytes value = 2;
}
/**
* @TypeId(39)
*/
message HashSet {
repeated bytes element = 1;
}
/**
* @TypeId(40)
*/
message LinkedHashMap {
repeated bytes key = 1;
repeated bytes value = 2;
optional bool accessOrder = 3;
}
〜省略〜
/**
* @TypeId(49)
*/
message SimpleImmutableEntry {
optional bytes key = 1;
optional bytes value = 2;
}
/**
* @TypeId(50)
*/
message TimeZone {
optional string value = 1;
}
/**
* @TypeId(51)
*/
message TreeMap {
repeated bytes key = 1;
repeated bytes value = 2;
optional bool reverse = 3;
optional bytes comparator = 4;
}
/**
* @TypeId(52)
*/
message TreeSet {
repeated bytes element = 1;
optional bool reverse = 2;
optional bytes comparator = 3;
}
/**
* @TypeId(53)
*/
message UUID {
optional sfixed64 high = 1;
optional sfixed64 low = 2;
}
WilfFlyのモジュール内にも.protoファイルがありました。
https://github.com/wildfly/wildfly/tree/26.1.1.Final/clustering/web/infinispan/src/main/resources
中身を見てみましょう。
$ unzip -p modules/system/layers/base/org/wildfly/clustering/web/infinispan/main/wildfly-clustering-web-infinispan-26.1.1.Final.jar org.wildfly.clustering.web.infinispan.session.proto
package org.wildfly.clustering.web.infinispan.session;
import "java.time.proto";
// IDs: 200 - 204
/**
* @TypeId(200)
*/
message SessionCreationMetaDataKey {
required bytes id = 1;
}
/**
* @TypeId(201)
*/
message SessionAccessMetaDataKey {
required bytes id = 1;
}
/**
* @TypeId(202)
*/
enum SessionCreationMetaDataKeyFilter {
INSTANCE = 0;
}
/**
* @TypeId(203)
*/
message SimpleSessionExpirationMetaData {
optional java.time.Duration maxInactiveInterval = 1;
optional java.time.Instant lastAccessEndTime = 2;
}
Marshallerも含まれていますね。
public class SimpleSessionExpirationMetaDataMarshaller implements ProtoStreamMarshaller<SimpleSessionExpirationMetaData> {
となると、これらのMarshallerをどうやって検出しているのかという疑問も出てきます。
ちょっと探していると、このあたりでService Loaderの仕組みでSerializationContextInitializerをロードしているようです。
private boolean tryLoad(ClassLoader loader) { PrivilegedAction<Iterator<SerializationContextInitializer>> action = new PrivilegedAction<Iterator<SerializationContextInitializer>>() { @Override public Iterator<SerializationContextInitializer> run() { return ServiceLoader.load(SerializationContextInitializer.class, loader).iterator(); } }; Iterator<SerializationContextInitializer> initializers = WildFlySecurityManager.doUnchecked(action); boolean init = initializers.hasNext(); while (initializers.hasNext()) { SerializationContextInitializer initializer = initializers.next(); // Do not load initializers from protostream-types if (!initializer.getClass().getName().startsWith(PROTOSTREAM_BASE_PACKAGE_NAME)) { this.init(initializer); } } return init; }
探してみましょう。
$ find modules/system/layers/base/org/wildfly/clustering/ -name '*.jar' | xargs -I {} unzip -l {} | grep SerializationContextInitializer
81 2022-05-18 11:57 META-INF/services/org.infinispan.protostream.SerializationContextInitializer
5107 2022-05-18 11:57 org/wildfly/clustering/ee/cache/function/FunctionSerializationContextInitializer.class
88 2022-05-18 12:00 META-INF/services/org.infinispan.protostream.SerializationContextInitializer
2672 2022-05-18 12:00 org/wildfly/clustering/ee/infinispan/scheduler/SchedulerSerializationContextInitializer.class
2592 2022-05-18 11:56 org/wildfly/clustering/marshalling/protostream/CompositeSerializationContextInitializer.class
1173 2022-05-18 11:56 org/wildfly/clustering/marshalling/protostream/SerializationContextInitializerProvider.class
1524 2022-05-18 11:56 org/wildfly/clustering/marshalling/protostream/LangSerializationContextInitializer.class
2236 2022-05-18 11:56 org/wildfly/clustering/marshalling/protostream/ProviderSerializationContextInitializer.class
2508 2022-05-18 11:56 org/wildfly/clustering/marshalling/protostream/AbstractSerializationContextInitializer.class
995 2022-05-18 11:56 org/wildfly/clustering/marshalling/protostream/AnySerializationContextInitializer.class
3619 2022-05-18 11:56 org/wildfly/clustering/marshalling/protostream/DefaultSerializationContextInitializerProvider.class
960 2022-05-18 12:02 org/wildfly/clustering/web/undertow/sso/elytron/ElytronSSOSerializationContextInitializer.class
178 2022-05-18 12:02 META-INF/services/org.infinispan.protostream.SerializationContextInitializer
1024 2022-05-18 12:02 org/wildfly/clustering/web/undertow/sso/UndertowSecuritySerializationContextInitializer.class
1037 2022-05-18 12:01 org/wildfly/clustering/web/cache/session/SessionSerializationContextInitializer.class
336 2022-05-18 12:01 META-INF/services/org.infinispan.protostream.SerializationContextInitializer
2131 2022-05-18 12:01 org/wildfly/clustering/web/cache/sso/SSOSerializationContextInitializer.class
4352 2022-05-18 12:01 org/wildfly/clustering/web/cache/session/fine/FineSessionAttributesSerializationContextInitializer.class
2159 2022-05-18 12:01 org/wildfly/clustering/web/cache/sso/coarse/CoarseSSOSerializationContextInitializer.class
1825 2022-05-18 12:02 org/wildfly/clustering/web/infinispan/session/coarse/CoarseSessionAttributeSerializationContextInitializer.class
558 2022-05-18 12:02 META-INF/services/org.infinispan.protostream.SerializationContextInitializer
1964 2022-05-18 12:02 org/wildfly/clustering/web/infinispan/sso/coarse/CoarseSSOSerializationContextInitializer.class
2304 2022-05-18 12:02 org/wildfly/clustering/web/infinispan/session/SessionSerializationContextInitializer.class
1718 2022-05-18 12:02 org/wildfly/clustering/web/infinispan/sso/SSOSerializationContextInitializer.class
2444 2022-05-18 12:02 org/wildfly/clustering/web/infinispan/routing/InfinispanRoutingSerializationContextInitializer.class
1930 2022-05-18 12:02 org/wildfly/clustering/web/infinispan/session/fine/FineSessionAttributesSerializationContextInitializer.class
1962 2022-05-18 12:01 org/wildfly/clustering/web/hotrod/session/SessionSerializationContextInitializer.class
443 2022-05-18 12:01 META-INF/services/org.infinispan.protostream.SerializationContextInitializer
1702 2022-05-18 12:01 org/wildfly/clustering/web/hotrod/sso/SSOSerializationContextInitializer.class
1944 2022-05-18 12:01 org/wildfly/clustering/web/hotrod/sso/coarse/CoarseSSOSerializationContextInitializer.class
1809 2022-05-18 12:01 org/wildfly/clustering/web/hotrod/session/coarse/CoarseSessionAttributeSerializationContextInitializer.class
1910 2022-05-18 12:01 org/wildfly/clustering/web/hotrod/session/fine/FineSessionAttributesSerializationContextInitializer.class
1117 2022-05-18 11:57 org/wildfly/clustering/infinispan/marshalling/protostream/IOSerializationContextInitializer.class
1521 2022-05-18 11:57 org/wildfly/clustering/infinispan/spi/metadata/MetadataSerializationContextInitializer.class
2747 2022-05-18 12:00 org/wildfly/clustering/ejb/infinispan/EJBSerializationContextInitializerProvider.class
945 2022-05-18 12:00 org/wildfly/clustering/ejb/infinispan/InfinispanEJBSerializationContextInitializer.class
589 2022-05-18 12:00 org/wildfly/clustering/ejb/infinispan/EJBSerializationContextInitializer.class
73 2022-05-18 12:00 META-INF/services/org.infinispan.protostream.SerializationContextInitializer
2175 2022-05-18 12:00 org/wildfly/clustering/ejb/infinispan/bean/BeanSerializationContextInitializer.class
2745 2022-05-18 12:00 org/wildfly/clustering/ejb/infinispan/group/BeanGroupSerializationContextInitializer.class
75 2022-05-18 12:00 META-INF/services/org.infinispan.protostream.SerializationContextInitializer
2194 2022-05-18 12:00 org/wildfly/clustering/ejb/client/EJBClientSerializationContextInitializer.class
1404 2022-05-18 12:01 org/wildfly/clustering/server/singleton/SingletonSerializationContextInitializer.class
1012 2022-05-18 12:01 org/wildfly/clustering/server/registry/RegistrySerializationContextInitializer.class
2315 2022-05-18 12:01 org/wildfly/clustering/server/group/GroupSerializationContextInitializer.class
68 2022-05-18 12:01 META-INF/services/org.infinispan.protostream.SerializationContextInitializer
1039 2022-05-18 12:01 org/wildfly/clustering/server/dispatcher/CommandDispatcherSerializationContextInitializer.class
3677 2022-05-18 12:01 org/wildfly/clustering/server/ServerSerializationContextInitializerProvider.class
4092 2022-05-18 12:01 org/wildfly/clustering/server/provider/ServiceProviderRegistrySerializationContextInitializer.class
577 2022-05-18 12:01 org/wildfly/clustering/server/ServerSerializationContextInitializer.class
…たくさんありました。
META-INF/services/org.infinispan.protostream.SerializationContextInitializerをピックアップして見てみます。
$ unzip -p modules/system/layers/base/org/wildfly/clustering/web/cache/main/wildfly-clustering-web-cache-26.1.1.Final.jar META-INF/services/org.infinispan.protostream.SerializationContextInitializer org.wildfly.clustering.web.cache.session.SessionSerializationContextInitializer org.wildfly.clustering.web.cache.session.fine.FineSessionAttributesSerializationContextInitializer org.wildfly.clustering.web.cache.sso.SSOSerializationContextInitializer org.wildfly.clustering.web.cache.sso.coarse.CoarseSSOSerializationContextInitializer
どうやら、ここがMarshallerの検出に関係ありそうですね。
いくつか見てみましょう。
こちらに、SerializationContextにMarshallerを登録している箇所があります。
@MetaInfServices(SerializationContextInitializer.class) public class SessionSerializationContextInitializer extends AbstractSerializationContextInitializer { @Override public void registerMarshallers(SerializationContext context) { context.registerMarshaller(new SessionCreationMetaDataEntryMarshaller()); context.registerMarshaller(new SessionAccessMetaDataMarshaller()); } }
@MetaInfServicesアノテーションが付与されているので、META-INF/services/org.infinispan.protostream.SerializationContextInitializerファイルを
作成する仕組みはこちらを使っていそうですね。
Infinispanも内部で使っていたライブラリです。
他のパターンも見てみましょう。
$ unzip -p modules/system/layers/base/org/wildfly/clustering/server/main/wildfly-clustering-server-26.1.1.Final.jar META-INF/services/org.infinispan.protostream.SerializationContextInitializer org.wildfly.clustering.server.ServerSerializationContextInitializer
Service LoaderによりSerializationContextInitializerをロードして、
@MetaInfServices(SerializationContextInitializer.class) public class ServerSerializationContextInitializer extends CompositeSerializationContextInitializer { public ServerSerializationContextInitializer() { super(ServerSerializationContextInitializerProvider.class); } }
実際のMarshallerを把握しているのはSerializationContextInitializerProvider、というパターンもあるようです。
public enum ServerSerializationContextInitializerProvider implements SerializationContextInitializerProvider { COMMAND_DISPATCHER(new CommandDispatcherSerializationContextInitializer()), JGROUPS_UTIL(new ProviderSerializationContextInitializer<>("org.jgroups.util.proto", JGroupsUtilMarshallerProvider.class)), JGROUPS_STACK(new ProviderSerializationContextInitializer<>("org.jgroups.stack.proto", JGroupsStackMarshallerProvider.class)), INFINISPAN_TRANSPORT(new ProviderSerializationContextInitializer<>("org.infinispan.remoting.transport.proto", InfinispanTransportMarshallerProvider.class)), INFINISPAN_JGROUPS_TRANSPORT(new ProviderSerializationContextInitializer<>("org.infinispan.remoting.transport.jgroups.proto", InfinispanJGroupsTransportMarshallerProvider.class)), GROUP(new GroupSerializationContextInitializer()), PROVIDER(new ServiceProviderRegistrySerializationContextInitializer()), REGISTRY(new RegistrySerializationContextInitializer()), SERVICE(new ProviderSerializationContextInitializer<>("org.jboss.msc.service.proto", ServiceMarshallerProvider.class)), SINGLETON(new SingletonSerializationContextInitializer()), ;
ちなみに、META-INF/services/org.infinispan.protostream.SerializationContextInitializerファイルはjava.〜パッケージに対応する
.protoファイルを扱っているwildfly-clustering-marshalling-apiのJARファイル内にはありませんでした。
java.〜の基本的なパッケージに対応するMarshallerは、以下で共通的に登録されているようです。
public SerializationContextBuilder(ClassLoaderMarshaller marshaller) { // Load default schemas first, so they can be referenced by loader-specific schemas this.register(Collections.singleton(new LangSerializationContextInitializer(marshaller))); this.register(EnumSet.allOf(DefaultSerializationContextInitializerProvider.class)); }
概ね、把握できたかなと思います。
WildFly側でProtoStreamのMarshallerを大量に作成しておき、Infinispanに格納する際のシリアライズ処理の効率化と利用者のMarshallerの実装の
手間を軽減している感じですね。
ところで、用意されていないクラスがマーシャリング対象になった場合はどうするんでしょうね…?自分でProtoStreamのMarshallerを
書いたらいいのでしょうか?
JBoss Marshalling
なお、比較としてJBoss Marshallingの方も見ておきましょう。JBoss Marshallingの方のモジュールは、割とあっさりしています。
Java標準のシリアライズのように、個別にMarshallerを書かなくてもオブジェクトをシリアライズできますからね。
と、見ていくのはこれくらいにして、少し確認してみましょう。
お題
WildFlyのHTTPセッション管理を、Infinispan Serverとする変更をまずは行います。
つまり、このエントリーでやったことですね。
WildFlyのHTTPセッションの保存先をInfinispan Serverに変更する - CLOVER🍀
さらに、作成したソースコードを変更しつつ使って、WildFlyでのマーシャリングとInfinispan Serverでのエンコーディングの関連を
見ていこうと思います。
いきなりInfinispan Server(Hot Rod)でやっていて、デフォルトのEmbeddedを飛ばしていますが。こちらはデフォルトから変えることは
ないだろうし、まあいいかなと…。
環境
今回の環境は、こちら。
$ java --version openjdk 17.0.3 2022-04-19 OpenJDK Runtime Environment (build 17.0.3+7-Ubuntu-0ubuntu0.20.04.1) OpenJDK 64-Bit Server VM (build 17.0.3+7-Ubuntu-0ubuntu0.20.04.1, mixed mode, sharing) $ mvn --version Apache Maven 3.8.6 (84538c9988a25aec085021c365c560670ad80f63) Maven home: $HOME/.sdkman/candidates/maven/current Java version: 17.0.3, vendor: Private Build, runtime: /usr/lib/jvm/java-17-openjdk-amd64 Default locale: ja_JP, platform encoding: UTF-8 OS name: "linux", version: "5.4.0-120-generic", arch: "amd64", family: "unix"
WildFlyは、最初に取得したように26.1.1.Finalを使います。起動は、そのままローカルで。
$ bin/standalone.sh
Infinispan Serverは13.0.10.Finalを使用し、172.18.0.2にひとつのNodeを用意することにします。クラスターは構成しません。
$ bin/server.sh \
-b 0.0.0.0 \
-Djgroups.tcp.address=`hostname -i`
今回、Infinispan Server上にユーザーは2つ作成します。
$ bin/cli.sh user create -g admin -p password ispn-admin $ bin/cli.sh user create -g application -p password web-session-user
adminグループのユーザーでは、Cacheを作成します。WildFly側では実行時にCacheを定義させず、定義済みのCacheを利用する方針に
なりますね。
WildFlyからはapplicationグループのユーザーを使うことになります。
サンプルアプリケーション
サンプルアプリケーションのソースコードは、このエントリーからそのまま持ってきます。
WildFlyのHTTPセッションの保存先をInfinispan Serverに変更する - CLOVER🍀
Maven依存関係等。
<groupId>org.littlewings</groupId> <artifactId>remote-wildfly-hotrod-session-marshaller</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <failOnMissingWebXml>false</failOnMissingWebXml> </properties> <dependencies> <dependency> <groupId>jakarta.platform</groupId> <artifactId>jakarta.jakartaee-web-api</artifactId> <version>8.0.0</version> <scope>provided</scope> </dependency> </dependencies> <build> <finalName>ROOT</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>3.3.2</version> </plugin> </plugins> </build>
WARファイル名は、ROOT.warとします。
HTTPセッションに保存するクラス。
src/main/java/org/littlewings/infinispan/session/Counter.java
package org.littlewings.infinispan.session; import java.io.Serializable; import java.util.concurrent.atomic.AtomicInteger; public class Counter implements Serializable { private static final long serialVersionUID = 1L; AtomicInteger value = new AtomicInteger(); public int increment() { return value.incrementAndGet(); } public int getValue() { return value.get(); } }
HTTPセッションを操作するクラス。
src/main/java/org/littlewings/infinispan/session/CounterServlet.java
package org.littlewings.infinispan.session; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; @WebServlet("/counter") public class CounterServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session = req.getSession(); Counter counter = (Counter) session.getAttribute("counter"); if (counter == null) { counter = new Counter(); session.setAttribute("counter", counter); } int current = counter.increment(); resp.getWriter().write(String.format("current value = %d%n", current)); } }
web.xml。<distributable/>を忘れずに。
src/main/webapp/WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee webapp_4_0.xsd" version="4.0"> <distributable/> </web-app>
PROTOSTREAM Marshallerを使う
では、MarshallerにPROTOSTREAMを選択しつつ、いろいろ試してみましょう。
まずはInfinispan ServerにCacheを作成します。こんな感じのテンプレートを用意。
/path/to/cache-protostream.xml
<?xml version="1.0" ?> <infinispan> <cache-container> <distributed-cache mode="SYNC" name="protoStreamCacheTemplate"> <encoding> <key media-type="application/x-protostream"/> <value media-type="application/x-protostream"/> </encoding> </distributed-cache> </cache-container> </infinispan>
encodingをProtoStreamに限定しています。
Infinispan Serverにログインして
$ bin/cli.sh -c http://ispn-admin:password@localhost:11222 [infinispan-server-10427@cluster//containers/default]>
Cacheを作成。
create cache ROOT.war -f /path/to/cache-protostream.xml
できました。
describe caches/ROOT.war
{
"distributed-cache" : {
"mode" : "SYNC",
"encoding" : {
"key" : {
"media-type" : "application/x-protostream"
},
"value" : {
"media-type" : "application/x-protostream"
}
}
}
}
次に、WildFly側も準備をします。
管理CLIを起動して
$ bin/jboss-cli.sh -c [standalone@localhost:9990 /]
Socket Binding Group、Remote Cache Container、Hot Rod Session Managementを作成。デフォルトのHTTPセッション保存先も
Infinispan Serverにしておきます。
/socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=infinispan-server:add(host=172.18.0.2, port=11222)
batch
/subsystem=infinispan/remote-cache-container=remote-infinispan:add(default-remote-cluster=remote-infinispan-cluster)
/subsystem=infinispan/remote-cache-container=remote-infinispan/remote-cluster=remote-infinispan-cluster:add(socket-bindings=[infinispan-server])
run-batch
/subsystem=infinispan/remote-cache-container=remote-infinispan:write-attribute(name=modules,value=[org.wildfly.clustering.web.hotrod])
/subsystem=infinispan/remote-cache-container=remote-infinispan:write-attribute(name=properties,value={infinispan.client.hotrod.auth_username=web-session-user,infinispan.client.hotrod.auth_password=password})
/subsystem=infinispan/remote-cache-container=remote-infinispan:write-attribute(name=marshaller,value=PROTOSTREAM)
/subsystem=distributable-web/hotrod-session-management=remote-session:add(remote-cache-container=remote-infinispan, cache-configuration=org.infinispan.DIST_SYNC, granularity=SESSION)
/subsystem=distributable-web/hotrod-session-management=remote-session/affinity=none:add()
/subsystem=distributable-web:write-attribute(name=default-session-management,value=remote-session)
reload
しれっと入っていますが、MarshallerはPROTOSTREAMです。
/subsystem=infinispan/remote-cache-container=remote-infinispan:write-attribute(name=marshaller,value=PROTOSTREAM)
では、アプリケーションをパッケージングしてデプロイしたいと思うのですが。
ふと思い立ったので、Serializableを外してデプロイしてみましょう。PROTOSTREAMを選んでもやっぱりダメなのでしょうか?
public class Counter /* implements Serializable */ { // private static final long serialVersionUID = 1L;
試してみましょう。パッケージング。
$ mvn package
デプロイ。
$ cp target/ROOT.war /path/to/wildfly-26.1.1.Final/standalone/deployments
デプロイ自体は、問題なく成功します。
アクセスしてみましょう。
$ curl -c cookie.txt -b cookie.txt localhost:8080/counter
すると、WildFly側でSerializableではないということで失敗します。
13:42:55,469 ERROR [io.undertow.request] (default task-1) UT005023: Exception handling request to /counter: java.lang.IllegalArgumentException: java.io.NotSerializableException: org.littlewings.infinispan.session.Counter
at org.wildfly.clustering.web.cache@26.1.1.Final//org.wildfly.clustering.web.cache.session.coarse.CoarseSessionAttributes.setAttribute(CoarseSessionAttributes.java:76)
at org.wildfly.clustering.web.undertow@26.1.1.Final//org.wildfly.clustering.web.undertow.session.DistributableSession.setAttribute(DistributableSession.java:228)
at io.undertow.servlet@2.2.17.Final//io.undertow.servlet.spec.HttpSessionImpl.setAttribute(HttpSessionImpl.java:169)
at deployment.ROOT.war//org.littlewings.infinispan.session.CounterServlet.doGet(CounterServlet.java:21)
at javax.servlet.api@2.0.0.Final//javax.servlet.http.HttpServlet.service(HttpServlet.java:503)
at javax.servlet.api@2.0.0.Final//javax.servlet.http.HttpServlet.service(HttpServlet.java:590)
at io.undertow.servlet@2.2.17.Final//io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74)
at io.undertow.servlet@2.2.17.Final//io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
at io.undertow.servlet@2.2.17.Final//io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68)
at io.undertow.servlet@2.2.17.Final//io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
〜省略〜
やっぱりそうですよね。
というわけで、Serializableを実装するように戻して
public class Counter implements Serializable {
private static final long serialVersionUID = 1L;
再度パッケージングしてデプロイ。
$ mvn package $ cp target/ROOT.war /path/to/wildfly-26.1.1.Final/standalone/deployments
当然ですが、今度はうまくいきます。
$ curl -c cookie.txt -b cookie.txt localhost:8080/counter current value = 1 $ curl -c cookie.txt -b cookie.txt localhost:8080/counter current value = 2 $ curl -c cookie.txt -b cookie.txt localhost:8080/counter current value = 3
では、Infinispan Serverに保存する際のエンコーディングも本当にProtoStreamなのでしょうか?
Infinispan Serverで、いったんCacheを削除します。
drop cache ROOT.war
encodingをJBoss Marshallingにしたテンプレートを用意。
/path/to//cache-jboss-marshalling.xml
<?xml version="1.0" ?> <infinispan> <cache-container> <distributed-cache mode="SYNC" name="jbossMarshallingCacheTemplate"> <encoding> <key media-type="application/x-jboss-marshalling"/> <value media-type="application/x-jboss-marshalling"/> </encoding> </distributed-cache> </cache-container> </infinispan>
Cacheを作成。
create cache ROOT.war -f /path/to/cache-jboss-marshalling.xml
確認。
describe caches/ROOT.war
{
"distributed-cache" : {
"mode" : "SYNC",
"encoding" : {
"key" : {
"media-type" : "application/x-jboss-marshalling"
},
"value" : {
"media-type" : "application/x-jboss-marshalling"
}
}
}
}
WildFly側をreloadしてみます。
reload
すると、Cacheのエンコーディングが合わないということで、アプリケーションの起動に失敗します。
13:54:23,932 WARN [org.infinispan.HOTROD] (HotRod-client-async-pool-0) ISPN004005: Error received from the server: org.infinispan.commons.CacheConfigurationException: Unable to inject dependencies for component class org.infinispan.encoding.DataConversion, path wireDependencies (a org.infinispan.encoding.DataConversion)
org.infinispan.commons.dataconversion.EncodingException: ISPN000492: Cannot find transcoder between 'application/x-protostream' to 'application/x-jboss-marshalling'
13:54:23,932 ERROR [org.jboss.msc.service.fail] (MSC service thread 1-2) MSC000001: Failed to start service jboss.clustering.web."ROOT.war": org.jboss.msc.service.StartException in service jboss.clustering.web."ROOT.war": org.infinispan.client.hotrod.exceptions.HotRodClientException:Request for messageId=60 returned server error (status=0x85): org.infinispan.commons.CacheConfigurationException: Unable to inject dependencies for component class org.infinispan.encoding.DataConversion, path wireDependencies (a org.infinispan.encoding.DataConversion)
org.infinispan.commons.dataconversion.EncodingException: ISPN000492: Cannot find transcoder between 'application/x-protostream' to 'application/x-jboss-marshalling'
at org.wildfly.clustering.service@26.1.1.Final//org.wildfly.clustering.service.FunctionalService.start(FunctionalService.java:66)
at org.jboss.msc@1.4.13.Final//org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:1739)
at org.jboss.msc@1.4.13.Final//org.jboss.msc.service.ServiceControllerImpl$StartTask.execute(ServiceControllerImpl.java:1701)
at org.jboss.msc@1.4.13.Final//org.jboss.msc.service.ServiceControllerImpl$ControllerTask.run(ServiceControllerImpl.java:1559)
at org.jboss.threads@2.4.0.Final//org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1990)
at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377)
at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: org.infinispan.client.hotrod.exceptions.HotRodClientException:Request for messageId=60 returned server error (status=0x85): org.infinispan.commons.CacheConfigurationException: Unable to inject dependencies for component class org.infinispan.encoding.DataConversion, path wireDependencies (a org.infinispan.encoding.DataConversion)
org.infinispan.commons.dataconversion.EncodingException: ISPN000492: Cannot find transcoder between 'application/x-protostream' to 'application/x-jboss-marshalling'
at org.infinispan.client.hotrod@13.0.10.Final//org.infinispan.client.hotrod.impl.protocol.Codec20.checkForErrorsInResponseStatus(Codec20.java:323)
at org.infinispan.client.hotrod@13.0.10.Final//org.infinispan.client.hotrod.impl.protocol.Codec20.readHeader(Codec20.java:168)
at org.infinispan.client.hotrod@13.0.10.Final//org.infinispan.client.hotrod.impl.transport.netty.HeaderDecoder.decode(HeaderDecoder.java:139)
at org.infinispan.client.hotrod@13.0.10.Final//org.infinispan.client.hotrod.impl.transport.netty.HintedReplayingDecoder.callDecode(HintedReplayingDecoder.java:94)
at io.netty.netty-codec@4.1.76.Final//io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:279)
at io.netty.netty-transport@4.1.76.Final//io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.netty-transport@4.1.76.Final//io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.netty.netty-transport@4.1.76.Final//io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
at io.netty.netty-handler@4.1.76.Final//io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:286)
at io.netty.netty-transport@4.1.76.Final//io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.netty-transport@4.1.76.Final//io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.netty.netty-transport@4.1.76.Final//io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
at io.netty.netty-transport@4.1.76.Final//io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
at io.netty.netty-transport@4.1.76.Final//io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.netty-transport@4.1.76.Final//io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.netty.netty-transport@4.1.76.Final//io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
at io.netty.netty-transport-native-epoll@4.1.76.Final//io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:800)
at io.netty.netty-transport-native-epoll@4.1.76.Final//io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:487)
at io.netty.netty-transport-native-epoll@4.1.76.Final//io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:385)
〜省略〜
ということは、Infinispan Serverでの保存形態もやっぱりProtoStreamということですね。
なお、今回はreloadしましたが、裏でCacheの再作成をしてそのままアクセスしても、単純にWebアプリケーションを再デプロイしなおしても
同様の理由で失敗します。
JBOSS Marshallerを使う
次は、MarshallerにJBOSSを選択してみます。
Cacheは、encodingをJBoss Marshallerにして再作成。
drop cache ROOT.war
create cache ROOT.war -f /path/to/cache-jboss-marshalling.xml
describe caches/ROOT.war
{
"distributed-cache" : {
"mode" : "SYNC",
"encoding" : {
"key" : {
"media-type" : "application/x-jboss-marshalling"
},
"value" : {
"media-type" : "application/x-jboss-marshalling"
}
}
}
}
/path/to//cache-jboss-marshalling.xml
<?xml version="1.0" ?> <infinispan> <cache-container> <distributed-cache mode="SYNC" name="jbossMarshallingCacheTemplate"> <encoding> <key media-type="application/x-jboss-marshalling"/> <value media-type="application/x-jboss-marshalling"/> </encoding> </distributed-cache> </cache-container> </infinispan>
WildFlyは、インストールしなおして、Socket Binding Group、Remote Cache Container、Hot Rod Session Managementなどの設定。
/socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=infinispan-server:add(host=172.18.0.2, port=11222)
batch
/subsystem=infinispan/remote-cache-container=remote-infinispan:add(default-remote-cluster=remote-infinispan-cluster)
/subsystem=infinispan/remote-cache-container=remote-infinispan/remote-cluster=remote-infinispan-cluster:add(socket-bindings=[infinispan-server])
run-batch
/subsystem=infinispan/remote-cache-container=remote-infinispan:write-attribute(name=modules,value=[org.wildfly.clustering.web.hotrod])
/subsystem=infinispan/remote-cache-container=remote-infinispan:write-attribute(name=properties,value={infinispan.client.hotrod.auth_username=web-session-user,infinispan.client.hotrod.auth_password=password})
/subsystem=infinispan/remote-cache-container=remote-infinispan:write-attribute(name=marshaller,value=JBOSS)
/subsystem=distributable-web/hotrod-session-management=remote-session:add(remote-cache-container=remote-infinispan, cache-configuration=org.infinispan.DIST_SYNC, granularity=SESSION)
/subsystem=distributable-web/hotrod-session-management=remote-session/affinity=none:add()
/subsystem=distributable-web:write-attribute(name=default-session-management,value=remote-session)
reload
MarshallerはJBOSSです。
/subsystem=infinispan/remote-cache-container=remote-infinispan:write-attribute(name=marshaller,value=JBOSS)
アプリケーションをデプロイ。
$ cp target/ROOT.war /path/to/wildfly-26.1.1.Final/standalone/deployments
確認。
$ curl -c cookie.txt -b cookie.txt localhost:8080/counter
すると、SessionCreationMetaDataKeyというクラスをシリアライズできずに失敗します…。
14:07:39,868 ERROR [io.undertow.request] (default task-1) UT005023: Exception handling request to /counter: org.infinispan.client.hotrod.exceptions.HotRodClientException:: Unable to marshall object of type [org.wildfly.clustering.web.hotrod.session.SessionCreationMetaDataKey]
at org.infinispan.client.hotrod@13.0.10.Final//org.infinispan.client.hotrod.marshall.MarshallerUtil.obj2bytes(MarshallerUtil.java:121)
at org.infinispan.client.hotrod@13.0.10.Final//org.infinispan.client.hotrod.DataFormat.keyToBytes(DataFormat.java:132)
at org.infinispan.client.hotrod@13.0.10.Final//org.infinispan.client.hotrod.impl.RemoteCacheImpl.keyToBytes(RemoteCacheImpl.java:575)
at org.infinispan.client.hotrod@13.0.10.Final//org.infinispan.client.hotrod.impl.RemoteCacheImpl.getAllAsync(RemoteCacheImpl.java:466)
at org.wildfly.clustering.infinispan.client@26.1.1.Final//org.wildfly.clustering.infinispan.client.RegisteredRemoteCache.getAllAsync(RegisteredRemoteCache.java:277)
at org.infinispan.client.hotrod@13.0.10.Final//org.infinispan.client.hotrod.impl.RemoteCacheSupport.getAll(RemoteCacheSupport.java:162)
at org.wildfly.clustering.web.hotrod@26.1.1.Final//org.wildfly.clustering.web.hotrod.session.HotRodSessionMetaDataFactory.findValue(HotRodSessionMetaDataFactory.java:94)
at org.wildfly.clustering.web.hotrod@26.1.1.Final//org.wildfly.clustering.web.hotrod.session.HotRodSessionMetaDataFactory.findValue(HotRodSessionMetaDataFactory.java:54)
at org.wildfly.clustering.web.cache@26.1.1.Final//org.wildfly.clustering.web.cache.session.CompositeSessionFactory.findValue(CompositeSessionFactory.java:63)
at org.wildfly.clustering.web.cache@26.1.1.Final//org.wildfly.clustering.web.cache.session.CompositeSessionFactory.findValue(CompositeSessionFactory.java:40)
at org.wildfly.clustering.web.hotrod@26.1.1.Final//org.wildfly.clustering.web.hotrod.session.HotRodSessionManager.findSession(HotRodSessionManager.java:113)
at org.wildfly.clustering.web.cache@26.1.1.Final//org.wildfly.clustering.web.cache.session.ConcurrentSessionManager$2.apply(ConcurrentSessionManager.java:67)
at org.wildfly.clustering.web.cache@26.1.1.Final//org.wildfly.clustering.web.cache.session.ConcurrentSessionManager$2.apply(ConcurrentSessionManager.java:64)
at org.wildfly.clustering.ee.cache@26.1.1.Final//org.wildfly.clustering.ee.cache.ConcurrentManager.apply(ConcurrentManager.java:91)
at org.wildfly.clustering.web.cache@26.1.1.Final//org.wildfly.clustering.web.cache.session.ConcurrentSessionManager.findSession(ConcurrentSessionManager.java:72)
at org.wildfly.clustering.web.undertow@26.1.1.Final//org.wildfly.clustering.web.undertow.session.DistributableSessionManager.getSession(DistributableSessionManager.java:236)
at io.undertow.servlet@2.2.17.Final//io.undertow.servlet.spec.ServletContextImpl.getSession(ServletContextImpl.java:903)
at io.undertow.servlet@2.2.17.Final//io.undertow.servlet.spec.HttpServletRequestImpl.getSession(HttpServletRequestImpl.java:425)
at org.wildfly.security.elytron-web.undertow-server-servlet@1.10.1.Final//org.wildfly.elytron.web.undertow.server.servlet.ElytronHttpServletExchange$3.<init>(ElytronHttpServletExchange.java:242)
at org.wildfly.security.elytron-web.undertow-server-servlet@1.10.1.Final//org.wildfly.elytron.web.undertow.server.servlet.ElytronHttpServletExchange.sessionScope(ElytronHttpServletExchange.java:241)
at org.wildfly.security.elytron-web.undertow-server-servlet@1.10.1.Final//org.wildfly.elytron.web.undertow.server.servlet.ElytronHttpServletExchange.getScope(ElytronHttpServletExchange.java:163)
at org.wildfly.security.elytron-base@1.19.0.Final//org.wildfly.security.http.HttpAuthenticator.getAttachableSessionScope(HttpAuthenticator.java:279)
at org.wildfly.security.elytron-base@1.19.0.Final//org.wildfly.security.http.HttpAuthenticator.access$900(HttpAuthenticator.java:56)
at org.wildfly.security.elytron-base@1.19.0.Final//org.wildfly.security.http.HttpAuthenticator$1.get(HttpAuthenticator.java:252)
at org.wildfly.security.elytron-base@1.19.0.Final//org.wildfly.security.http.HttpAuthenticator.restoreIdentity(HttpAuthenticator.java:175)
at org.wildfly.security.elytron-base@1.19.0.Final//org.wildfly.security.http.HttpAuthenticator.authenticate(HttpAuthenticator.java:90)
at org.wildfly.security.elytron-web.undertow-server@1.10.1.Final//org.wildfly.elytron.web.undertow.server.SecurityContextImpl.authenticate(SecurityContextImpl.java:107)
at org.wildfly.security.elytron-web.undertow-server-servlet@1.10.1.Final//org.wildfly.elytron.web.undertow.server.servlet.ServletSecurityContextImpl.authenticate(ServletSecurityContextImpl.java:115)
at io.undertow.servlet@2.2.17.Final//io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:55)
at io.undertow.core@2.2.17.Final//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.core@2.2.17.Final//io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
at io.undertow.servlet@2.2.17.Final//io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
at io.undertow.core@2.2.17.Final//io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
at org.wildfly.security.elytron-web.undertow-server-servlet@1.10.1.Final//org.wildfly.elytron.web.undertow.server.servlet.CleanUpHandler.handleRequest(CleanUpHandler.java:38)
at io.undertow.core@2.2.17.Final//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at org.wildfly.extension.undertow@26.1.1.Final//org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61)
at io.undertow.core@2.2.17.Final//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at org.wildfly.extension.undertow@26.1.1.Final//org.wildfly.extension.undertow.deployment.GlobalRequestControllerHandler.handleRequest(GlobalRequestControllerHandler.java:68)
at io.undertow.servlet@2.2.17.Final//io.undertow.servlet.handlers.SendErrorPageHandler.handleRequest(SendErrorPageHandler.java:52)
at io.undertow.core@2.2.17.Final//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.servlet@2.2.17.Final//io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:275)
at io.undertow.servlet@2.2.17.Final//io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:79)
at io.undertow.servlet@2.2.17.Final//io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:134)
at io.undertow.servlet@2.2.17.Final//io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:131)
at io.undertow.servlet@2.2.17.Final//io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
at io.undertow.servlet@2.2.17.Final//io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
at org.wildfly.extension.undertow@26.1.1.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1544)
at org.wildfly.extension.undertow@26.1.1.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1544)
at org.wildfly.extension.undertow@26.1.1.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1544)
at org.wildfly.extension.undertow@26.1.1.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1544)
at io.undertow.servlet@2.2.17.Final//io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:255)
at io.undertow.servlet@2.2.17.Final//io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:79)
at io.undertow.servlet@2.2.17.Final//io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:100)
at io.undertow.core@2.2.17.Final//io.undertow.server.Connectors.executeRootHandler(Connectors.java:387)
at io.undertow.core@2.2.17.Final//io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:852)
at org.jboss.threads@2.4.0.Final//org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1990)
at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1348)
at org.jboss.xnio@3.8.7.Final//org.xnio.XnioWorker$WorkerThreadFactory$1$1.run(XnioWorker.java:1282)
at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.io.NotSerializableException: org.wildfly.clustering.web.hotrod.session.SessionCreationMetaDataKey
at org.jboss.marshalling.river@2.0.12.Final//org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:274)
at org.jboss.marshalling@2.0.12.Final//org.jboss.marshalling.AbstractObjectOutput.writeObject(AbstractObjectOutput.java:58)
at org.jboss.marshalling@2.0.12.Final//org.jboss.marshalling.AbstractMarshaller.writeObject(AbstractMarshaller.java:111)
at org.wildfly.clustering.marshalling.jboss@26.1.1.Final//org.wildfly.clustering.marshalling.jboss.JBossByteBufferMarshaller.writeTo(JBossByteBufferMarshaller.java:92)
at org.wildfly.clustering.infinispan.marshalling@26.1.1.Final//org.wildfly.clustering.infinispan.marshalling.AbstractUserMarshaller.writeObject(AbstractUserMarshaller.java:62)
at org.wildfly.clustering.infinispan.marshalling@26.1.1.Final//org.wildfly.clustering.infinispan.marshalling.AbstractMarshaller.objectToBuffer(AbstractMarshaller.java:107)
at org.wildfly.clustering.infinispan.marshalling@26.1.1.Final//org.wildfly.clustering.infinispan.marshalling.AbstractMarshaller.objectToByteBuffer(AbstractMarshaller.java:101)
at org.infinispan.client.hotrod@13.0.10.Final//org.infinispan.client.hotrod.marshall.MarshallerUtil.obj2bytes(MarshallerUtil.java:116)
... 60 more
Caused by: an exception which occurred:
in object org.wildfly.clustering.web.hotrod.session.SessionCreationMetaDataKey@caabe3e6
〜省略〜
見てみると、確かにこのクラスは(継承元クラスやインターフェースを含めて)Serializableではありません…。
とすると、LEGACYはどうだったのでしょう?
LEGACYを確認する
最後に、LEGACYでも動かしてみます。
Infinispan Server側のCacheは、ProtoStreamで再作成。
drop cache ROOT.war
create cache ROOT.war -f /path/to/cache-protostream.xml
describe caches/ROOT.war
{
"distributed-cache" : {
"mode" : "SYNC",
"encoding" : {
"key" : {
"media-type" : "application/x-protostream"
},
"value" : {
"media-type" : "application/x-protostream"
}
}
}
}
WildFlyの設定。
/socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=infinispan-server:add(host=172.18.0.2, port=11222)
batch
/subsystem=infinispan/remote-cache-container=remote-infinispan:add(default-remote-cluster=remote-infinispan-cluster)
/subsystem=infinispan/remote-cache-container=remote-infinispan/remote-cluster=remote-infinispan-cluster:add(socket-bindings=[infinispan-server])
run-batch
/subsystem=infinispan/remote-cache-container=remote-infinispan:write-attribute(name=modules,value=[org.wildfly.clustering.web.hotrod])
/subsystem=infinispan/remote-cache-container=remote-infinispan:write-attribute(name=properties,value={infinispan.client.hotrod.auth_username=web-session-user,infinispan.client.hotrod.auth_password=password})
/subsystem=infinispan/remote-cache-container=remote-infinispan:write-attribute(name=marshaller,value=LEGACY)
/subsystem=distributable-web/hotrod-session-management=remote-session:add(remote-cache-container=remote-infinispan, cache-configuration=org.infinispan.DIST_SYNC, granularity=SESSION)
/subsystem=distributable-web/hotrod-session-management=remote-session/affinity=none:add()
/subsystem=distributable-web:write-attribute(name=default-session-management,value=remote-session)
reload
marshallerはLEGACYにしておきます。
/subsystem=infinispan/remote-cache-container=remote-infinispan:write-attribute(name=marshaller,value=LEGACY)
アプリケーションをデプロイ。
$ cp target/ROOT.war /path/to/wildfly-26.1.1.Final/standalone/deployments
確認。
$ curl -c cookie.txt -b cookie.txt localhost:8080/counter current value = 1 $ curl -c cookie.txt -b cookie.txt localhost:8080/counter current value = 2 $ curl -c cookie.txt -b cookie.txt localhost:8080/counter current value = 3
問題なく動きますね。ということはProtoStreamが実際のMarshallerになっているようです。
これはどういうことかというと。
LEGACYの場合は条件によってPROTOSTREAMかJBOSSを選択する、となっていましたがこちらをよく見てみます。
LEGACY() {
private final Set<String> protoStreamModules = Collections.singleton("org.wildfly.clustering.web.hotrod");
private final Predicate<String> protoStreamPredicate = this.protoStreamModules::contains;
@Override
public Marshaller apply(ModuleLoader moduleLoader, List<Module> modules) {
// Choose marshaller based on the associated modules
if (modules.stream().map(Module::getName).anyMatch(this.protoStreamPredicate)) {
return PROTOSTREAM.apply(moduleLoader, modules);
}
return JBOSS.apply(moduleLoader, modules);
}
},
org.wildfly.clustering.web.hotrodというモジュールは、ここで指定していたもののことですね。
/subsystem=infinispan/remote-cache-container=remote-infinispan:write-attribute(name=modules,value=[org.wildfly.clustering.web.hotrod])
つまり、Hot Rod Session ManagementではProtoStreamが前提になっている気がしますね…。
Embeddedの方はどうでしょう。
LEGACY() {
private final Set<String> protoStreamModules = new HashSet<>(Arrays.asList("org.wildfly.clustering.server", "org.wildfly.clustering.ejb.infinispan", "org.wildfly.clustering.web.infinispan"));
private final Predicate<String> protoStreamPredicate = this.protoStreamModules::contains;
@Override
public Marshaller apply(ModuleLoader moduleLoader, List<Module> modules) {
// Choose marshaller based on the associated modules
if (modules.stream().map(Module::getName).anyMatch(this.protoStreamPredicate)) {
return PROTOSTREAM.apply(moduleLoader, modules);
}
return JBOSS.apply(moduleLoader, modules);
}
},
server、ejb、webですね。
Arrays.asList("org.wildfly.clustering.server", "org.wildfly.clustering.ejb.infinispan", "org.wildfly.clustering.web.infinispan")
少なくともHTTPセッションのMarshallerに関しては、ProtoStreamを使うことが前提になっていることがわかりました。
だいたい雰囲気がわかったので、これで良しとしましょう。
まとめ
WildFlyのInfinispanサブシステムで、MarshallerにProtoStreamが使われるようになっていたのでちょっと気になっていろいろ調べてみました。
むしろ、今のWildFlyのHTTPセッション等のマーシャリングの前提になっているようですね。ProtoStreamといえば、IDLを書いて
Marshallerを書くイメージだったのでちょっと驚きました。
覚えておきましょう。
あとちょっと気になるのは、WildFlyの用意しているProtoStreamのMarshallerでカバーできなかったりするケースがあるかどうかですが。
どうなんでしょうね?