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;
20  
21  import java.util.ArrayList;
22  import java.util.Collections;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.function.Function;
26  
27  import org.eclipse.aether.ConfigurationProperties;
28  import org.eclipse.aether.RepositorySystemSession;
29  import org.eclipse.aether.util.ConfigUtils;
30  
31  /**
32   * Helps to sort pluggable components by their priority.
33   *
34   * @param <T> The component type.
35   */
36  public final class PrioritizedComponents<T> {
37      /**
38       * Reuses or creates and stores (if session data does not contain yet) prioritized components under this key into
39       * given session. Same session is used to configure prioritized components.
40       * <p>
41       * The {@code components} are expected to be Sisu injected {@link Map<String, C>}-like component maps. There is a
42       * simple "change detection" in place, as injected maps are dynamic, they are atomically expanded or contracted
43       * as components are dynamically discovered or unloaded.
44       *
45       * @since 2.0.0
46       */
47      @SuppressWarnings("unchecked")
48      public static <C> PrioritizedComponents<C> reuseOrCreate(
49              RepositorySystemSession session, Map<String, C> components, Function<C, Float> priorityFunction) {
50          boolean cached = ConfigUtils.getBoolean(
51                  session, ConfigurationProperties.DEFAULT_CACHED_PRIORITIES, ConfigurationProperties.CACHED_PRIORITIES);
52          if (cached) {
53              String key = PrioritizedComponents.class.getName() + ".pc" + Integer.toHexString(components.hashCode());
54              return (PrioritizedComponents<C>)
55                      session.getData().computeIfAbsent(key, () -> create(session, components, priorityFunction));
56          } else {
57              return create(session, components, priorityFunction);
58          }
59      }
60  
61      private static <C> PrioritizedComponents<C> create(
62              RepositorySystemSession session, Map<String, C> components, Function<C, Float> priorityFunction) {
63          PrioritizedComponents<C> newInstance = new PrioritizedComponents<>(session);
64          components.values().forEach(c -> newInstance.add(c, priorityFunction.apply(c)));
65          return newInstance;
66      }
67  
68      private static final String FACTORY_SUFFIX = "Factory";
69  
70      private final Map<?, ?> configProps;
71  
72      private final boolean useInsertionOrder;
73  
74      private final List<PrioritizedComponent<T>> components;
75  
76      private int firstDisabled;
77  
78      PrioritizedComponents(RepositorySystemSession session) {
79          this(session.getConfigProperties());
80      }
81  
82      PrioritizedComponents(Map<?, ?> configurationProperties) {
83          configProps = configurationProperties;
84          useInsertionOrder = ConfigUtils.getBoolean(
85                  configProps,
86                  ConfigurationProperties.DEFAULT_IMPLICIT_PRIORITIES,
87                  ConfigurationProperties.IMPLICIT_PRIORITIES);
88          components = new ArrayList<>();
89          firstDisabled = 0;
90      }
91  
92      public void add(T component, float priority) {
93          Class<?> type = getImplClass(component);
94          int index = components.size();
95          priority = useInsertionOrder ? -index : ConfigUtils.getFloat(configProps, priority, getConfigKeys(type));
96          PrioritizedComponent<T> pc = new PrioritizedComponent<>(component, type, priority, index);
97  
98          if (!useInsertionOrder) {
99              index = Collections.binarySearch(components, pc);
100             if (index < 0) {
101                 index = -index - 1;
102             } else {
103                 index++;
104             }
105         }
106         components.add(index, pc);
107 
108         if (index <= firstDisabled && !pc.isDisabled()) {
109             firstDisabled++;
110         }
111     }
112 
113     private static Class<?> getImplClass(Object component) {
114         Class<?> type = component.getClass();
115         // detect and ignore CGLIB-based proxy classes employed by Guice for AOP (cf. BytecodeGen.newEnhancer)
116         int idx = type.getName().indexOf("$$");
117         if (idx >= 0) {
118             Class<?> base = type.getSuperclass();
119             if (base != null && idx == base.getName().length() && type.getName().startsWith(base.getName())) {
120                 type = base;
121             }
122         }
123         return type;
124     }
125 
126     static String[] getConfigKeys(Class<?> type) {
127         List<String> keys = new ArrayList<>();
128         keys.add(ConfigurationProperties.PREFIX_PRIORITY + type.getName());
129         String sn = type.getSimpleName();
130         keys.add(ConfigurationProperties.PREFIX_PRIORITY + sn);
131         if (sn.endsWith(FACTORY_SUFFIX)) {
132             keys.add(ConfigurationProperties.PREFIX_PRIORITY + sn.substring(0, sn.length() - FACTORY_SUFFIX.length()));
133         }
134         return keys.toArray(new String[0]);
135     }
136 
137     public boolean isEmpty() {
138         return components.isEmpty();
139     }
140 
141     public List<PrioritizedComponent<T>> getAll() {
142         return components;
143     }
144 
145     public List<PrioritizedComponent<T>> getEnabled() {
146         return components.subList(0, firstDisabled);
147     }
148 
149     public void list(StringBuilder buffer) {
150         int i = 0;
151         for (PrioritizedComponent<?> component : components) {
152             if (i++ > 0) {
153                 buffer.append(", ");
154             }
155             buffer.append(component.getType().getSimpleName());
156             if (component.isDisabled()) {
157                 buffer.append(" (disabled)");
158             }
159         }
160     }
161 
162     @Override
163     public String toString() {
164         return components.toString();
165     }
166 }