001package org.eclipse.aether.internal.impl.synccontext.named;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *  http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import javax.inject.Inject;
023import javax.inject.Named;
024import javax.inject.Singleton;
025
026import java.util.ArrayList;
027import java.util.Collections;
028import java.util.HashMap;
029import java.util.Map;
030
031import org.eclipse.aether.MultiRuntimeException;
032import org.eclipse.aether.RepositorySystemSession;
033import org.eclipse.aether.impl.RepositorySystemLifecycle;
034import org.eclipse.aether.named.NamedLockFactory;
035import org.eclipse.aether.named.providers.FileLockNamedLockFactory;
036import org.eclipse.aether.named.providers.LocalReadWriteLockNamedLockFactory;
037import org.eclipse.aether.named.providers.LocalSemaphoreNamedLockFactory;
038import org.eclipse.aether.named.providers.NoopNamedLockFactory;
039import org.eclipse.aether.spi.locator.Service;
040import org.eclipse.aether.spi.locator.ServiceLocator;
041import org.eclipse.aether.util.ConfigUtils;
042import org.slf4j.Logger;
043import org.slf4j.LoggerFactory;
044
045import static java.util.Objects.requireNonNull;
046
047/**
048 * Default implementation of {@link NamedLockFactoryAdapterFactory}. This implementation creates new instances of the
049 * adapter on every call. In turn, on shutdown, it will shut down all existing named lock factories. This is merely for
050 * simplicity, to not have to track "used" named lock factories, while it exposes all available named lock factories to
051 * callers.
052 * <p>
053 * Most members and methods of this class are protected. It is meant to be extended in case of need to customize its
054 * behavior. An exception from this are private static methods, mostly meant to provide out of the box
055 * defaults and to be used when no Eclipse Sisu component container is used.
056 *
057 * @since 1.9.1
058 */
059@Singleton
060@Named
061public class NamedLockFactoryAdapterFactoryImpl implements NamedLockFactoryAdapterFactory, Service
062{
063    private static final String DEFAULT_FACTORY_NAME = LocalReadWriteLockNamedLockFactory.NAME;
064
065    private static final String DEFAULT_NAME_MAPPER_NAME = NameMappers.GAV_NAME;
066
067    private static Map<String, NamedLockFactory> getManuallyCreatedFactories()
068    {
069        HashMap<String, NamedLockFactory> factories = new HashMap<>();
070        factories.put( NoopNamedLockFactory.NAME, new NoopNamedLockFactory() );
071        factories.put( LocalReadWriteLockNamedLockFactory.NAME, new LocalReadWriteLockNamedLockFactory() );
072        factories.put( LocalSemaphoreNamedLockFactory.NAME, new LocalSemaphoreNamedLockFactory() );
073        factories.put( FileLockNamedLockFactory.NAME, new FileLockNamedLockFactory() );
074        return Collections.unmodifiableMap( factories );
075    }
076
077    private static Map<String, NameMapper> getManuallyCreatedNameMappers()
078    {
079        HashMap<String, NameMapper> mappers = new HashMap<>();
080        mappers.put( NameMappers.STATIC_NAME, NameMappers.staticNameMapper() );
081        mappers.put( NameMappers.GAV_NAME, NameMappers.gavNameMapper() );
082        mappers.put( NameMappers.DISCRIMINATING_NAME, NameMappers.discriminatingNameMapper() );
083        mappers.put( NameMappers.FILE_GAV_NAME, NameMappers.fileGavNameMapper() );
084        mappers.put( NameMappers.FILE_HGAV_NAME, NameMappers.fileHashingGavNameMapper() );
085        return Collections.unmodifiableMap( mappers );
086    }
087
088    protected static final String FACTORY_KEY = "aether.syncContext.named.factory";
089
090    protected static final String NAME_MAPPER_KEY = "aether.syncContext.named.nameMapper";
091
092    protected final Logger logger = LoggerFactory.getLogger( getClass() );
093
094    protected final Map<String, NamedLockFactory> factories;
095
096    protected final String defaultFactoryName;
097
098    protected final Map<String, NameMapper> nameMappers;
099
100    protected final String defaultNameMapperName;
101
102    /**
103     * Default constructor for non Eclipse Sisu uses.
104     *
105     * @deprecated for use in SL only.
106     */
107    @Deprecated
108    public NamedLockFactoryAdapterFactoryImpl()
109    {
110        this.factories = getManuallyCreatedFactories();
111        this.defaultFactoryName = DEFAULT_FACTORY_NAME;
112        this.nameMappers = getManuallyCreatedNameMappers();
113        this.defaultNameMapperName = DEFAULT_NAME_MAPPER_NAME;
114    }
115
116    @Override
117    public void initService( ServiceLocator locator )
118    {
119        locator.getService( RepositorySystemLifecycle.class ).addOnSystemEndedHandler( this::shutdown );
120    }
121
122    @Inject
123    public NamedLockFactoryAdapterFactoryImpl( final Map<String, NamedLockFactory> factories,
124                                               final Map<String, NameMapper> nameMappers,
125                                               final RepositorySystemLifecycle lifecycle )
126    {
127        this( factories, DEFAULT_FACTORY_NAME, nameMappers, DEFAULT_NAME_MAPPER_NAME, lifecycle );
128    }
129
130    public NamedLockFactoryAdapterFactoryImpl( final Map<String, NamedLockFactory> factories,
131                                               final String defaultFactoryName,
132                                               final Map<String, NameMapper> nameMappers,
133                                               final String defaultNameMapperName,
134                                               final RepositorySystemLifecycle lifecycle )
135    {
136        this.factories = requireNonNull( factories );
137        this.defaultFactoryName = requireNonNull( defaultFactoryName );
138        this.nameMappers = requireNonNull( nameMappers );
139        this.defaultNameMapperName = requireNonNull( defaultNameMapperName );
140        lifecycle.addOnSystemEndedHandler( this::shutdown );
141
142        logger.debug( "Created adapter factory; available factories {}; available name mappers {}",
143                factories.keySet(), nameMappers.keySet() );
144    }
145
146    /**
147     * Current implementation simply delegates to {@link #createAdapter(RepositorySystemSession)}.
148     */
149    @Override
150    public NamedLockFactoryAdapter getAdapter( RepositorySystemSession session )
151    {
152        return createAdapter( session );
153    }
154
155    /**
156     * Creates a new adapter instance, never returns {@code null}.
157     */
158    protected NamedLockFactoryAdapter createAdapter( RepositorySystemSession session )
159    {
160        final String nameMapperName = requireNonNull( getNameMapperName( session ) );
161        final String factoryName = requireNonNull( getFactoryName( session ) );
162        final NameMapper nameMapper = selectNameMapper( nameMapperName );
163        final NamedLockFactory factory = selectFactory( factoryName );
164        logger.debug( "Creating adapter using nameMapper '{}' and factory '{}'",
165                nameMapperName, factoryName );
166        return new NamedLockFactoryAdapter( nameMapper, factory );
167    }
168
169    /**
170     * Returns the selected (user configured or default) named lock factory name, never {@code null}.
171     */
172    protected String getFactoryName( RepositorySystemSession session )
173    {
174        return ConfigUtils.getString( session, getDefaultFactoryName(), FACTORY_KEY );
175    }
176
177    /**
178     * Returns the default named lock factory name, never {@code null}.
179     */
180    protected String getDefaultFactoryName()
181    {
182        return defaultFactoryName;
183    }
184
185    /**
186     * Returns the selected (user configured or default) name mapper name, never {@code null}.
187     */
188    protected String getNameMapperName( RepositorySystemSession session )
189    {
190        return ConfigUtils.getString( session, getDefaultNameMapperName(), NAME_MAPPER_KEY );
191    }
192
193    /**
194     * Returns the default name mapper name, never {@code null}.
195     */
196    protected String getDefaultNameMapperName()
197    {
198        return defaultNameMapperName;
199    }
200
201    /**
202     * Selects a named lock factory, never returns {@code null}.
203     */
204    protected NamedLockFactory selectFactory( final String factoryName )
205    {
206        NamedLockFactory factory = factories.get( factoryName );
207        if ( factory == null )
208        {
209            throw new IllegalArgumentException(
210                    "Unknown NamedLockFactory name: '" + factoryName + "', known ones: " + factories.keySet() );
211        }
212        return factory;
213    }
214
215    /**
216     * Selects a name mapper, never returns {@code null}.
217     */
218    protected NameMapper selectNameMapper( final String nameMapperName )
219    {
220        NameMapper nameMapper = nameMappers.get( nameMapperName );
221        if ( nameMapper == null )
222        {
223            throw new IllegalArgumentException(
224                    "Unknown NameMapper name: '" + nameMapperName + "', known ones: " + nameMappers.keySet() );
225        }
226        return nameMapper;
227    }
228
229    /**
230     * To be invoked on repository system shut down. This method will shut down each {@link NamedLockFactory}.
231     */
232    protected void shutdown()
233    {
234        logger.debug( "Shutting down adapter factory; available factories {}; available name mappers {}",
235                factories.keySet(), nameMappers.keySet() );
236        ArrayList<Exception> exceptions = new ArrayList<>();
237        for ( Map.Entry<String, NamedLockFactory> entry : factories.entrySet() )
238        {
239            try
240            {
241                logger.debug( "Shutting down '{}' factory", entry.getKey() );
242                entry.getValue().shutdown();
243            }
244            catch ( Exception e )
245            {
246                exceptions.add( e );
247            }
248        }
249        MultiRuntimeException.mayThrow( "Problem shutting down factories", exceptions );
250    }
251}