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
032/**
033 * A transfer 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 final class ChainedTransferListener
037    extends AbstractTransferListener
038{
039
040    private final List<TransferListener> 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 TransferListener newInstance( TransferListener listener1, TransferListener listener2 )
052    {
053        if ( listener1 == null )
054        {
055            return listener2;
056        }
057        else if ( listener2 == null )
058        {
059            return listener1;
060        }
061        return new ChainedTransferListener( listener1, listener2 );
062    }
063
064    /**
065     * Creates a new multicast listener that delegates to the specified listeners.
066     * 
067     * @param listeners The listeners to delegate to, may be {@code null} or empty.
068     */
069    public ChainedTransferListener( TransferListener... listeners )
070    {
071        if ( listeners != null )
072        {
073            add( Arrays.asList( listeners ) );
074        }
075    }
076
077    /**
078     * Creates a new multicast listener that delegates to the specified listeners.
079     * 
080     * @param listeners The listeners to delegate to, may be {@code null} or empty.
081     */
082    public ChainedTransferListener( Collection<? extends TransferListener> listeners )
083    {
084        add( listeners );
085    }
086
087    /**
088     * Adds the specified listeners to the end of the multicast chain.
089     * 
090     * @param listeners The listeners to add, may be {@code null} or empty.
091     */
092    public void add( Collection<? extends TransferListener> listeners )
093    {
094        if ( listeners != null )
095        {
096            for ( TransferListener listener : listeners )
097            {
098                add( listener );
099            }
100        }
101    }
102
103    /**
104     * Adds the specified listener to the end of the multicast chain.
105     * 
106     * @param listener The listener to add, may be {@code null}.
107     */
108    public void add( TransferListener listener )
109    {
110        if ( listener != null )
111        {
112            listeners.add( listener );
113        }
114    }
115
116    /**
117     * Removes the specified listener from the multicast chain. Trying to remove a non-existing listener has no effect.
118     * 
119     * @param listener The listener to remove, may be {@code null}.
120     */
121    public void remove( TransferListener listener )
122    {
123        if ( listener != null )
124        {
125            listeners.remove( listener );
126        }
127    }
128
129    @SuppressWarnings( "EmptyMethod" )
130    protected void handleError( TransferEvent event, TransferListener listener, RuntimeException error )
131    {
132        // default just swallows errors
133    }
134
135    @Override
136    public void transferInitiated( TransferEvent event )
137        throws TransferCancelledException
138    {
139        for ( TransferListener listener : listeners )
140        {
141            try
142            {
143                listener.transferInitiated( event );
144            }
145            catch ( RuntimeException e )
146            {
147                handleError( event, listener, e );
148            }
149        }
150    }
151
152    @Override
153    public void transferStarted( TransferEvent event )
154        throws TransferCancelledException
155    {
156        for ( TransferListener listener : listeners )
157        {
158            try
159            {
160                listener.transferStarted( event );
161            }
162            catch ( RuntimeException e )
163            {
164                handleError( event, listener, e );
165            }
166        }
167    }
168
169    @Override
170    public void transferProgressed( TransferEvent event )
171        throws TransferCancelledException
172    {
173        for ( TransferListener listener : listeners )
174        {
175            try
176            {
177                listener.transferProgressed( event );
178            }
179            catch ( RuntimeException e )
180            {
181                handleError( event, listener, e );
182            }
183        }
184    }
185
186    @Override
187    public void transferCorrupted( TransferEvent event )
188        throws TransferCancelledException
189    {
190        for ( TransferListener listener : listeners )
191        {
192            try
193            {
194                listener.transferCorrupted( event );
195            }
196            catch ( RuntimeException e )
197            {
198                handleError( event, listener, e );
199            }
200        }
201    }
202
203    @Override
204    public void transferSucceeded( TransferEvent event )
205    {
206        for ( TransferListener listener : listeners )
207        {
208            try
209            {
210                listener.transferSucceeded( event );
211            }
212            catch ( RuntimeException e )
213            {
214                handleError( event, listener, e );
215            }
216        }
217    }
218
219    @Override
220    public void transferFailed( TransferEvent event )
221    {
222        for ( TransferListener listener : listeners )
223        {
224            try
225            {
226                listener.transferFailed( event );
227            }
228            catch ( RuntimeException e )
229            {
230                handleError( event, listener, e );
231            }
232        }
233    }
234
235}