これは、なにをしたくて書いたもの?
前に、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
…PROTOSTREAM
web
…PROTOSTREAM
server
…PROTOSTREAM
hibernate
…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.lang
java.math
java.net
java.sql
java.util
java.util.concurrent
java.util.concurrent.atomic
java.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でカバーできなかったりするケースがあるかどうかですが。
どうなんでしょうね?