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 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    /**
114     * Invoked when any listener throws, by default is no op, extend if required.
115     */
116    @SuppressWarnings("EmptyMethod")
117    protected void handleError(RepositoryEvent event, RepositoryListener listener, RuntimeException error) {
118        // default just swallows errors
119    }
120
121    @Override
122    public void artifactDeployed(RepositoryEvent event) {
123        requireNonNull(event, "event cannot be null");
124        for (RepositoryListener listener : listeners) {
125            try {
126                listener.artifactDeployed(event);
127            } catch (RuntimeException e) {
128                handleError(event, listener, e);
129            }
130        }
131    }
132
133    @Override
134    public void artifactDeploying(RepositoryEvent event) {
135        requireNonNull(event, "event cannot be null");
136        for (RepositoryListener listener : listeners) {
137            try {
138                listener.artifactDeploying(event);
139            } catch (RuntimeException e) {
140                handleError(event, listener, e);
141            }
142        }
143    }
144
145    @Override
146    public void artifactDescriptorInvalid(RepositoryEvent event) {
147        requireNonNull(event, "event cannot be null");
148        for (RepositoryListener listener : listeners) {
149            try {
150                listener.artifactDescriptorInvalid(event);
151            } catch (RuntimeException e) {
152                handleError(event, listener, e);
153            }
154        }
155    }
156
157    @Override
158    public void artifactDescriptorMissing(RepositoryEvent event) {
159        requireNonNull(event, "event cannot be null");
160        for (RepositoryListener listener : listeners) {
161            try {
162                listener.artifactDescriptorMissing(event);
163            } catch (RuntimeException e) {
164                handleError(event, listener, e);
165            }
166        }
167    }
168
169    @Override
170    public void artifactDownloaded(RepositoryEvent event) {
171        requireNonNull(event, "event cannot be null");
172        for (RepositoryListener listener : listeners) {
173            try {
174                listener.artifactDownloaded(event);
175            } catch (RuntimeException e) {
176                handleError(event, listener, e);
177            }
178        }
179    }
180
181    @Override
182    public void artifactDownloading(RepositoryEvent event) {
183        requireNonNull(event, "event cannot be null");
184        for (RepositoryListener listener : listeners) {
185            try {
186                listener.artifactDownloading(event);
187            } catch (RuntimeException e) {
188                handleError(event, listener, e);
189            }
190        }
191    }
192
193    @Override
194    public void artifactInstalled(RepositoryEvent event) {
195        requireNonNull(event, "event cannot be null");
196        for (RepositoryListener listener : listeners) {
197            try {
198                listener.artifactInstalled(event);
199            } catch (RuntimeException e) {
200                handleError(event, listener, e);
201            }
202        }
203    }
204
205    @Override
206    public void artifactInstalling(RepositoryEvent event) {
207        requireNonNull(event, "event cannot be null");
208        for (RepositoryListener listener : listeners) {
209            try {
210                listener.artifactInstalling(event);
211            } catch (RuntimeException e) {
212                handleError(event, listener, e);
213            }
214        }
215    }
216
217    @Override
218    public void artifactResolved(RepositoryEvent event) {
219        requireNonNull(event, "event cannot be null");
220        for (RepositoryListener listener : listeners) {
221            try {
222                listener.artifactResolved(event);
223            } catch (RuntimeException e) {
224                handleError(event, listener, e);
225            }
226        }
227    }
228
229    @Override
230    public void artifactResolving(RepositoryEvent event) {
231        requireNonNull(event, "event cannot be null");
232        for (RepositoryListener listener : listeners) {
233            try {
234                listener.artifactResolving(event);
235            } catch (RuntimeException e) {
236                handleError(event, listener, e);
237            }
238        }
239    }
240
241    @Override
242    public void metadataDeployed(RepositoryEvent event) {
243        requireNonNull(event, "event cannot be null");
244        for (RepositoryListener listener : listeners) {
245            try {
246                listener.metadataDeployed(event);
247            } catch (RuntimeException e) {
248                handleError(event, listener, e);
249            }
250        }
251    }
252
253    @Override
254    public void metadataDeploying(RepositoryEvent event) {
255        requireNonNull(event, "event cannot be null");
256        for (RepositoryListener listener : listeners) {
257            try {
258                listener.metadataDeploying(event);
259            } catch (RuntimeException e) {
260                handleError(event, listener, e);
261            }
262        }
263    }
264
265    @Override
266    public void metadataDownloaded(RepositoryEvent event) {
267        requireNonNull(event, "event cannot be null");
268        for (RepositoryListener listener : listeners) {
269            try {
270                listener.metadataDownloaded(event);
271            } catch (RuntimeException e) {
272                handleError(event, listener, e);
273            }
274        }
275    }
276
277    @Override
278    public void metadataDownloading(RepositoryEvent event) {
279        requireNonNull(event, "event cannot be null");
280        for (RepositoryListener listener : listeners) {
281            try {
282                listener.metadataDownloading(event);
283            } catch (RuntimeException e) {
284                handleError(event, listener, e);
285            }
286        }
287    }
288
289    @Override
290    public void metadataInstalled(RepositoryEvent event) {
291        requireNonNull(event, "event cannot be null");
292        for (RepositoryListener listener : listeners) {
293            try {
294                listener.metadataInstalled(event);
295            } catch (RuntimeException e) {
296                handleError(event, listener, e);
297            }
298        }
299    }
300
301    @Override
302    public void metadataInstalling(RepositoryEvent event) {
303        requireNonNull(event, "event cannot be null");
304        for (RepositoryListener listener : listeners) {
305            try {
306                listener.metadataInstalling(event);
307            } catch (RuntimeException e) {
308                handleError(event, listener, e);
309            }
310        }
311    }
312
313    @Override
314    public void metadataInvalid(RepositoryEvent event) {
315        requireNonNull(event, "event cannot be null");
316        for (RepositoryListener listener : listeners) {
317            try {
318                listener.metadataInvalid(event);
319            } catch (RuntimeException e) {
320                handleError(event, listener, e);
321            }
322        }
323    }
324
325    @Override
326    public void metadataResolved(RepositoryEvent event) {
327        requireNonNull(event, "event cannot be null");
328        for (RepositoryListener listener : listeners) {
329            try {
330                listener.metadataResolved(event);
331            } catch (RuntimeException e) {
332                handleError(event, listener, e);
333            }
334        }
335    }
336
337    @Override
338    public void metadataResolving(RepositoryEvent event) {
339        requireNonNull(event, "event cannot be null");
340        for (RepositoryListener listener : listeners) {
341            try {
342                listener.metadataResolving(event);
343            } catch (RuntimeException e) {
344                handleError(event, listener, e);
345            }
346        }
347    }
348}