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.named;
020
021import javax.inject.Inject;
022import javax.inject.Named;
023import javax.inject.Singleton;
024
025import java.util.ArrayList;
026import java.util.Collections;
027import java.util.HashSet;
028import java.util.Map;
029import java.util.Set;
030import java.util.concurrent.ConcurrentHashMap;
031import java.util.concurrent.ConcurrentMap;
032import java.util.concurrent.TimeUnit;
033
034import org.eclipse.aether.ConfigurationProperties;
035import org.eclipse.aether.MultiRuntimeException;
036import org.eclipse.aether.RepositorySystemSession;
037import org.eclipse.aether.impl.NamedLockFactorySelector;
038import org.eclipse.aether.impl.RepositorySystemLifecycle;
039import org.eclipse.aether.internal.impl.synccontext.named.NamedLockFactoryAdapter;
040import org.eclipse.aether.internal.impl.synccontext.named.NamedLockFactoryAdapterFactoryImpl;
041import org.eclipse.aether.named.NamedLockFactory;
042import org.eclipse.aether.named.providers.FileLockNamedLockFactory;
043import org.eclipse.aether.util.ConfigUtils;
044import org.slf4j.Logger;
045import org.slf4j.LoggerFactory;
046
047import static java.util.Objects.requireNonNull;
048
049@Singleton
050@Named
051public class DefaultNamedLockFactorySelector implements NamedLockFactorySelector {
052    public static final String CONFIG_PROPS_PREFIX = ConfigurationProperties.PREFIX_SYSTEM + "named.";
053
054    /**
055     * Name of the lock factory to use in system. Out of the box supported ones are "file-lock", "rwlock-local",
056     * "semaphore-local", "noop". By adding extensions one can extend available lock factories (for example IPC locking).
057     *
058     * @configurationSource {@link RepositorySystemSession#getConfigProperties()}
059     * @configurationType {@link java.lang.String}
060     * @configurationDefaultValue {@link #DEFAULT_FACTORY_NAME}
061     */
062    public static final String CONFIG_PROP_FACTORY_NAME = CONFIG_PROPS_PREFIX + "factory";
063
064    public static final String DEFAULT_FACTORY_NAME = FileLockNamedLockFactory.NAME;
065
066    /**
067     * The maximum amount of time to be blocked, while obtaining a named lock.
068     *
069     * @configurationSource {@link RepositorySystemSession#getConfigProperties()}
070     * @configurationType {@link java.lang.Long}
071     * @configurationDefaultValue {@link #DEFAULT_LOCK_WAIT_TIME}
072     */
073    public static final String CONFIG_PROP_LOCK_WAIT_TIME = CONFIG_PROPS_PREFIX + "time";
074
075    public static final long DEFAULT_LOCK_WAIT_TIME = 900L;
076
077    /**
078     * The unit of maximum amount of time. Accepts TimeUnit enum names.
079     *
080     * @configurationSource {@link RepositorySystemSession#getConfigProperties()}
081     * @configurationType {@link java.lang.String}
082     * @configurationDefaultValue {@link #DEFAULT_LOCK_WAIT_TIME_UNIT}
083     */
084    public static final String CONFIG_PROP_LOCK_WAIT_TIME_UNIT = CONFIG_PROPS_PREFIX + "timeUnit";
085
086    public static final String DEFAULT_LOCK_WAIT_TIME_UNIT = "SECONDS";
087
088    protected final Logger logger = LoggerFactory.getLogger(getClass());
089
090    protected final Map<String, NamedLockFactory> factories;
091
092    protected final ConcurrentMap<String, NamedLockFactory> usedFactories;
093
094    @Inject
095    public DefaultNamedLockFactorySelector(
096            Map<String, NamedLockFactory> factories, RepositorySystemLifecycle lifecycle) {
097        this.factories = requireNonNull(factories);
098        this.usedFactories = new ConcurrentHashMap<>();
099        lifecycle.addOnSystemEndedHandler(this::shutdown);
100
101        logger.debug("Created lock factory selector; available lock factories {}", factories.keySet());
102    }
103
104    @Override
105    public Set<String> getAvailableLockFactories() {
106        return Collections.unmodifiableSet(new HashSet<>(factories.keySet()));
107    }
108
109    @Override
110    public NamedLockFactory getNamedLockFactory(Map<String, ?> configuration) {
111        String factoryName = ConfigUtils.getString(
112                configuration,
113                DEFAULT_FACTORY_NAME,
114                CONFIG_PROP_FACTORY_NAME,
115                NamedLockFactoryAdapterFactoryImpl.CONFIG_PROP_FACTORY_KEY);
116        NamedLockFactory factory = factories.get(factoryName);
117        if (factory == null) {
118            throw new IllegalArgumentException(
119                    "Unknown NamedLockFactory name: '" + factoryName + "', known ones: " + factories.keySet());
120        }
121        usedFactories.putIfAbsent(factoryName, factory);
122        return factory;
123    }
124
125    @Override
126    public long getLockWaitTime(Map<String, ?> configuration) {
127        long result = ConfigUtils.getLong(
128                configuration,
129                DEFAULT_LOCK_WAIT_TIME,
130                CONFIG_PROP_LOCK_WAIT_TIME,
131                NamedLockFactoryAdapter.CONFIG_PROP_TIME);
132        if (result <= 0) {
133            throw new IllegalArgumentException(
134                    "The " + CONFIG_PROP_LOCK_WAIT_TIME + " configuration must be greater than zero");
135        }
136        return result;
137    }
138
139    @Override
140    public TimeUnit getLockWaitTimeUnit(Map<String, ?> configuration) {
141        return TimeUnit.valueOf(ConfigUtils.getString(
142                configuration,
143                DEFAULT_LOCK_WAIT_TIME_UNIT,
144                CONFIG_PROP_LOCK_WAIT_TIME_UNIT,
145                NamedLockFactoryAdapter.CONFIG_PROP_TIME_UNIT));
146    }
147
148    private void shutdown() {
149        logger.debug("Shutting down used lock factories {}", usedFactories.keySet());
150        ArrayList<Exception> exceptions = new ArrayList<>();
151        for (Map.Entry<String, NamedLockFactory> entry : usedFactories.entrySet()) {
152            try {
153                logger.debug("Shutting down '{}' factory", entry.getKey());
154                entry.getValue().shutdown();
155            } catch (Exception e) {
156                exceptions.add(e);
157            }
158        }
159        MultiRuntimeException.mayThrow("Problem shutting down used lock factories", exceptions);
160    }
161}