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<TransferListener>();
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    protected void handleError( TransferEvent event, TransferListener listener, RuntimeException error )
130    {
131        // default just swallows errors
132    }
133
134    @Override
135    public void transferInitiated( TransferEvent event )
136        throws TransferCancelledException
137    {
138        for ( TransferListener listener : listeners )
139        {
140            try
141            {
142                listener.transferInitiated( event );
143            }
144            catch ( RuntimeException e )
145            {
146                handleError( event, listener, e );
147            }
148        }
149    }
150
151    @Override
152    public void transferStarted( TransferEvent event )
153        throws TransferCancelledException
154    {
155        for ( TransferListener listener : listeners )
156        {
157            try
158            {
159                listener.transferStarted( event );
160            }
161            catch ( RuntimeException e )
162            {
163                handleError( event, listener, e );
164            }
165        }
166    }
167
168    @Override
169    public void transferProgressed( TransferEvent event )
170        throws TransferCancelledException
171    {
172        for ( TransferListener listener : listeners )
173        {
174            try
175            {
176                listener.transferProgressed( event );
177            }
178            catch ( RuntimeException e )
179            {
180                handleError( event, listener, e );
181            }
182        }
183    }
184
185    @Override
186    public void transferCorrupted( TransferEvent event )
187        throws TransferCancelledException
188    {
189        for ( TransferListener listener : listeners )
190        {
191            try
192            {
193                listener.transferCorrupted( event );
194            }
195            catch ( RuntimeException e )
196            {
197                handleError( event, listener, e );
198            }
199        }
200    }
201
202    @Override
203    public void transferSucceeded( TransferEvent event )
204    {
205        for ( TransferListener listener : listeners )
206        {
207            try
208            {
209                listener.transferSucceeded( event );
210            }
211            catch ( RuntimeException e )
212            {
213                handleError( event, listener, e );
214            }
215        }
216    }
217
218    @Override
219    public void transferFailed( TransferEvent event )
220    {
221        for ( TransferListener listener : listeners )
222        {
223            try
224            {
225                listener.transferFailed( event );
226            }
227            catch ( RuntimeException e )
228            {
229                handleError( event, listener, e );
230            }
231        }
232    }
233
234}