This is an automated email from the ASF dual-hosted git repository. michaelo pushed a commit to branch MRESOLVER-131 in repository https://gitbox.apache.org/repos/asf/maven-resolver.git
commit d045b4d4b80971b468230b45e8bf09fc6c9cc422 Author: Michael Osipov <micha...@apache.org> AuthorDate: Sun Aug 9 20:05:53 2020 +0200 [MRESOLVER-131] Introduce a Redisson-based SyncContextFactory This closes #68 --- maven-resolver-synccontext-redisson/pom.xml | 100 ++++++ .../aether/internal/impl/TrackingFileManager.java | 151 ++++++++ .../synccontext/RedissonSyncContextFactory.java | 390 +++++++++++++++++++++ .../src/site/markdown/index.md.vm | 105 ++++++ .../src/site/site.xml | 37 ++ pom.xml | 1 + 6 files changed, 784 insertions(+) diff --git a/maven-resolver-synccontext-redisson/pom.xml b/maven-resolver-synccontext-redisson/pom.xml new file mode 100644 index 0000000..bc069ac --- /dev/null +++ b/maven-resolver-synccontext-redisson/pom.xml @@ -0,0 +1,100 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +--> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.apache.maven.resolver</groupId> + <artifactId>maven-resolver</artifactId> + <version>1.5.1-SNAPSHOT</version> + </parent> + + <artifactId>maven-resolver-synccontext-redisson</artifactId> + + <name>Maven Artifact Resolver Sync Context Redisson</name> + <description> + A synchronization context implementation using Redisson distributed locks. + </description> + + <properties> + <javaVersion>8</javaVersion> + <Automatic-Module-Name>org.apache.maven.resolver.synccontext.redisson</Automatic-Module-Name> + <Bundle-SymbolicName>${Automatic-Module-Name}</Bundle-SymbolicName> + </properties> + + <dependencies> + <dependency> + <groupId>org.apache.maven.resolver</groupId> + <artifactId>maven-resolver-api</artifactId> + </dependency> + <dependency> + <groupId>org.apache.maven.resolver</groupId> + <artifactId>maven-resolver-impl</artifactId> + </dependency> + <dependency> + <groupId>org.apache.maven.resolver</groupId> + <artifactId>maven-resolver-util</artifactId> + </dependency> + <dependency> + <groupId>javax.inject</groupId> + <artifactId>javax.inject</artifactId> + <scope>provided</scope> + <optional>true</optional> + </dependency> + <dependency> + <groupId>javax.annotation</groupId> + <artifactId>javax.annotation-api</artifactId> + <version>1.3.2</version> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </dependency> + <dependency> + <groupId>org.redisson</groupId> + <artifactId>redisson</artifactId> + <version>3.13.3</version> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.eclipse.sisu</groupId> + <artifactId>sisu-maven-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <configuration> + <archive> + <manifestFile>${project.build.directory}/osgi/MANIFEST.MF</manifestFile> + </archive> + </configuration> + </plugin> + </plugins> + </build> +</project> diff --git a/maven-resolver-synccontext-redisson/src/main/java/org/eclipse/aether/internal/impl/TrackingFileManager.java b/maven-resolver-synccontext-redisson/src/main/java/org/eclipse/aether/internal/impl/TrackingFileManager.java new file mode 100644 index 0000000..ffc200f --- /dev/null +++ b/maven-resolver-synccontext-redisson/src/main/java/org/eclipse/aether/internal/impl/TrackingFileManager.java @@ -0,0 +1,151 @@ +package org.eclipse.aether.internal.impl; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.eclipse.aether.SyncContext; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.Map; +import java.util.Properties; + +/** + * Manages access to a properties file. This override drops internal synchronization becauses it + * relies on external synchronization provided by outer {@link SyncContext} instances. + */ +class TrackingFileManager +{ + + private static final Logger LOGGER = LoggerFactory.getLogger( TrackingFileManager.class ); + + public Properties read( File file ) + { + FileInputStream stream = null; + try + { + if ( !file.exists() ) + { + return null; + } + + stream = new FileInputStream( file ); + + Properties props = new Properties(); + props.load( stream ); + + return props; + } + catch ( IOException e ) + { + LOGGER.warn( "Failed to read tracking file {}", file, e ); + } + finally + { + close( stream, file ); + } + + return null; + } + + public Properties update( File file, Map<String, String> updates ) + { + Properties props = new Properties(); + + File directory = file.getParentFile(); + if ( !directory.mkdirs() && !directory.exists() ) + { + LOGGER.warn( "Failed to create parent directories for tracking file {}", file ); + return props; + } + + RandomAccessFile raf = null; + try + { + raf = new RandomAccessFile( file, "rw" ); + + if ( file.canRead() ) + { + byte[] buffer = new byte[(int) raf.length()]; + + raf.readFully( buffer ); + + ByteArrayInputStream stream = new ByteArrayInputStream( buffer ); + + props.load( stream ); + } + + for ( Map.Entry<String, String> update : updates.entrySet() ) + { + if ( update.getValue() == null ) + { + props.remove( update.getKey() ); + } + else + { + props.setProperty( update.getKey(), update.getValue() ); + } + } + + ByteArrayOutputStream stream = new ByteArrayOutputStream( 1024 * 2 ); + + LOGGER.debug( "Writing tracking file {}", file ); + props.store( stream, "NOTE: This is a Maven Resolver internal implementation file" + + ", its format can be changed without prior notice." ); + + raf.seek( 0 ); + raf.write( stream.toByteArray() ); + raf.setLength( raf.getFilePointer() ); + } + catch ( IOException e ) + { + LOGGER.warn( "Failed to write tracking file {}", file, e ); + } + finally + { + close( raf, file ); + } + + return props; + } + + private void close( Closeable closeable, File file ) + { + if ( closeable != null ) + { + try + { + closeable.close(); + } + catch ( IOException e ) + { + LOGGER.warn( "Error closing tracking file {}", file, e ); + } + } + } + +} diff --git a/maven-resolver-synccontext-redisson/src/main/java/org/eclipse/aether/synccontext/RedissonSyncContextFactory.java b/maven-resolver-synccontext-redisson/src/main/java/org/eclipse/aether/synccontext/RedissonSyncContextFactory.java new file mode 100644 index 0000000..4bdcf75 --- /dev/null +++ b/maven-resolver-synccontext-redisson/src/main/java/org/eclipse/aether/synccontext/RedissonSyncContextFactory.java @@ -0,0 +1,390 @@ +package org.eclipse.aether.synccontext; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collection; +import java.util.Collections; +import java.util.Deque; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.TreeSet; + +import javax.annotation.PreDestroy; +import javax.annotation.Priority; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.SyncContext; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.impl.SyncContextFactory; +import org.eclipse.aether.metadata.Metadata; +import org.eclipse.aether.util.ChecksumUtils; +import org.eclipse.aether.util.ConfigUtils; +import org.redisson.Redisson; +import org.redisson.api.RLock; +import org.redisson.api.RReadWriteLock; +import org.redisson.api.RedissonClient; +import org.redisson.config.Config; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A singleton factory to create synchronization contexts using Redisson's {@link RReadWriteLock}. + * It locks fine-grained with groupId, artifactId and version if required. + * <p> + * <strong>Note: This component is still considered to be experimental, use with caution!</strong> + * <h2>Configuration</h2> + * You can configure various aspects of this factory. + * + * <h3>Redisson Client</h3> + * To fully configure the Redisson client, this factory uses the following staggered approach: + * <ol> + * <li>If the property {@code aether.syncContext.redisson.configFile} is set and the file at that + * specific path does exist, load it otherwise an exception is thrown.</li> + * <li>If no configuration file path is provided, load default from + * <code>${maven.conf}/maven-resolver-redisson.yaml</code>, but ignore if it does not exist.</li> + * <li>If no configuration file is available at all, Redisson is configured with a single server pointing + * to {@code redis://localhost:6379} with client name {@code maven-resolver}.</li> + * </ol> + * Please note that an invalid confguration file results in an exception too. + * + * <h3>Discrimination</h3> + * You may freely use a single Redis instance to serve multiple Maven instances, on multiple hosts + * with shared or exclusive local repositories. Every sync context instance will generate a unique + * discriminator which identifies each host paired with the local repository currently accessed. + * The following staggered approach is used: + * <ol> + * <li>Determine hostname, if not possible use {@code localhost}.</li> + * <li>If the property {@code aether.syncContext.redisson.discriminator} is set, use it and skip + * the remaining steps.</li> + * <li>Concat hostname with the path of the local repository: <code>${hostname}:${maven.repo.local}</code>.</li> + * <li>Calculate the SHA-1 digest of this value. If that fails use the static digest of an empty string.</li> + * </ol> + * + * <h2>Key Composition</h2> + * Each lock is assigned a unique key in the configured Redis instance which has the following pattern: + * <code>maven:resolver:${discriminator}:${artifact|metadata}</code>. + * <ul> + * <li><code>${artifact}</code> will + * always resolve to <code>artifact:${groupId}:${artifactId}:${baseVersion}</code>.</li> + * <li><code>${metadata}</code> will resolve to one of <code>metadata:${groupId}:${artifactId}:${version}</code>, + * <code>metadata:${groupId}:${artifactId}</code>, <code>metadata:${groupId}</code>, + * <code>metadata:</code>.</li> + * </ul> + */ +@Named +@Priority( Integer.MAX_VALUE ) +@Singleton +public class RedissonSyncContextFactory + implements SyncContextFactory +{ + + private static final String DEFAULT_CONFIG_FILE_NAME = "maven-resolver-redisson.yaml"; + private static final String DEFAULT_REDIS_ADDRESS = "redis://localhost:6379"; + private static final String DEFAULT_CLIENT_NAME = "maven-resolver"; + private static final String DEFAULT_HOSTNAME = "localhost"; + private static final String DEFAULT_DISCRIMINATOR_DIGEST = "da39a3ee5e6b4b0d3255bfef95601890afd80709"; + + private static final String CONFIG_PROP_CONFIG_FILE = "aether.syncContext.redisson.configFile"; + + private static final Logger LOGGER = LoggerFactory.getLogger( RedissonSyncContextFactory.class ); + + // We are in a singleton so these should exist only once! + private RedissonClient redissonClient; + private String hostname; + + public RedissonSyncContextFactory() + { + // TODO These two log statements will go away + LOGGER.trace( "TCCL: {}", Thread.currentThread().getContextClassLoader() ); + LOGGER.trace( "CCL: {}", getClass().getClassLoader() ); + this.redissonClient = createRedissonClient(); + this.hostname = getHostname(); + } + + private RedissonClient createRedissonClient() + { + Path configFilePath = null; + + String configFile = ConfigUtils.getString( System.getProperties(), null, CONFIG_PROP_CONFIG_FILE ); + if ( configFile != null && !configFile.isEmpty() ) + { + configFilePath = Paths.get( configFile ); + if ( Files.notExists( configFilePath ) ) + { + throw new IllegalArgumentException( "The specified Redisson config file does not exist: " + + configFilePath ); + } + } + + if ( configFilePath == null ) + { + String mavenConf = ConfigUtils.getString( System.getProperties(), null, "maven.conf" ); + if ( mavenConf != null && !mavenConf.isEmpty() ) + { + configFilePath = Paths.get( mavenConf, DEFAULT_CONFIG_FILE_NAME ); + if ( Files.notExists( configFilePath ) ) + { + configFilePath = null; + } + } + } + + Config config = null; + + if ( configFilePath != null ) + { + LOGGER.trace( "Reading Redisson config file from '{}'", configFilePath ); + try ( InputStream is = Files.newInputStream( configFilePath ) ) + { + config = Config.fromYAML( is ); + } + catch ( IOException e ) + { + throw new IllegalStateException( "Failed to read Redisson config file: " + configFilePath, e ); + } + } + else + { + config = new Config(); + config.useSingleServer() + .setAddress( DEFAULT_REDIS_ADDRESS ) + .setClientName( DEFAULT_CLIENT_NAME ); + } + + RedissonClient redissonClient = Redisson.create( config ); + LOGGER.trace( "Created Redisson client with id '{}'", redissonClient.getId() ); + + return redissonClient; + } + + private String getHostname() + { + try + { + return InetAddress.getLocalHost().getHostName(); + } + catch ( UnknownHostException e ) + { + LOGGER.warn( "Failed to get hostname, using '{}'", + DEFAULT_HOSTNAME, e ); + return DEFAULT_HOSTNAME; + } + } + + public SyncContext newInstance( RepositorySystemSession session, boolean shared ) + { + // This log statement will go away + LOGGER.trace( "Instance: {}", this ); + return new RedissonSyncContext( session, hostname, redissonClient, shared ); + } + + @PreDestroy + public void shutdown() + { + LOGGER.trace( "Shutting down Redisson client with id '{}'", redissonClient.getId() ); + redissonClient.shutdown(); + } + + static class RedissonSyncContext + implements SyncContext + { + + private static final String CONFIG_PROP_DISCRIMINATOR = "aether.syncContext.redisson.discriminator"; + + private static final String KEY_PREFIX = "maven:resolver:"; + + private static final Logger LOGGER = LoggerFactory.getLogger( RedissonSyncContext.class ); + + private final RepositorySystemSession session; + private final String hostname; + private final RedissonClient redissonClient; + private final boolean shared; + private final Map<String, RReadWriteLock> locks = new LinkedHashMap<>(); + + private RedissonSyncContext( RepositorySystemSession session, String hostname, + RedissonClient redissonClient, boolean shared ) + { + this.session = session; + this.hostname = hostname; + this.redissonClient = redissonClient; + this.shared = shared; + } + + public void acquire( Collection<? extends Artifact> artifacts, + Collection<? extends Metadata> metadatas ) + { + // Deadlock prevention: https://stackoverflow.com/a/16780988/696632 + // We must acquire multiple locks always in the same order! + Collection<String> keys = new TreeSet<>(); + if ( artifacts != null ) + { + for ( Artifact artifact : artifacts ) + { + // TODO Should we include extension and classifier too? + String key = "artifact:" + artifact.getGroupId() + ":" + + artifact.getArtifactId() + ":" + artifact.getBaseVersion(); + keys.add( key ); + } + } + + if ( metadatas != null ) + { + for ( Metadata metadata : metadatas ) + { + StringBuilder key = new StringBuilder( "metadata:" ); + if ( !metadata.getGroupId().isEmpty() ) + { + key.append( metadata.getGroupId() ); + if ( !metadata.getArtifactId().isEmpty() ) + { + key.append( ':' ).append( metadata.getArtifactId() ); + if ( !metadata.getVersion().isEmpty() ) + { + key.append( ':' ).append( metadata.getVersion() ); + } + } + } + keys.add( key.toString() ); + } + } + + if ( keys.isEmpty() ) + { + return; + } + + String discriminator = createDiscriminator(); + LOGGER.trace( "Using Redis key discriminator '{}' during this session", discriminator ); + + LOGGER.trace( "Need {} {} lock(s) for {}", keys.size(), shared ? "read" : "write", keys ); + int acquiredLockCount = 0; + int reacquiredLockCount = 0; + for ( String key : keys ) + { + RReadWriteLock rwLock = locks.get( key ); + if ( rwLock == null ) + { + rwLock = redissonClient + .getReadWriteLock( KEY_PREFIX + discriminator + ":" + key ); + locks.put( key, rwLock ); + acquiredLockCount++; + } + else + { + reacquiredLockCount++; + } + + RLock actualLock = shared ? rwLock.readLock() : rwLock.writeLock(); + // Avoid #getHoldCount() and #isLocked() roundtrips when we are not logging + if ( LOGGER.isTraceEnabled() ) + { + LOGGER.trace( "Acquiring {} lock for '{}' (currently held: {}, already locked: {})", + shared ? "read" : "write", key, actualLock.getHoldCount(), + actualLock.isLocked() ); + } + // If this still produces a deadlock we might need to switch to #tryLock() with n attempts + actualLock.lock(); + } + LOGGER.trace( "Total new locks acquired: {}, total existing locks reacquired: {}", + acquiredLockCount, reacquiredLockCount ); + } + + private String createDiscriminator() + { + String discriminator = ConfigUtils.getString( session, null, CONFIG_PROP_DISCRIMINATOR ); + + if ( discriminator == null || discriminator.isEmpty() ) + { + + File basedir = session.getLocalRepository().getBasedir(); + discriminator = hostname + ":" + basedir; + try + { + Map<String, Object> checksums = ChecksumUtils.calc( + discriminator.toString().getBytes( StandardCharsets.UTF_8 ), + Collections.singletonList( "SHA-1" ) ); + Object checksum = checksums.get( "SHA-1" ); + + if ( checksum instanceof Exception ) + { + throw (Exception) checksum; + } + + return String.valueOf( checksum ); + } + catch ( Exception e ) + { + // TODO Should this be warn? + LOGGER.trace( "Failed to calculate discriminator digest, using '{}'", + DEFAULT_DISCRIMINATOR_DIGEST, e ); + return DEFAULT_DISCRIMINATOR_DIGEST; + } + } + + return discriminator; + } + + public void close() + { + if ( locks.isEmpty() ) + { + return; + } + + // Release locks in reverse insertion order + Deque<String> keys = new LinkedList<>( locks.keySet() ); + Iterator<String> keysIter = keys.descendingIterator(); + while ( keysIter.hasNext() ) + { + String key = keysIter.next(); + RReadWriteLock rwLock = locks.get( key ); + RLock actualLock = shared ? rwLock.readLock() : rwLock.writeLock(); + while ( actualLock.getHoldCount() > 0 ) + { + // Avoid #getHoldCount() roundtrips when we are not logging + if ( LOGGER.isTraceEnabled() ) + { + LOGGER.trace( "Releasing {} lock for '{}' (currently held: {})", + shared ? "read" : "write", key, actualLock.getHoldCount() ); + } + actualLock.unlock(); + } + } + // TODO Should we count reentrant ones too? + LOGGER.trace( "Total locks released: {}", locks.size() ); + locks.clear(); + } + + } + +} diff --git a/maven-resolver-synccontext-redisson/src/site/markdown/index.md.vm b/maven-resolver-synccontext-redisson/src/site/markdown/index.md.vm new file mode 100644 index 0000000..7f1f004 --- /dev/null +++ b/maven-resolver-synccontext-redisson/src/site/markdown/index.md.vm @@ -0,0 +1,105 @@ +${esc.hash} Redisson Sync Context for Maven Resolver + +<!-- +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +--> + +<span style="color: red; font-size: 16pt">***Note***: *This component is still considered to be experimental, use with caution!*</span> + +The Redisson Sync Context Factory is a Redisson-based distributed locks factory for Maven Resolver +on top of Redis to provide a fast, concurrent-safe access from one or multiple Maven instances to the +same local Maven repository. + +For further details about the factory read the [Javadoc](./apidocs/org/eclipse/aether/synccontext/RedissonSyncContextFactory.html). + +${esc.hash}${esc.hash} Open Issues/Notes + +- It only works when dependency injection is used and not the bundled `AetherModule` or + `ServiceLocator` (Maven uses dependency injection). +- It includes a lot of trace logging which partially will go way as soon as it has been stabilized. +- Usage from plugins has not been tested yet. +- The `furnace-maven-plugin` does not work this implementation because it uses `ServiceLocator` instead + of dependency injection. +- The installation process has not been streamlined yet. + +${esc.hash}${esc.hash} Installation/Testing + +- Clone Maven Resolver and perform `mvn install`. +- Clone Maven, change the Resolver version in Maven's parent POM to `${project.version}` and perform `mvn install`. +- Extract `apache-maven-3.7.0-SNAPSHOT-bin.tar.gz` to a location of your choice. +- Modify `${maven.home}/bin/m2.conf` by adding `optionally ${maven.home}/lib/ext/redisson/*.jar` + right after the `${maven.home}/conf/logging` line. +- Add/modify the following entries in `${maven.home}/conf/logging/simplelogger.properties`: + ``` + org.slf4j.simpleLogger.showDateTime=true + org.slf4j.simpleLogger.showThreadName=true + org.slf4j.simpleLogger.showShortLogName=true + org.slf4j.simpleLogger.log.org.eclipse.aether=trace + #org.slf4j.simpleLogger.log.org.redisson=debug + #org.slf4j.simpleLogger.log.io.netty=debug + ``` +- Go back to Resolver and run `mvn dependency:copy-dependencies -pl maven-resolver-synccontext-redisson`. +- Copy the following dependencies from `maven-resolver-synccontext-redisson/target/dependency` + to `${maven.home}/lib/ext/redisson/`: + ``` + ├── byte-buddy-1.10.7.jar + ├── cache-api-1.0.0.jar + ├── jackson-annotations-2.11.1.jar + ├── jackson-core-2.11.1.jar + ├── jackson-databind-2.11.1.jar + ├── jackson-dataformat-yaml-2.11.1.jar + ├── javax.annotation-api-1.3.2.jar + ├── jboss-marshalling-2.0.9.Final.jar + ├── jboss-marshalling-river-2.0.9.Final.jar + ├── jodd-bean-5.0.13.jar + ├── jodd-core-5.0.13.jar + ├── maven-resolver-synccontext-redisson-${project.version}.jar + ├── netty-buffer-4.1.51.Final.jar + ├── netty-codec-4.1.51.Final.jar + ├── netty-codec-dns-4.1.51.Final.jar + ├── netty-common-4.1.51.Final.jar + ├── netty-handler-4.1.51.Final.jar + ├── netty-resolver-4.1.51.Final.jar + ├── netty-resolver-dns-4.1.51.Final.jar + ├── netty-transport-4.1.51.Final.jar + ├── reactive-streams-1.0.3.jar + ├── reactor-core-3.3.4.RELEASE.jar + ├── redisson-3.13.3.jar + ├── rxjava-2.2.19.jar + └── snakeyaml-1.26.jar + ``` + Dependencies which are already bundled with Maven have been omitted. +- Start your Redis instance on a machine of your choice (ideally `localhost`). +- Now start a multithreaded Maven (`3.7.0-SNAPSHOT`) build on your project and you should see at least these lines: + ``` + # This line does not appear for the default configuration + 2316 [main] [TRACE] RedissonSyncContextFactory - Reading Redisson config file from '${maven.home}/conf/maven-resolver-redisson.yaml' + 4626 [main] [TRACE] RedissonSyncContextFactory - Created Redisson client with id '1c8db59b-7939-4014-8506-ae841c74608c' + 35318 [main] [TRACE] RedissonSyncContextFactory - Shutting down Redisson client with id '1c8db59b-7939-4014-8506-ae841c74608c' + ``` + +${esc.hash}${esc.hash} Configuration Options + +Option | Type | Description | Default Value +--- | --- | --- | --- | --- +`aether.syncContext.redisson.configFile` | String | Path to a Redisson configuration file in YAML format. Read [official documentation](https://github.com/redisson/redisson/wiki/2.-Configuration) for details. | `${maven.home}/conf/maven-resolver-redisson.yaml` +`aether.syncContext.redisson.discriminator` | String | A discriminator uniquely identifying a host and repository pair. If the generation of the default value fails, it will use `sha1('')`. | `sha1('${esc.dollar}{hostname:-localhost}:${maven.repo.local}')` + +${esc.hash}${esc.hash} Set Configuration from Apache Maven + +To set one of the configuration options from above just use system variables. diff --git a/maven-resolver-synccontext-redisson/src/site/site.xml b/maven-resolver-synccontext-redisson/src/site/site.xml new file mode 100644 index 0000000..0dd2b23 --- /dev/null +++ b/maven-resolver-synccontext-redisson/src/site/site.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +--> + +<project xmlns="http://maven.apache.org/DECORATION/1.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/DECORATION/1.0.0 http://maven.apache.org/xsd/decoration-1.0.0.xsd" + name="RedissonSyncContext"> + <body> + <menu name="Overview"> + <item name="Introduction" href="index.html"/> + <item name="Javadoc" href="apidocs/index.html"/> + <item name="Source Xref" href="xref/index.html"/> + <!--item name="FAQ" href="faq.html"/--> + </menu> + + <menu ref="parent"/> + <menu ref="reports"/> + </body> +</project> diff --git a/pom.xml b/pom.xml index 39bddc7..4d70f4a 100644 --- a/pom.xml +++ b/pom.xml @@ -517,6 +517,7 @@ </activation> <modules> <module>maven-resolver-synccontext-global</module> + <module>maven-resolver-synccontext-redisson</module> </modules> </profile> </profiles>