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.util.listener;
20  
21  import java.util.Arrays;
22  import java.util.Collection;
23  import java.util.List;
24  import java.util.concurrent.CopyOnWriteArrayList;
25  
26  import org.eclipse.aether.transfer.AbstractTransferListener;
27  import org.eclipse.aether.transfer.TransferCancelledException;
28  import org.eclipse.aether.transfer.TransferEvent;
29  import org.eclipse.aether.transfer.TransferListener;
30  
31  import static java.util.Objects.requireNonNull;
32  
33  /**
34   * A transfer listener that delegates to zero or more other listeners (multicast). The list of target listeners is
35   * thread-safe, i.e. target listeners can be added or removed by any thread at any time.
36   */
37  public class ChainedTransferListener extends AbstractTransferListener {
38  
39      private final List<TransferListener> listeners = new CopyOnWriteArrayList<>();
40  
41      /**
42       * Creates a new multicast listener that delegates to the specified listeners. In contrast to the constructor, this
43       * factory method will avoid creating an actual chained listener if one of the specified readers is actually
44       * {@code null}.
45       *
46       * @param listener1 The first listener, may be {@code null}.
47       * @param listener2 The second listener, may be {@code null}.
48       * @return The chained listener or {@code null} if no listener was supplied.
49       */
50      public static TransferListener newInstance(TransferListener listener1, TransferListener listener2) {
51          if (listener1 == null) {
52              return listener2;
53          } else if (listener2 == null) {
54              return listener1;
55          }
56          return new ChainedTransferListener(listener1, listener2);
57      }
58  
59      /**
60       * Creates a new multicast listener that delegates to the specified listeners.
61       *
62       * @param listeners The listeners to delegate to, may be {@code null} or empty.
63       */
64      public ChainedTransferListener(TransferListener... listeners) {
65          if (listeners != null) {
66              add(Arrays.asList(listeners));
67          }
68      }
69  
70      /**
71       * Creates a new multicast listener that delegates to the specified listeners.
72       *
73       * @param listeners The listeners to delegate to, may be {@code null} or empty.
74       */
75      public ChainedTransferListener(Collection<? extends TransferListener> listeners) {
76          add(listeners);
77      }
78  
79      /**
80       * Adds the specified listeners to the end of the multicast chain.
81       *
82       * @param listeners The listeners to add, may be {@code null} or empty.
83       */
84      public void add(Collection<? extends TransferListener> listeners) {
85          if (listeners != null) {
86              for (TransferListener listener : listeners) {
87                  add(listener);
88              }
89          }
90      }
91  
92      /**
93       * Adds the specified listener to the end of the multicast chain.
94       *
95       * @param listener The listener to add, may be {@code null}.
96       */
97      public void add(TransferListener listener) {
98          if (listener != null) {
99              listeners.add(listener);
100         }
101     }
102 
103     /**
104      * Removes the specified listener from the multicast chain. Trying to remove a non-existing listener has no effect.
105      *
106      * @param listener The listener to remove, may be {@code null}.
107      */
108     public void remove(TransferListener listener) {
109         if (listener != null) {
110             listeners.remove(listener);
111         }
112     }
113 
114     /**
115      * Invoked when any listener throws, by default is no op, extend if required.
116      */
117     @SuppressWarnings("EmptyMethod")
118     protected void handleError(TransferEvent event, TransferListener listener, RuntimeException error) {
119         // default just swallows errors
120     }
121 
122     @Override
123     public void transferInitiated(TransferEvent event) throws TransferCancelledException {
124         requireNonNull(event, "event cannot be null");
125         for (TransferListener listener : listeners) {
126             try {
127                 listener.transferInitiated(event);
128             } catch (RuntimeException e) {
129                 handleError(event, listener, e);
130             }
131         }
132     }
133 
134     @Override
135     public void transferStarted(TransferEvent event) throws TransferCancelledException {
136         requireNonNull(event, "event cannot be null");
137         for (TransferListener listener : listeners) {
138             try {
139                 listener.transferStarted(event);
140             } catch (RuntimeException e) {
141                 handleError(event, listener, e);
142             }
143         }
144     }
145 
146     @Override
147     public void transferProgressed(TransferEvent event) throws TransferCancelledException {
148         requireNonNull(event, "event cannot be null");
149         for (TransferListener listener : listeners) {
150             try {
151                 listener.transferProgressed(event);
152             } catch (RuntimeException e) {
153                 handleError(event, listener, e);
154             }
155         }
156     }
157 
158     @Override
159     public void transferCorrupted(TransferEvent event) throws TransferCancelledException {
160         requireNonNull(event, "event cannot be null");
161         for (TransferListener listener : listeners) {
162             try {
163                 listener.transferCorrupted(event);
164             } catch (RuntimeException e) {
165                 handleError(event, listener, e);
166             }
167         }
168     }
169 
170     @Override
171     public void transferSucceeded(TransferEvent event) {
172         requireNonNull(event, "event cannot be null");
173         for (TransferListener listener : listeners) {
174             try {
175                 listener.transferSucceeded(event);
176             } catch (RuntimeException e) {
177                 handleError(event, listener, e);
178             }
179         }
180     }
181 
182     @Override
183     public void transferFailed(TransferEvent event) {
184         requireNonNull(event, "event cannot be null");
185         for (TransferListener listener : listeners) {
186             try {
187                 listener.transferFailed(event);
188             } catch (RuntimeException e) {
189                 handleError(event, listener, e);
190             }
191         }
192     }
193 }