CLOVER🍀

That was when it all began.

WildFlyのInfinispanサブシステムでProtoStreamが使われているようになっていたという話

これは、なにをしたくて書いたもの?

前に、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の定義にフォーカスして見るとこんな感じですね。

  • ejbPROTOSTREAM
  • webPROTOSTREAM
  • serverPROTOSTREAM
  • hibernateJBOSS

WildFly Model Referenceのcache-containerおよびremote-cache-containerの定義を見ると、marshaller属性にはLEGACY
JBOSSPROTOSTREAMのいずれかを指定できます。なにも指定していない場合はLEGACYを選択したことになります。

それぞれの値の説明は、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のドキュメントを見てみます。

Encoding Infinispan caches and marshalling data / Configuring cache encoding / Marshalled Java objects

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.

Encoding Infinispan caches and marshalling data / Using alternative and custom marshaller implementations / Using JBoss Marshalling

となると、LEGACYJava Serializationのことなのかな?と短絡的に思ったりもしたのですが、そうではないようです。

WildFlyでのInfinispanサブシステムのMarshallerの実体

話をWildFlyのInfinispanサブシステムに戻して。

Embedded、Hot RodそれぞれのMarshallerの定義(LEGACYJBOSSPROTOSTREAM)に応じてどのようなMarshallerが
選択されるのかを見てみます。

Embedded。

https://github.com/wildfly/wildfly/blob/26.1.1.Final/clustering/infinispan/spi/src/main/java/org/wildfly/clustering/infinispan/spi/marshalling/InfinispanMarshallerFactory.java

Hot Rod。

https://github.com/wildfly/wildfly/blob/26.1.1.Final/clustering/infinispan/client/src/main/java/org/wildfly/clustering/infinispan/client/marshaller/HotRodMarshallerFactory.java

両者で微妙に定義が異なりますが、PROTOSTREAMはProtoStream、JBOSSJBoss Marshalling、そしてLEGACY
条件によってPROTOSTREAMJBOSSを選択する、ということになっています。

そもそも、Java標準のシリアライズは使われないんですね。

ここで使われているMarshallerFactory

https://github.com/wildfly/wildfly/blob/26.1.1.Final/clustering/infinispan/marshalling/src/main/java/org/wildfly/clustering/infinispan/marshalling/MarshallerFactory.java

そして、各Marshaller。

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

このモジュール内を見ると、その量にけっこう驚いたりするのですが。

https://github.com/wildfly/wildfly/tree/26.1.1.Final/clustering/marshalling/protostream/src/main/java/org/wildfly/clustering/marshalling/protostream

Javaの標準ライブラリの一部をカバーするMarshallerが書かれています。以下は、その一部です。

https://github.com/wildfly/wildfly/tree/26.1.1.Final/clustering/marshalling/protostream/src/main/java/org/wildfly/clustering/marshalling/protostream/util

https://github.com/wildfly/wildfly/tree/26.1.1.Final/clustering/marshalling/protostream/src/main/java/org/wildfly/clustering/marshalling/protostream/time

以下が含まれていそうですね。

  • java.lang
  • java.math
  • java.net
  • java.sql
  • java.util
  • java.util.concurrent
  • java.util.concurrent.atomic
  • java.time

Collectionに対するMarshaller。

https://github.com/wildfly/wildfly/blob/26.1.1.Final/clustering/marshalling/protostream/src/main/java/org/wildfly/clustering/marshalling/protostream/util/CollectionMarshaller.java

public class CollectionMarshaller<T extends Collection<Object>> extends AbstractCollectionMarshaller<T> {

各パッケージ内にMarshallerProviderというenumがあり、こちらでMarshallerをまとめて取り扱っているようです。

https://github.com/wildfly/wildfly/blob/26.1.1.Final/clustering/marshalling/protostream/src/main/java/org/wildfly/clustering/marshalling/protostream/util/UtilMarshallerProvider.java

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を利用するような感じなのかなと思ったり。

https://github.com/wildfly/wildfly/blob/26.1.1.Final/clustering/marshalling/protostream/src/main/java/org/wildfly/clustering/marshalling/protostream/ProtoStreamByteBufferMarshaller.java#L61-L82

    @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ファイルを扱っている箇所がありました。

https://github.com/wildfly/wildfly/blob/26.1.1.Final/clustering/marshalling/protostream/src/main/java/org/wildfly/clustering/marshalling/protostream/AbstractSerializationContextInitializer.java#L49-L52

    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も含まれていますね。

https://github.com/wildfly/wildfly/blob/26.1.1.Final/clustering/web/infinispan/src/main/java/org/wildfly/clustering/web/infinispan/session/SimpleSessionExpirationMetaDataMarshaller.java

public class SimpleSessionExpirationMetaDataMarshaller implements ProtoStreamMarshaller<SimpleSessionExpirationMetaData> {

となると、これらのMarshallerをどうやって検出しているのかという疑問も出てきます。

ちょっと探していると、このあたりでService Loaderの仕組みでSerializationContextInitializerをロードしているようです。

https://github.com/wildfly/wildfly/blob/26.1.1.Final/clustering/marshalling/protostream/src/main/java/org/wildfly/clustering/marshalling/protostream/SerializationContextBuilder.java#L117-L134

    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を登録している箇所があります。

https://github.com/wildfly/wildfly/blob/26.1.1.Final/clustering/web/cache/src/main/java/org/wildfly/clustering/web/cache/session/SessionSerializationContextInitializer.java

@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ファイルを
作成する仕組みはこちらを使っていそうですね。

META-INF/services generator -

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をロードして、

https://github.com/wildfly/wildfly/blob/26.1.1.Final/clustering/server/src/main/java/org/wildfly/clustering/server/ServerSerializationContextInitializer.java

@MetaInfServices(SerializationContextInitializer.class)
public class ServerSerializationContextInitializer extends CompositeSerializationContextInitializer {

    public ServerSerializationContextInitializer() {
        super(ServerSerializationContextInitializerProvider.class);
    }
}

実際のMarshallerを把握しているのはSerializationContextInitializerProvider、というパターンもあるようです。

https://github.com/wildfly/wildfly/blob/26.1.1.Final/clustering/server/src/main/java/org/wildfly/clustering/server/ServerSerializationContextInitializerProvider.java

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は、以下で共通的に登録されているようです。

https://github.com/wildfly/wildfly/blob/26.1.1.Final/clustering/marshalling/protostream/src/main/java/org/wildfly/clustering/marshalling/protostream/SerializationContextBuilder.java#L52-L53

    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の方のモジュールは、割とあっさりしています。

https://github.com/wildfly/wildfly/tree/26.1.1.Final/clustering/marshalling/jboss/src/main/java/org/wildfly/clustering/marshalling/jboss

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

encodingJBoss 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は、encodingJBoss 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ではありません…。

https://github.com/wildfly/wildfly/blob/26.1.1.Final/clustering/web/hotrod/src/main/java/org/wildfly/clustering/web/hotrod/session/SessionCreationMetaDataKey.java

とすると、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の場合は条件によってPROTOSTREAMJBOSSを選択する、となっていましたがこちらをよく見てみます。

https://github.com/wildfly/wildfly/blob/26.1.1.Final/clustering/infinispan/client/src/main/java/org/wildfly/clustering/infinispan/client/marshaller/HotRodMarshallerFactory.java#L41-L53

    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の方はどうでしょう。

https://github.com/wildfly/wildfly/blob/26.1.1.Final/clustering/infinispan/spi/src/main/java/org/wildfly/clustering/infinispan/spi/marshalling/InfinispanMarshallerFactory.java#L45-L57

    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);
        }
    },

serverejbwebですね。

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でカバーできなかったりするケースがあるかどうかですが。
どうなんでしょうね?