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