001package org.eclipse.aether.util.listener;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 * 
012 *  http://www.apache.org/licenses/LICENSE-2.0
013 * 
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.util.Arrays;
023import java.util.Collection;
024import java.util.List;
025import java.util.concurrent.CopyOnWriteArrayList;
026
027import org.eclipse.aether.transfer.AbstractTransferListener;
028import org.eclipse.aether.transfer.TransferCancelledException;
029import org.eclipse.aether.transfer.TransferEvent;
030import org.eclipse.aether.transfer.TransferListener;
031
032import static java.util.Objects.requireNonNull;
033
034/**
035 * A transfer 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 final class ChainedTransferListener
039    extends AbstractTransferListener
040{
041
042    private final List<TransferListener> listeners = new CopyOnWriteArrayList<>();
043
044    /**
045     * Creates a new multicast listener that delegates to the specified listeners. In contrast to the constructor, this
046     * factory method will avoid creating an actual chained listener if one of the specified readers is actually
047     * {@code null}.
048     * 
049     * @param listener1 The first listener, may be {@code null}.
050     * @param listener2 The second listener, may be {@code null}.
051     * @return The chained listener or {@code null} if no listener was supplied.
052     */
053    public static TransferListener newInstance( TransferListener listener1, TransferListener listener2 )
054    {
055        if ( listener1 == null )
056        {
057            return listener2;
058        }
059        else if ( listener2 == null )
060        {
061            return listener1;
062        }
063        return new ChainedTransferListener( listener1, listener2 );
064    }
065
066    /**
067     * Creates a new multicast listener that delegates to the specified listeners.
068     * 
069     * @param listeners The listeners to delegate to, may be {@code null} or empty.
070     */
071    public ChainedTransferListener( TransferListener... listeners )
072    {
073        if ( listeners != null )
074        {
075            add( Arrays.asList( listeners ) );
076        }
077    }
078
079    /**
080     * Creates a new multicast listener that delegates to the specified listeners.
081     * 
082     * @param listeners The listeners to delegate to, may be {@code null} or empty.
083     */
084    public ChainedTransferListener( Collection<? extends TransferListener> listeners )
085    {
086        add( listeners );
087    }
088
089    /**
090     * Adds the specified listeners to the end of the multicast chain.
091     * 
092     * @param listeners The listeners to add, may be {@code null} or empty.
093     */
094    public void add( Collection<? extends TransferListener> listeners )
095    {
096        if ( listeners != null )
097        {
098            for ( TransferListener listener : listeners )
099            {
100                add( listener );
101            }
102        }
103    }
104
105    /**
106     * Adds the specified listener to the end of the multicast chain.
107     * 
108     * @param listener The listener to add, may be {@code null}.
109     */
110    public void add( TransferListener listener )
111    {
112        if ( listener != null )
113        {
114            listeners.add( listener );
115        }
116    }
117
118    /**
119     * Removes the specified listener from the multicast chain. Trying to remove a non-existing listener has no effect.
120     * 
121     * @param listener The listener to remove, may be {@code null}.
122     */
123    public void remove( TransferListener listener )
124    {
125        if ( listener != null )
126        {
127            listeners.remove( listener );
128        }
129    }
130
131    @SuppressWarnings( "EmptyMethod" )
132    protected void handleError( TransferEvent event, TransferListener listener, RuntimeException error )
133    {
134        // default just swallows errors
135    }
136
137    @Override
138    public void transferInitiated( TransferEvent event )
139        throws TransferCancelledException
140    {
141        requireNonNull( event, "event cannot be null" );
142        for ( TransferListener listener : listeners )
143        {
144            try
145            {
146                listener.transferInitiated( event );
147            }
148            catch ( RuntimeException e )
149            {
150                handleError( event, listener, e );
151            }
152        }
153    }
154
155    @Override
156    public void transferStarted( TransferEvent event )
157        throws TransferCancelledException
158    {
159        requireNonNull( event, "event cannot be null" );
160        for ( TransferListener listener : listeners )
161        {
162            try
163            {
164                listener.transferStarted( event );
165            }
166            catch ( RuntimeException e )
167            {
168                handleError( event, listener, e );
169            }
170        }
171    }
172
173    @Override
174    public void transferProgressed( TransferEvent event )
175        throws TransferCancelledException
176    {
177        requireNonNull( event, "event cannot be null" );
178        for ( TransferListener listener : listeners )
179        {
180            try
181            {
182                listener.transferProgressed( event );
183            }
184            catch ( RuntimeException e )
185            {
186                handleError( event, listener, e );
187            }
188        }
189    }
190
191    @Override
192    public void transferCorrupted( TransferEvent event )
193        throws TransferCancelledException
194    {
195        requireNonNull( event, "event cannot be null" );
196        for ( TransferListener listener : listeners )
197        {
198            try
199            {
200                listener.transferCorrupted( event );
201            }
202            catch ( RuntimeException e )
203            {
204                handleError( event, listener, e );
205            }
206        }
207    }
208
209    @Override
210    public void transferSucceeded( TransferEvent event )
211    {
212        requireNonNull( event, "event cannot be null" );
213        for ( TransferListener listener : listeners )
214        {
215            try
216            {
217                listener.transferSucceeded( event );
218            }
219            catch ( RuntimeException e )
220            {
221                handleError( event, listener, e );
222            }
223        }
224    }
225
226    @Override
227    public void transferFailed( TransferEvent event )
228    {
229        requireNonNull( event, "event cannot be null" );
230        for ( TransferListener listener : listeners )
231        {
232            try
233            {
234                listener.transferFailed( event );
235            }
236            catch ( RuntimeException e )
237            {
238                handleError( event, listener, e );
239            }
240        }
241    }
242
243}