View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.eclipse.aether.internal.impl.synccontext.named;
20  
21  import javax.inject.Inject;
22  import javax.inject.Named;
23  import javax.inject.Singleton;
24  
25  import java.util.ArrayList;
26  import java.util.Collections;
27  import java.util.HashMap;
28  import java.util.Map;
29  
30  import org.eclipse.aether.MultiRuntimeException;
31  import org.eclipse.aether.RepositorySystemSession;
32  import org.eclipse.aether.impl.RepositorySystemLifecycle;
33  import org.eclipse.aether.named.NamedLockFactory;
34  import org.eclipse.aether.named.providers.FileLockNamedLockFactory;
35  import org.eclipse.aether.named.providers.LocalReadWriteLockNamedLockFactory;
36  import org.eclipse.aether.named.providers.LocalSemaphoreNamedLockFactory;
37  import org.eclipse.aether.named.providers.NoopNamedLockFactory;
38  import org.eclipse.aether.spi.locator.Service;
39  import org.eclipse.aether.spi.locator.ServiceLocator;
40  import org.eclipse.aether.util.ConfigUtils;
41  import org.slf4j.Logger;
42  import org.slf4j.LoggerFactory;
43  
44  import static java.util.Objects.requireNonNull;
45  
46  /**
47   * Default implementation of {@link NamedLockFactoryAdapterFactory}. This implementation creates new instances of the
48   * adapter on every call. In turn, on shutdown, it will shut down all existing named lock factories. This is merely for
49   * simplicity, to not have to track "used" named lock factories, while it exposes all available named lock factories to
50   * callers.
51   * <p>
52   * Most members and methods of this class are protected. It is meant to be extended in case of need to customize its
53   * behavior. An exception from this are private static methods, mostly meant to provide out of the box
54   * defaults and to be used when no Eclipse Sisu component container is used.
55   *
56   * @since 1.9.1
57   */
58  @Singleton
59  @Named
60  public class NamedLockFactoryAdapterFactoryImpl implements NamedLockFactoryAdapterFactory, Service {
61      private static final String DEFAULT_FACTORY_NAME = LocalReadWriteLockNamedLockFactory.NAME;
62  
63      private static final String DEFAULT_NAME_MAPPER_NAME = NameMappers.GAECV_NAME;
64  
65      private static Map<String, NamedLockFactory> getManuallyCreatedFactories() {
66          HashMap<String, NamedLockFactory> factories = new HashMap<>();
67          factories.put(NoopNamedLockFactory.NAME, new NoopNamedLockFactory());
68          factories.put(LocalReadWriteLockNamedLockFactory.NAME, new LocalReadWriteLockNamedLockFactory());
69          factories.put(LocalSemaphoreNamedLockFactory.NAME, new LocalSemaphoreNamedLockFactory());
70          factories.put(FileLockNamedLockFactory.NAME, new FileLockNamedLockFactory());
71          return Collections.unmodifiableMap(factories);
72      }
73  
74      private static Map<String, NameMapper> getManuallyCreatedNameMappers() {
75          HashMap<String, NameMapper> mappers = new HashMap<>();
76          mappers.put(NameMappers.STATIC_NAME, NameMappers.staticNameMapper());
77          mappers.put(NameMappers.GAV_NAME, NameMappers.gavNameMapper());
78          mappers.put(NameMappers.GAECV_NAME, NameMappers.gaecvNameMapper());
79          mappers.put(NameMappers.DISCRIMINATING_NAME, NameMappers.discriminatingNameMapper());
80          mappers.put(NameMappers.FILE_GAV_NAME, NameMappers.fileGavNameMapper());
81          mappers.put(NameMappers.FILE_GAECV_NAME, NameMappers.fileGaecvNameMapper());
82          mappers.put(NameMappers.FILE_HGAV_NAME, NameMappers.fileHashingGavNameMapper());
83          mappers.put(NameMappers.FILE_HGAECV_NAME, NameMappers.fileHashingGaecvNameMapper());
84          return Collections.unmodifiableMap(mappers);
85      }
86  
87      protected static final String FACTORY_KEY = "aether.syncContext.named.factory";
88  
89      protected static final String NAME_MAPPER_KEY = "aether.syncContext.named.nameMapper";
90  
91      protected final Logger logger = LoggerFactory.getLogger(getClass());
92  
93      protected final Map<String, NamedLockFactory> factories;
94  
95      protected final String defaultFactoryName;
96  
97      protected final Map<String, NameMapper> nameMappers;
98  
99      protected final String defaultNameMapperName;
100 
101     /**
102      * Default constructor for non Eclipse Sisu uses.
103      *
104      * @deprecated for use in SL only.
105      */
106     @Deprecated
107     public NamedLockFactoryAdapterFactoryImpl() {
108         this.factories = getManuallyCreatedFactories();
109         this.defaultFactoryName = DEFAULT_FACTORY_NAME;
110         this.nameMappers = getManuallyCreatedNameMappers();
111         this.defaultNameMapperName = DEFAULT_NAME_MAPPER_NAME;
112     }
113 
114     @Override
115     public void initService(ServiceLocator locator) {
116         locator.getService(RepositorySystemLifecycle.class).addOnSystemEndedHandler(this::shutdown);
117     }
118 
119     @Inject
120     public NamedLockFactoryAdapterFactoryImpl(
121             final Map<String, NamedLockFactory> factories,
122             final Map<String, NameMapper> nameMappers,
123             final RepositorySystemLifecycle lifecycle) {
124         this(factories, DEFAULT_FACTORY_NAME, nameMappers, DEFAULT_NAME_MAPPER_NAME, lifecycle);
125     }
126 
127     public NamedLockFactoryAdapterFactoryImpl(
128             final Map<String, NamedLockFactory> factories,
129             final String defaultFactoryName,
130             final Map<String, NameMapper> nameMappers,
131             final String defaultNameMapperName,
132             final RepositorySystemLifecycle lifecycle) {
133         this.factories = requireNonNull(factories);
134         this.defaultFactoryName = requireNonNull(defaultFactoryName);
135         this.nameMappers = requireNonNull(nameMappers);
136         this.defaultNameMapperName = requireNonNull(defaultNameMapperName);
137         lifecycle.addOnSystemEndedHandler(this::shutdown);
138 
139         logger.debug(
140                 "Created adapter factory; available factories {}; available name mappers {}",
141                 factories.keySet(),
142                 nameMappers.keySet());
143     }
144 
145     /**
146      * Current implementation simply delegates to {@link #createAdapter(RepositorySystemSession)}.
147      */
148     @Override
149     public NamedLockFactoryAdapter getAdapter(RepositorySystemSession session) {
150         return createAdapter(session);
151     }
152 
153     /**
154      * Creates a new adapter instance, never returns {@code null}.
155      */
156     protected NamedLockFactoryAdapter createAdapter(RepositorySystemSession session) {
157         final String nameMapperName = requireNonNull(getNameMapperName(session));
158         final String factoryName = requireNonNull(getFactoryName(session));
159         final NameMapper nameMapper = selectNameMapper(nameMapperName);
160         final NamedLockFactory factory = selectFactory(factoryName);
161         logger.debug("Creating adapter using nameMapper '{}' and factory '{}'", nameMapperName, factoryName);
162         return new NamedLockFactoryAdapter(nameMapper, factory);
163     }
164 
165     /**
166      * Returns the selected (user configured or default) named lock factory name, never {@code null}.
167      */
168     protected String getFactoryName(RepositorySystemSession session) {
169         return ConfigUtils.getString(session, getDefaultFactoryName(), FACTORY_KEY);
170     }
171 
172     /**
173      * Returns the default named lock factory name, never {@code null}.
174      */
175     protected String getDefaultFactoryName() {
176         return defaultFactoryName;
177     }
178 
179     /**
180      * Returns the selected (user configured or default) name mapper name, never {@code null}.
181      */
182     protected String getNameMapperName(RepositorySystemSession session) {
183         return ConfigUtils.getString(session, getDefaultNameMapperName(), NAME_MAPPER_KEY);
184     }
185 
186     /**
187      * Returns the default name mapper name, never {@code null}.
188      */
189     protected String getDefaultNameMapperName() {
190         return defaultNameMapperName;
191     }
192 
193     /**
194      * Selects a named lock factory, never returns {@code null}.
195      */
196     protected NamedLockFactory selectFactory(final String factoryName) {
197         NamedLockFactory factory = factories.get(factoryName);
198         if (factory == null) {
199             throw new IllegalArgumentException(
200                     "Unknown NamedLockFactory name: '" + factoryName + "', known ones: " + factories.keySet());
201         }
202         return factory;
203     }
204 
205     /**
206      * Selects a name mapper, never returns {@code null}.
207      */
208     protected NameMapper selectNameMapper(final String nameMapperName) {
209         NameMapper nameMapper = nameMappers.get(nameMapperName);
210         if (nameMapper == null) {
211             throw new IllegalArgumentException(
212                     "Unknown NameMapper name: '" + nameMapperName + "', known ones: " + nameMappers.keySet());
213         }
214         return nameMapper;
215     }
216 
217     /**
218      * To be invoked on repository system shut down. This method will shut down each {@link NamedLockFactory}.
219      */
220     protected void shutdown() {
221         logger.debug(
222                 "Shutting down adapter factory; available factories {}; available name mappers {}",
223                 factories.keySet(),
224                 nameMappers.keySet());
225         ArrayList<Exception> exceptions = new ArrayList<>();
226         for (Map.Entry<String, NamedLockFactory> entry : factories.entrySet()) {
227             try {
228                 logger.debug("Shutting down '{}' factory", entry.getKey());
229                 entry.getValue().shutdown();
230             } catch (Exception e) {
231                 exceptions.add(e);
232             }
233         }
234         MultiRuntimeException.mayThrow("Problem shutting down factories", exceptions);
235     }
236 }