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 final 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     @SuppressWarnings("EmptyMethod")
115     protected void handleError(TransferEvent event, TransferListener listener, RuntimeException error) {
116         // default just swallows errors
117     }
118 
119     @Override
120     public void transferInitiated(TransferEvent event) throws TransferCancelledException {
121         requireNonNull(event, "event cannot be null");
122         for (TransferListener listener : listeners) {
123             try {
124                 listener.transferInitiated(event);
125             } catch (RuntimeException e) {
126                 handleError(event, listener, e);
127             }
128         }
129     }
130 
131     @Override
132     public void transferStarted(TransferEvent event) throws TransferCancelledException {
133         requireNonNull(event, "event cannot be null");
134         for (TransferListener listener : listeners) {
135             try {
136                 listener.transferStarted(event);
137             } catch (RuntimeException e) {
138                 handleError(event, listener, e);
139             }
140         }
141     }
142 
143     @Override
144     public void transferProgressed(TransferEvent event) throws TransferCancelledException {
145         requireNonNull(event, "event cannot be null");
146         for (TransferListener listener : listeners) {
147             try {
148                 listener.transferProgressed(event);
149             } catch (RuntimeException e) {
150                 handleError(event, listener, e);
151             }
152         }
153     }
154 
155     @Override
156     public void transferCorrupted(TransferEvent event) throws TransferCancelledException {
157         requireNonNull(event, "event cannot be null");
158         for (TransferListener listener : listeners) {
159             try {
160                 listener.transferCorrupted(event);
161             } catch (RuntimeException e) {
162                 handleError(event, listener, e);
163             }
164         }
165     }
166 
167     @Override
168     public void transferSucceeded(TransferEvent event) {
169         requireNonNull(event, "event cannot be null");
170         for (TransferListener listener : listeners) {
171             try {
172                 listener.transferSucceeded(event);
173             } catch (RuntimeException e) {
174                 handleError(event, listener, e);
175             }
176         }
177     }
178 
179     @Override
180     public void transferFailed(TransferEvent event) {
181         requireNonNull(event, "event cannot be null");
182         for (TransferListener listener : listeners) {
183             try {
184                 listener.transferFailed(event);
185             } catch (RuntimeException e) {
186                 handleError(event, listener, e);
187             }
188         }
189     }
190 }