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.util.ConfigUtils;
034import org.slf4j.Logger;
035import org.slf4j.LoggerFactory;
036
037import static java.util.Objects.requireNonNull;
038
039/**
040 * Default implementation of {@link NamedLockFactoryAdapterFactory}. This implementation creates new instances of the
041 * adapter on every call. In turn, on shutdown, it will shut down all existing named lock factories. This is merely for
042 * simplicity, to not have to track "used" named lock factories, while it exposes all available named lock factories to
043 * callers.
044 * <p>
045 * Most members and methods of this class are protected. It is meant to be extended in case of need to customize its
046 * behavior. An exception from this are private static methods, mostly meant to provide out of the box
047 * defaults and to be used when no Eclipse Sisu component container is used.
048 *
049 * @since 1.9.1
050 */
051@Singleton
052@Named
053public class NamedLockFactoryAdapterFactoryImpl implements NamedLockFactoryAdapterFactory {
054    public static final String DEFAULT_FACTORY_NAME = FileLockNamedLockFactory.NAME;
055
056    public static final String DEFAULT_NAME_MAPPER_NAME = NameMappers.FILE_GAV_NAME;
057
058    /**
059     * Name of the lock factory to use in session.
060     *
061     * @configurationSource {@link RepositorySystemSession#getConfigProperties()}
062     * @configurationType {@link java.lang.String}
063     * @configurationDefaultValue {@link #DEFAULT_FACTORY_NAME}
064     */
065    public static final String CONFIG_PROP_FACTORY_KEY = NamedLockFactoryAdapter.CONFIG_PROPS_PREFIX + "factory";
066
067    /**
068     * Name of the name mapper to use in session. Out of the box supported ones are "static", "gav", "file-gav",
069     * "file-hgav", "file-static" and "discriminating".
070     *
071     * @configurationSource {@link RepositorySystemSession#getConfigProperties()}
072     * @configurationType {@link java.lang.String}
073     * @configurationDefaultValue {@link #DEFAULT_NAME_MAPPER_NAME}
074     */
075    public static final String CONFIG_PROP_NAME_MAPPER_KEY = NamedLockFactoryAdapter.CONFIG_PROPS_PREFIX + "nameMapper";
076
077    protected final Logger logger = LoggerFactory.getLogger(getClass());
078
079    protected final Map<String, NamedLockFactory> factories;
080
081    protected final String defaultFactoryName;
082
083    protected final Map<String, NameMapper> nameMappers;
084
085    protected final String defaultNameMapperName;
086
087    @Inject
088    public NamedLockFactoryAdapterFactoryImpl(
089            final Map<String, NamedLockFactory> factories,
090            final Map<String, NameMapper> nameMappers,
091            final RepositorySystemLifecycle lifecycle) {
092        this(factories, DEFAULT_FACTORY_NAME, nameMappers, DEFAULT_NAME_MAPPER_NAME, lifecycle);
093    }
094
095    public NamedLockFactoryAdapterFactoryImpl(
096            final Map<String, NamedLockFactory> factories,
097            final String defaultFactoryName,
098            final Map<String, NameMapper> nameMappers,
099            final String defaultNameMapperName,
100            final RepositorySystemLifecycle lifecycle) {
101        this.factories = requireNonNull(factories);
102        this.defaultFactoryName = requireNonNull(defaultFactoryName);
103        this.nameMappers = requireNonNull(nameMappers);
104        this.defaultNameMapperName = requireNonNull(defaultNameMapperName);
105        lifecycle.addOnSystemEndedHandler(this::shutdown);
106
107        logger.debug(
108                "Created adapter factory; available factories {}; available name mappers {}",
109                factories.keySet(),
110                nameMappers.keySet());
111    }
112
113    /**
114     * Current implementation simply delegates to {@link #createAdapter(RepositorySystemSession)}.
115     */
116    @Override
117    public NamedLockFactoryAdapter getAdapter(RepositorySystemSession session) {
118        return createAdapter(session);
119    }
120
121    /**
122     * Creates a new adapter instance, never returns {@code null}.
123     */
124    protected NamedLockFactoryAdapter createAdapter(RepositorySystemSession session) {
125        final String nameMapperName = requireNonNull(getNameMapperName(session));
126        final String factoryName = requireNonNull(getFactoryName(session));
127        final NameMapper nameMapper = selectNameMapper(nameMapperName);
128        final NamedLockFactory factory = selectFactory(factoryName);
129        logger.debug("Creating adapter using nameMapper '{}' and factory '{}'", nameMapperName, factoryName);
130        return new NamedLockFactoryAdapter(nameMapper, factory);
131    }
132
133    /**
134     * Returns the selected (user configured or default) named lock factory name, never {@code null}.
135     */
136    protected String getFactoryName(RepositorySystemSession session) {
137        return ConfigUtils.getString(session, getDefaultFactoryName(), CONFIG_PROP_FACTORY_KEY);
138    }
139
140    /**
141     * Returns the default named lock factory name, never {@code null}.
142     */
143    protected String getDefaultFactoryName() {
144        return defaultFactoryName;
145    }
146
147    /**
148     * Returns the selected (user configured or default) name mapper name, never {@code null}.
149     */
150    protected String getNameMapperName(RepositorySystemSession session) {
151        return ConfigUtils.getString(session, getDefaultNameMapperName(), CONFIG_PROP_NAME_MAPPER_KEY);
152    }
153
154    /**
155     * Returns the default name mapper name, never {@code null}.
156     */
157    protected String getDefaultNameMapperName() {
158        return defaultNameMapperName;
159    }
160
161    /**
162     * Selects a named lock factory, never returns {@code null}.
163     */
164    protected NamedLockFactory selectFactory(final String factoryName) {
165        NamedLockFactory factory = factories.get(factoryName);
166        if (factory == null) {
167            throw new IllegalArgumentException(
168                    "Unknown NamedLockFactory name: '" + factoryName + "', known ones: " + factories.keySet());
169        }
170        return factory;
171    }
172
173    /**
174     * Selects a name mapper, never returns {@code null}.
175     */
176    protected NameMapper selectNameMapper(final String nameMapperName) {
177        NameMapper nameMapper = nameMappers.get(nameMapperName);
178        if (nameMapper == null) {
179            throw new IllegalArgumentException(
180                    "Unknown NameMapper name: '" + nameMapperName + "', known ones: " + nameMappers.keySet());
181        }
182        return nameMapper;
183    }
184
185    /**
186     * To be invoked on repository system shut down. This method will shut down each {@link NamedLockFactory}.
187     */
188    protected void shutdown() {
189        logger.debug(
190                "Shutting down adapter factory; available factories {}; available name mappers {}",
191                factories.keySet(),
192                nameMappers.keySet());
193        ArrayList<Exception> exceptions = new ArrayList<>();
194        for (Map.Entry<String, NamedLockFactory> entry : factories.entrySet()) {
195            try {
196                logger.debug("Shutting down '{}' factory", entry.getKey());
197                entry.getValue().shutdown();
198            } catch (Exception e) {
199                exceptions.add(e);
200            }
201        }
202        MultiRuntimeException.mayThrow("Problem shutting down factories", exceptions);
203    }
204}