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.util.listener;
020
021import java.util.Arrays;
022import java.util.Collection;
023import java.util.List;
024import java.util.concurrent.CopyOnWriteArrayList;
025
026import org.eclipse.aether.AbstractRepositoryListener;
027import org.eclipse.aether.RepositoryEvent;
028import org.eclipse.aether.RepositoryListener;
029
030import static java.util.Objects.requireNonNull;
031
032/**
033 * A repository listener that delegates to zero or more other listeners (multicast). The list of target listeners is
034 * thread-safe, i.e. target listeners can be added or removed by any thread at any time.
035 */
036public final class ChainedRepositoryListener extends AbstractRepositoryListener {
037
038    private final List<RepositoryListener> listeners = new CopyOnWriteArrayList<>();
039
040    /**
041     * Creates a new multicast listener that delegates to the specified listeners. In contrast to the constructor, this
042     * factory method will avoid creating an actual chained listener if one of the specified readers is actually
043     * {@code null}.
044     *
045     * @param listener1 The first listener, may be {@code null}.
046     * @param listener2 The second listener, may be {@code null}.
047     * @return The chained listener or {@code null} if no listener was supplied.
048     */
049    public static RepositoryListener newInstance(RepositoryListener listener1, RepositoryListener listener2) {
050        if (listener1 == null) {
051            return listener2;
052        } else if (listener2 == null) {
053            return listener1;
054        }
055        return new ChainedRepositoryListener(listener1, listener2);
056    }
057
058    /**
059     * Creates a new multicast listener that delegates to the specified listeners.
060     *
061     * @param listeners The listeners to delegate to, may be {@code null} or empty.
062     */
063    public ChainedRepositoryListener(RepositoryListener... listeners) {
064        if (listeners != null) {
065            add(Arrays.asList(listeners));
066        }
067    }
068
069    /**
070     * Creates a new multicast listener that delegates to the specified listeners.
071     *
072     * @param listeners The listeners to delegate to, may be {@code null} or empty.
073     */
074    public ChainedRepositoryListener(Collection<? extends RepositoryListener> listeners) {
075        add(listeners);
076    }
077
078    /**
079     * Adds the specified listeners to the end of the multicast chain.
080     *
081     * @param listeners The listeners to add, may be {@code null} or empty.
082     */
083    public void add(Collection<? extends RepositoryListener> listeners) {
084        if (listeners != null) {
085            for (RepositoryListener listener : listeners) {
086                add(listener);
087            }
088        }
089    }
090
091    /**
092     * Adds the specified listener to the end of the multicast chain.
093     *
094     * @param listener The listener to add, may be {@code null}.
095     */
096    public void add(RepositoryListener listener) {
097        if (listener != null) {
098            listeners.add(listener);
099        }
100    }
101
102    /**
103     * Removes the specified listener from the multicast chain. Trying to remove a non-existing listener has no effect.
104     *
105     * @param listener The listener to remove, may be {@code null}.
106     */
107    public void remove(RepositoryListener listener) {
108        if (listener != null) {
109            listeners.remove(listener);
110        }
111    }
112
113    @SuppressWarnings("EmptyMethod")
114    protected void handleError(RepositoryEvent event, RepositoryListener listener, RuntimeException error) {
115        // default just swallows errors
116    }
117
118    @Override
119    public void artifactDeployed(RepositoryEvent event) {
120        requireNonNull(event, "event cannot be null");
121        for (RepositoryListener listener : listeners) {
122            try {
123                listener.artifactDeployed(event);
124            } catch (RuntimeException e) {
125                handleError(event, listener, e);
126            }
127        }
128    }
129
130    @Override
131    public void artifactDeploying(RepositoryEvent event) {
132        requireNonNull(event, "event cannot be null");
133        for (RepositoryListener listener : listeners) {
134            try {
135                listener.artifactDeploying(event);
136            } catch (RuntimeException e) {
137                handleError(event, listener, e);
138            }
139        }
140    }
141
142    @Override
143    public void artifactDescriptorInvalid(RepositoryEvent event) {
144        requireNonNull(event, "event cannot be null");
145        for (RepositoryListener listener : listeners) {
146            try {
147                listener.artifactDescriptorInvalid(event);
148            } catch (RuntimeException e) {
149                handleError(event, listener, e);
150            }
151        }
152    }
153
154    @Override
155    public void artifactDescriptorMissing(RepositoryEvent event) {
156        requireNonNull(event, "event cannot be null");
157        for (RepositoryListener listener : listeners) {
158            try {
159                listener.artifactDescriptorMissing(event);
160            } catch (RuntimeException e) {
161                handleError(event, listener, e);
162            }
163        }
164    }
165
166    @Override
167    public void artifactDownloaded(RepositoryEvent event) {
168        requireNonNull(event, "event cannot be null");
169        for (RepositoryListener listener : listeners) {
170            try {
171                listener.artifactDownloaded(event);
172            } catch (RuntimeException e) {
173                handleError(event, listener, e);
174            }
175        }
176    }
177
178    @Override
179    public void artifactDownloading(RepositoryEvent event) {
180        requireNonNull(event, "event cannot be null");
181        for (RepositoryListener listener : listeners) {
182            try {
183                listener.artifactDownloading(event);
184            } catch (RuntimeException e) {
185                handleError(event, listener, e);
186            }
187        }
188    }
189
190    @Override
191    public void artifactInstalled(RepositoryEvent event) {
192        requireNonNull(event, "event cannot be null");
193        for (RepositoryListener listener : listeners) {
194            try {
195                listener.artifactInstalled(event);
196            } catch (RuntimeException e) {
197                handleError(event, listener, e);
198            }
199        }
200    }
201
202    @Override
203    public void artifactInstalling(RepositoryEvent event) {
204        requireNonNull(event, "event cannot be null");
205        for (RepositoryListener listener : listeners) {
206            try {
207                listener.artifactInstalling(event);
208            } catch (RuntimeException e) {
209                handleError(event, listener, e);
210            }
211        }
212    }
213
214    @Override
215    public void artifactResolved(RepositoryEvent event) {
216        requireNonNull(event, "event cannot be null");
217        for (RepositoryListener listener : listeners) {
218            try {
219                listener.artifactResolved(event);
220            } catch (RuntimeException e) {
221                handleError(event, listener, e);
222            }
223        }
224    }
225
226    @Override
227    public void artifactResolving(RepositoryEvent event) {
228        requireNonNull(event, "event cannot be null");
229        for (RepositoryListener listener : listeners) {
230            try {
231                listener.artifactResolving(event);
232            } catch (RuntimeException e) {
233                handleError(event, listener, e);
234            }
235        }
236    }
237
238    @Override
239    public void metadataDeployed(RepositoryEvent event) {
240        requireNonNull(event, "event cannot be null");
241        for (RepositoryListener listener : listeners) {
242            try {
243                listener.metadataDeployed(event);
244            } catch (RuntimeException e) {
245                handleError(event, listener, e);
246            }
247        }
248    }
249
250    @Override
251    public void metadataDeploying(RepositoryEvent event) {
252        requireNonNull(event, "event cannot be null");
253        for (RepositoryListener listener : listeners) {
254            try {
255                listener.metadataDeploying(event);
256            } catch (RuntimeException e) {
257                handleError(event, listener, e);
258            }
259        }
260    }
261
262    @Override
263    public void metadataDownloaded(RepositoryEvent event) {
264        requireNonNull(event, "event cannot be null");
265        for (RepositoryListener listener : listeners) {
266            try {
267                listener.metadataDownloaded(event);
268            } catch (RuntimeException e) {
269                handleError(event, listener, e);
270            }
271        }
272    }
273
274    @Override
275    public void metadataDownloading(RepositoryEvent event) {
276        requireNonNull(event, "event cannot be null");
277        for (RepositoryListener listener : listeners) {
278            try {
279                listener.metadataDownloading(event);
280            } catch (RuntimeException e) {
281                handleError(event, listener, e);
282            }
283        }
284    }
285
286    @Override
287    public void metadataInstalled(RepositoryEvent event) {
288        requireNonNull(event, "event cannot be null");
289        for (RepositoryListener listener : listeners) {
290            try {
291                listener.metadataInstalled(event);
292            } catch (RuntimeException e) {
293                handleError(event, listener, e);
294            }
295        }
296    }
297
298    @Override
299    public void metadataInstalling(RepositoryEvent event) {
300        requireNonNull(event, "event cannot be null");
301        for (RepositoryListener listener : listeners) {
302            try {
303                listener.metadataInstalling(event);
304            } catch (RuntimeException e) {
305                handleError(event, listener, e);
306            }
307        }
308    }
309
310    @Override
311    public void metadataInvalid(RepositoryEvent event) {
312        requireNonNull(event, "event cannot be null");
313        for (RepositoryListener listener : listeners) {
314            try {
315                listener.metadataInvalid(event);
316            } catch (RuntimeException e) {
317                handleError(event, listener, e);
318            }
319        }
320    }
321
322    @Override
323    public void metadataResolved(RepositoryEvent event) {
324        requireNonNull(event, "event cannot be null");
325        for (RepositoryListener listener : listeners) {
326            try {
327                listener.metadataResolved(event);
328            } catch (RuntimeException e) {
329                handleError(event, listener, e);
330            }
331        }
332    }
333
334    @Override
335    public void metadataResolving(RepositoryEvent event) {
336        requireNonNull(event, "event cannot be null");
337        for (RepositoryListener listener : listeners) {
338            try {
339                listener.metadataResolving(event);
340            } catch (RuntimeException e) {
341                handleError(event, listener, e);
342            }
343        }
344    }
345}