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