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.internal.impl.synccontext.named.providers.DiscriminatingNameMapperProvider;
035import org.eclipse.aether.internal.impl.synccontext.named.providers.FileGAVNameMapperProvider;
036import org.eclipse.aether.internal.impl.synccontext.named.providers.FileHashingGAVNameMapperProvider;
037import org.eclipse.aether.internal.impl.synccontext.named.providers.GAVNameMapperProvider;
038import org.eclipse.aether.internal.impl.synccontext.named.providers.StaticNameMapperProvider;
039import org.eclipse.aether.named.NamedLockFactory;
040import org.eclipse.aether.named.providers.FileLockNamedLockFactory;
041import org.eclipse.aether.named.providers.LocalReadWriteLockNamedLockFactory;
042import org.eclipse.aether.named.providers.LocalSemaphoreNamedLockFactory;
043import org.eclipse.aether.named.providers.NoopNamedLockFactory;
044import org.eclipse.aether.spi.locator.Service;
045import org.eclipse.aether.spi.locator.ServiceLocator;
046import org.eclipse.aether.util.ConfigUtils;
047import org.slf4j.Logger;
048import org.slf4j.LoggerFactory;
049
050import static java.util.Objects.requireNonNull;
051
052/**
053 * Default implementation of {@link NamedLockFactoryAdapterFactory}. This implementation creates new instances of the
054 * adapter on every call. In turn, on shutdown, it will shut down all existing named lock factories. This is merely for
055 * simplicity, to not have to track "used" named lock factories, while it exposes all available named lock factories to
056 * callers.
057 * <p>
058 * Most members and methods of this class are protected. It is meant to be extended in case of need to customize its
059 * behavior. An exception from this are private static methods, mostly meant to provide out of the box
060 * defaults and to be used when no Eclipse Sisu component container is used.
061 *
062 * @since 1.9.1
063 */
064@Singleton
065@Named
066public class NamedLockFactoryAdapterFactoryImpl implements NamedLockFactoryAdapterFactory, Service
067{
068    private static final String DEFAULT_FACTORY_NAME = LocalReadWriteLockNamedLockFactory.NAME;
069
070    private static final String DEFAULT_NAME_MAPPER_NAME = GAVNameMapperProvider.NAME;
071
072    private static Map<String, NamedLockFactory> getManuallyCreatedFactories()
073    {
074        HashMap<String, NamedLockFactory> factories = new HashMap<>();
075        factories.put( NoopNamedLockFactory.NAME, new NoopNamedLockFactory() );
076        factories.put( LocalReadWriteLockNamedLockFactory.NAME, new LocalReadWriteLockNamedLockFactory() );
077        factories.put( LocalSemaphoreNamedLockFactory.NAME, new LocalSemaphoreNamedLockFactory() );
078        factories.put( FileLockNamedLockFactory.NAME, new FileLockNamedLockFactory() );
079        return Collections.unmodifiableMap( factories );
080    }
081
082    private static Map<String, NameMapper> getManuallyCreatedNameMappers()
083    {
084        HashMap<String, NameMapper> mappers = new HashMap<>();
085        mappers.put( StaticNameMapperProvider.NAME, new StaticNameMapperProvider().get() );
086        mappers.put( GAVNameMapperProvider.NAME, new GAVNameMapperProvider().get() );
087        mappers.put( DiscriminatingNameMapperProvider.NAME, new DiscriminatingNameMapperProvider().get() );
088        mappers.put( FileGAVNameMapperProvider.NAME, new FileGAVNameMapperProvider().get() );
089        mappers.put( FileHashingGAVNameMapperProvider.NAME, new FileHashingGAVNameMapperProvider().get() );
090        return Collections.unmodifiableMap( mappers );
091    }
092
093    protected static final String FACTORY_KEY = "aether.syncContext.named.factory";
094
095    protected static final String NAME_MAPPER_KEY = "aether.syncContext.named.nameMapper";
096
097    protected final Logger logger = LoggerFactory.getLogger( getClass() );
098
099    protected final Map<String, NamedLockFactory> factories;
100
101    protected final String defaultFactoryName;
102
103    protected final Map<String, NameMapper> nameMappers;
104
105    protected final String defaultNameMapperName;
106
107    /**
108     * Default constructor for non Eclipse Sisu uses.
109     *
110     * @deprecated for use in SL only.
111     */
112    @Deprecated
113    public NamedLockFactoryAdapterFactoryImpl()
114    {
115        this.factories = getManuallyCreatedFactories();
116        this.defaultFactoryName = DEFAULT_FACTORY_NAME;
117        this.nameMappers = getManuallyCreatedNameMappers();
118        this.defaultNameMapperName = DEFAULT_NAME_MAPPER_NAME;
119    }
120
121    @Override
122    public void initService( ServiceLocator locator )
123    {
124        locator.getService( RepositorySystemLifecycle.class ).addOnSystemEndedHandler( this::shutdown );
125    }
126
127    @Inject
128    public NamedLockFactoryAdapterFactoryImpl( final Map<String, NamedLockFactory> factories,
129                                               final Map<String, NameMapper> nameMappers,
130                                               final RepositorySystemLifecycle lifecycle )
131    {
132        this( factories, DEFAULT_FACTORY_NAME, nameMappers, DEFAULT_NAME_MAPPER_NAME, lifecycle );
133    }
134
135    public NamedLockFactoryAdapterFactoryImpl( final Map<String, NamedLockFactory> factories,
136                                               final String defaultFactoryName,
137                                               final Map<String, NameMapper> nameMappers,
138                                               final String defaultNameMapperName,
139                                               final RepositorySystemLifecycle lifecycle )
140    {
141        this.factories = requireNonNull( factories );
142        this.defaultFactoryName = requireNonNull( defaultFactoryName );
143        this.nameMappers = requireNonNull( nameMappers );
144        this.defaultNameMapperName = requireNonNull( defaultNameMapperName );
145        lifecycle.addOnSystemEndedHandler( this::shutdown );
146
147        logger.debug( "Created adapter factory; available factories {}; available name mappers {}",
148                factories.keySet(), nameMappers.keySet() );
149    }
150
151    /**
152     * Current implementation simply delegates to {@link #createAdapter(RepositorySystemSession)}.
153     */
154    @Override
155    public NamedLockFactoryAdapter getAdapter( RepositorySystemSession session )
156    {
157        return createAdapter( session );
158    }
159
160    /**
161     * Creates a new adapter instance, never returns {@code null}.
162     */
163    protected NamedLockFactoryAdapter createAdapter( RepositorySystemSession session )
164    {
165        final String nameMapperName = requireNonNull( getNameMapperName( session ) );
166        final String factoryName = requireNonNull( getFactoryName( session ) );
167        final NameMapper nameMapper = selectNameMapper( nameMapperName );
168        final NamedLockFactory factory = selectFactory( factoryName );
169        logger.debug( "Creating adapter using nameMapper '{}' and factory '{}'",
170                nameMapperName, factoryName );
171        return new NamedLockFactoryAdapter( nameMapper, factory );
172    }
173
174    /**
175     * Returns the selected (user configured or default) named lock factory name, never {@code null}.
176     */
177    protected String getFactoryName( RepositorySystemSession session )
178    {
179        return ConfigUtils.getString( session, getDefaultFactoryName(), FACTORY_KEY );
180    }
181
182    /**
183     * Returns the default named lock factory name, never {@code null}.
184     */
185    protected String getDefaultFactoryName()
186    {
187        return defaultFactoryName;
188    }
189
190    /**
191     * Returns the selected (user configured or default) name mapper name, never {@code null}.
192     */
193    protected String getNameMapperName( RepositorySystemSession session )
194    {
195        return ConfigUtils.getString( session, getDefaultNameMapperName(), NAME_MAPPER_KEY );
196    }
197
198    /**
199     * Returns the default name mapper name, never {@code null}.
200     */
201    protected String getDefaultNameMapperName()
202    {
203        return defaultNameMapperName;
204    }
205
206    /**
207     * Selects a named lock factory, never returns {@code null}.
208     */
209    protected NamedLockFactory selectFactory( final String factoryName )
210    {
211        NamedLockFactory factory = factories.get( factoryName );
212        if ( factory == null )
213        {
214            throw new IllegalArgumentException(
215                    "Unknown NamedLockFactory name: '" + factoryName + "', known ones: " + factories.keySet() );
216        }
217        return factory;
218    }
219
220    /**
221     * Selects a name mapper, never returns {@code null}.
222     */
223    protected NameMapper selectNameMapper( final String nameMapperName )
224    {
225        NameMapper nameMapper = nameMappers.get( nameMapperName );
226        if ( nameMapper == null )
227        {
228            throw new IllegalArgumentException(
229                    "Unknown NameMapper name: '" + nameMapperName + "', known ones: " + nameMappers.keySet() );
230        }
231        return nameMapper;
232    }
233
234    /**
235     * To be invoked on repository system shut down. This method will shut down each {@link NamedLockFactory}.
236     */
237    protected void shutdown()
238    {
239        logger.debug( "Shutting down adapter factory; available factories {}; available name mappers {}",
240                factories.keySet(), nameMappers.keySet() );
241        ArrayList<Exception> exceptions = new ArrayList<>();
242        for ( Map.Entry<String, NamedLockFactory> entry : factories.entrySet() )
243        {
244            try
245            {
246                logger.debug( "Shutting down '{}' factory", entry.getKey() );
247                entry.getValue().shutdown();
248            }
249            catch ( Exception e )
250            {
251                exceptions.add( e );
252            }
253        }
254        MultiRuntimeException.mayThrow( "Problem shutting down factories", exceptions );
255    }
256}