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;
027import java.util.concurrent.TimeUnit;
028
029import org.eclipse.aether.Keys;
030import org.eclipse.aether.RepositorySystemSession;
031import org.eclipse.aether.impl.NamedLockFactorySelector;
032import org.eclipse.aether.named.NamedLockFactory;
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_NAME_MAPPER_NAME = NameMappers.FILE_GAECV_NAME;
057
058    /**
059     * Name of the lock factory to use in session. Out of the box supported ones are "file-lock", "rwlock-local",
060     * "semaphore-local", "noop". By adding extensions one can extend available lock factories (for example IPC locking).
061     * <strong>Deprecated: use {@code aether.system.named...} configuration instead.</strong>
062     *
063     * @configurationSource {@link RepositorySystemSession#getConfigProperties()}
064     * @configurationType {@link java.lang.String}
065     * @deprecated
066     */
067    @Deprecated
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 NamedLockFactorySelector namedLockFactorySelector;
083
084    protected final Map<String, NameMapper> nameMappers;
085
086    protected final String defaultNameMapperName;
087
088    protected final Map<String, LockingInhibitorFactory> lockingInhibitorFactories;
089
090    @Inject
091    public NamedLockFactoryAdapterFactoryImpl(
092            final NamedLockFactorySelector namedLockFactorySelector,
093            final Map<String, NameMapper> nameMappers,
094            final Map<String, LockingInhibitorFactory> lockingInhibitorFactories) {
095        this(namedLockFactorySelector, nameMappers, DEFAULT_NAME_MAPPER_NAME, lockingInhibitorFactories);
096    }
097
098    public NamedLockFactoryAdapterFactoryImpl(
099            final NamedLockFactorySelector namedLockFactorySelector,
100            final Map<String, NameMapper> nameMappers,
101            final String defaultNameMapperName,
102            final Map<String, LockingInhibitorFactory> lockingInhibitorFactories) {
103        this.namedLockFactorySelector = requireNonNull(namedLockFactorySelector);
104        this.nameMappers = requireNonNull(nameMappers);
105        this.defaultNameMapperName = requireNonNull(defaultNameMapperName);
106        this.lockingInhibitorFactories = requireNonNull(lockingInhibitorFactories);
107
108        logger.debug(
109                "Created adapter factory; available lock factories {}; available name mappers {}",
110                namedLockFactorySelector.getAvailableLockFactories(),
111                nameMappers.keySet());
112    }
113
114    private static final Object ADAPTER_KEY = Keys.of(NamedLockFactoryAdapterFactoryImpl.class, "adapter");
115
116    /**
117     * Current implementation memoize instance in session or delegates to {@link #createAdapter(RepositorySystemSession)}.
118     */
119    @Override
120    public NamedLockFactoryAdapter getAdapter(RepositorySystemSession session) {
121        requireNonNull(session, "session cannot be null");
122        return (NamedLockFactoryAdapter) session.getData().computeIfAbsent(ADAPTER_KEY, () -> createAdapter(session));
123    }
124
125    /**
126     * Creates a new adapter instance, never returns {@code null}.
127     */
128    protected NamedLockFactoryAdapter createAdapter(RepositorySystemSession session) {
129        final NameMapper nameMapper = selectNameMapper(session, requireNonNull(getNameMapperName(session)));
130        final NamedLockFactory factory = namedLockFactorySelector.getNamedLockFactory(session.getConfigProperties());
131        final long lockWait = namedLockFactorySelector.getLockWaitTime(session.getConfigProperties());
132        final TimeUnit lockWaitUnit = namedLockFactorySelector.getLockWaitTimeUnit(session.getConfigProperties());
133        logger.debug("Creating adapter using nameMapper '{}' and factory '{}'", nameMapper, factory);
134        return new NamedLockFactoryAdapter(nameMapper, factory, lockWait, lockWaitUnit);
135    }
136
137    /**
138     * Returns the selected (user configured or default) name mapper name, never {@code null}.
139     */
140    protected String getNameMapperName(RepositorySystemSession session) {
141        return ConfigUtils.getString(session, getDefaultNameMapperName(), CONFIG_PROP_NAME_MAPPER_KEY);
142    }
143
144    /**
145     * Returns the default name mapper name, never {@code null}.
146     */
147    protected String getDefaultNameMapperName() {
148        return defaultNameMapperName;
149    }
150
151    /**
152     * Selects a name mapper, never returns {@code null}. Applies inhibitors.
153     */
154    protected NameMapper selectNameMapper(final RepositorySystemSession session, final String nameMapperName) {
155        NameMapper nameMapper = nameMappers.get(nameMapperName);
156        if (nameMapper == null) {
157            throw new IllegalArgumentException(
158                    "Unknown NameMapper name: '" + nameMapperName + "', known ones: " + nameMappers.keySet());
159        }
160        if (!lockingInhibitorFactories.isEmpty()) {
161            ArrayList<LockingInhibitor> inhibitors = new ArrayList<>();
162            for (LockingInhibitorFactory factory : lockingInhibitorFactories.values()) {
163                factory.newInstance(session).ifPresent(inhibitors::add);
164            }
165            if (!inhibitors.isEmpty()) {
166                return new InhibitingNameMapper(nameMapper, inhibitors);
167            }
168        }
169        return nameMapper;
170    }
171}