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.Map;
27  
28  import org.eclipse.aether.MultiRuntimeException;
29  import org.eclipse.aether.RepositorySystemSession;
30  import org.eclipse.aether.impl.RepositorySystemLifecycle;
31  import org.eclipse.aether.named.NamedLockFactory;
32  import org.eclipse.aether.named.providers.FileLockNamedLockFactory;
33  import org.eclipse.aether.spi.locking.LockingInhibitor;
34  import org.eclipse.aether.spi.locking.LockingInhibitorFactory;
35  import org.eclipse.aether.util.ConfigUtils;
36  import org.slf4j.Logger;
37  import org.slf4j.LoggerFactory;
38  
39  import static java.util.Objects.requireNonNull;
40  
41  /**
42   * Default implementation of {@link NamedLockFactoryAdapterFactory}. This implementation creates new instances of the
43   * adapter on every call. In turn, on shutdown, it will shut down all existing named lock factories. This is merely for
44   * simplicity, to not have to track "used" named lock factories, while it exposes all available named lock factories to
45   * callers.
46   * <p>
47   * Most members and methods of this class are protected. It is meant to be extended in case of need to customize its
48   * behavior. An exception from this are private static methods, mostly meant to provide out of the box
49   * defaults and to be used when no Eclipse Sisu component container is used.
50   *
51   * @since 1.9.1
52   */
53  @Singleton
54  @Named
55  public class NamedLockFactoryAdapterFactoryImpl implements NamedLockFactoryAdapterFactory {
56      public static final String DEFAULT_FACTORY_NAME = FileLockNamedLockFactory.NAME;
57  
58      public static final String DEFAULT_NAME_MAPPER_NAME = NameMappers.FILE_GAECV_NAME;
59  
60      /**
61       * Name of the lock factory to use in session. Out of the box supported ones are "file-lock", "rwlock-local",
62       * "semaphore-local", "noop". By adding extensions one can extend available lock factories (for example IPC locking).
63       *
64       * @configurationSource {@link RepositorySystemSession#getConfigProperties()}
65       * @configurationType {@link java.lang.String}
66       * @configurationDefaultValue {@link #DEFAULT_FACTORY_NAME}
67       */
68      public static final String CONFIG_PROP_FACTORY_KEY = NamedLockFactoryAdapter.CONFIG_PROPS_PREFIX + "factory";
69  
70      /**
71       * Name of the name mapper to use in session. Out of the box supported ones are "static", "gav", "gaecv", "file-gav",
72       * "file-gaecv", "file-hgav", "file-hgaecv", "file-static" and "discriminating".
73       *
74       * @configurationSource {@link RepositorySystemSession#getConfigProperties()}
75       * @configurationType {@link java.lang.String}
76       * @configurationDefaultValue {@link #DEFAULT_NAME_MAPPER_NAME}
77       */
78      public static final String CONFIG_PROP_NAME_MAPPER_KEY = NamedLockFactoryAdapter.CONFIG_PROPS_PREFIX + "nameMapper";
79  
80      protected final Logger logger = LoggerFactory.getLogger(getClass());
81  
82      protected final Map<String, NamedLockFactory> factories;
83  
84      protected final String defaultFactoryName;
85  
86      protected final Map<String, NameMapper> nameMappers;
87  
88      protected final String defaultNameMapperName;
89  
90      protected final Map<String, LockingInhibitorFactory> lockingInhibitorFactories;
91  
92      @Inject
93      public NamedLockFactoryAdapterFactoryImpl(
94              final Map<String, NamedLockFactory> factories,
95              final Map<String, NameMapper> nameMappers,
96              final Map<String, LockingInhibitorFactory> lockingInhibitorFactories,
97              final RepositorySystemLifecycle lifecycle) {
98          this(
99                  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 }