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