maven-shard-plugin使用体験


0.
maven管理項目を使うと便利ですが、依存が多くなるにつれて、ますます面倒になります.
一番頭が痛いのは衝突に依存して最終的に使う方が解決できますが、sdkの提供側としてはもっと面倒です.うまくいかないと使用者dissになります.
このような問題を解決するのに非常に適している不思議なmavenプラグイン、Maven-shard-pluginが最近発見されました.
1.maven-shard-pluginを使用する
直接的に前の例のpom:

    4.0.0

    scyuan.maven
    shade-example
    1.0-SNAPSHOT

    shade-example

    
        UTF-8
    

    
        
            redis.clients
            jedis
            2.8.0
        
        
            junit
            junit
            4.11
            test
        
    

    
        
            
                org.apache.maven.plugins
                maven-shade-plugin
                3.1.1
                
                    
                        
                            redis.clients:jedis
                            org.apache.commons:commons-pool2
                        
                    
                    
                        
                            *:*
                            
                                META-INF/*.SF
                                META-INF/*.DSA
                                META-INF/*.RSA
                            
                        
                    
                    
                        
                            redis
                            scyuan.maven.shaded.redis
                        
                        
                            org.apache.commons
                            scyuan.maven.shaded.org.apache.commons
                        
                    
                
                
                    
                        package
                        
                            shade
                        
                    
                
            
        
    

この例では、悪名高いjedisをjarパケットに打ち込み、パケット名プレフィックスをscyuan.maven.shaded.redisに変更すると、sdkの使用者は、明示的に2.2.0バージョンのjedisに依存してもsdkに壊滅的な影響を与えることはない.
簡単にメインクラスを書いて、カバンを作ってみます.
package scyuan.maven;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class App {
    public static void main(String[] args) {
        String key = "hello";
        String value = "redis";

        JedisPoolConfig config = new JedisPoolConfig();
        JedisPool jedisPool = new JedisPool(config, "127.0.0.1", 6379);

        try (Jedis jedis = jedisPool.getResource()) {
            jedis.set(key, value);
            System.out.println(jedis.get(key));
        } catch (Exception e) {
            System.out.println(e.toString());
        }

        jedisPool.close();
    }
}
作ったshade-example-1.0-SNAPSHOT.jarのカバンを解凍してみます.
➜  target tree -L 5 shade-example-1.0-SNAPSHOT
shade-example-1.0-SNAPSHOT
├── META-INF
│   ├── LICENSE.txt
│   ├── MANIFEST.MF
│   ├── NOTICE.txt
│   └── maven
│       ├── org.apache.commons
│       │   └── commons-pool2
│       │       ├── pom.properties
│       │       └── pom.xml
│       ├── redis.clients
│       │   └── jedis
│       │       ├── pom.properties
│       │       └── pom.xml
│       └── scyuan.maven
│           └── shade-example
│               ├── pom.properties
│               └── pom.xml
└── scyuan
    └── maven
        ├── App.class
        └── shaded
            ├── org
            │   └── apache
            └── redis
                └── clients

15 directories, 10 files

jedisおよびcommons-pool2は、私たちの意思に従って対応する位置に現れた.
実行してみます.
➜  target java -cp shade-example-1.0-SNAPSHOT.jar:jedis-2.2.0.jar:commons-pool-1.6.jar scyuan.maven.App
scyuan.maven.shaded.redis
出力が予想に合わない以外は正常です.
2.maven-shard-pluginの文字列に対する処理
コードString value = "redis";の文字列redisscyuan.maven.shaded.redisに置き換えられているように見える.
どうすればいいですか?relocationのpatternをもっと正確に制限する必要があるようです.例えばredis.clients.
もう一度試してみます.問題は確かに解決されました.ただし、コードがString value = "redis.clients";となると、前の問題が繰り返されますので、これは注意事項です.
私たちはmaven-shard-pluginのソースコードを見てみたいです.
全体としてはasmを使うことです.関連するRelocatorRemapperを見つけました.
// org/apache/maven/plugins/shade/DefaultShader.java#L564

    static class RelocatorRemapper
        extends Remapper
    {

        private final Pattern classPattern = Pattern.compile( "(\\[*)?L(.+);" );

        List relocators;

        RelocatorRemapper( List relocators )
        {
            this.relocators = relocators;
        }

        public boolean hasRelocators()
        {
            return !relocators.isEmpty();
        }

        public Object mapValue( Object object )
        {
            if ( object instanceof String )
            {
                String name = (String) object;
                String value = name;

                String prefix = "";
                String suffix = "";

                Matcher m = classPattern.matcher( name );
                if ( m.matches() )
                {
                    prefix = m.group( 1 ) + "L";
                    suffix = ";";
                    name = m.group( 2 );
                }

                for ( Relocator r : relocators )
                {
                    if ( r.canRelocateClass( name ) )
                    {
                        value = prefix + r.relocateClass( name ) + suffix;
                        break;
                    }
                    else if ( r.canRelocatePath( name ) )
                    {
                        value = prefix + r.relocatePath( name ) + suffix;
                        break;
                    }
                }

                return value;
            }

            return super.mapValue( object );
        }

        public String map( String name )
        {
            String value = name;

            String prefix = "";
            String suffix = "";

            Matcher m = classPattern.matcher( name );
            if ( m.matches() )
            {
                prefix = m.group( 1 ) + "L";
                suffix = ";";
                name = m.group( 2 );
            }

            for ( Relocator r : relocators )
            {
                if ( r.canRelocatePath( name ) )
                {
                    value = prefix + r.relocatePath( name ) + suffix;
                    break;
                }
            }

            return value;
        }

    }
mapValueの方法は、ハードコードのクラス名など、Stringを特殊処理する必要があります.
3.その他の資料
  • Apache Maven Shade Plugin