]> source.dussan.org Git - archiva.git/blob
a2521ef371d81d3e8d738fdad161afdfd17dda8a
[archiva.git] /
1 package org.apache.maven.archiva.proxy;
2
3 /*
4  * Licensed to the Apache Software Foundation (ASF) under one
5  * or more contributor license agreements.  See the NOTICE file
6  * distributed with this work for additional information
7  * regarding copyright ownership.  The ASF licenses this file
8  * to you under the Apache License, Version 2.0 (the
9  * "License"); you may not use this file except in compliance
10  * with the License.  You may obtain a copy of the License at
11  *
12  *  http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  * KIND, either express or implied.  See the License for the
18  * specific language governing permissions and limitations
19  * under the License.
20  */
21
22 import org.apache.commons.io.FileUtils;
23 import org.apache.maven.archiva.configuration.ArchivaConfiguration;
24 import org.apache.maven.archiva.configuration.NetworkProxyConfiguration;
25 import org.apache.maven.archiva.configuration.ProxyConnectorConfiguration;
26 import org.apache.maven.archiva.configuration.RepositoryConfiguration;
27 import org.apache.maven.archiva.model.ArchivaRepository;
28 import org.apache.maven.archiva.model.ArtifactReference;
29 import org.apache.maven.archiva.model.ProjectReference;
30 import org.apache.maven.archiva.model.VersionedReference;
31 import org.apache.maven.archiva.policies.DownloadPolicy;
32 import org.apache.maven.archiva.policies.urlcache.UrlFailureCache;
33 import org.apache.maven.archiva.repository.layout.BidirectionalRepositoryLayout;
34 import org.apache.maven.archiva.repository.layout.BidirectionalRepositoryLayoutFactory;
35 import org.apache.maven.archiva.repository.layout.LayoutException;
36 import org.apache.maven.wagon.ConnectionException;
37 import org.apache.maven.wagon.ResourceDoesNotExistException;
38 import org.apache.maven.wagon.Wagon;
39 import org.apache.maven.wagon.WagonException;
40 import org.apache.maven.wagon.authentication.AuthenticationException;
41 import org.apache.maven.wagon.proxy.ProxyInfo;
42 import org.apache.maven.wagon.repository.Repository;
43 import org.codehaus.plexus.logging.AbstractLogEnabled;
44 import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
45 import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
46 import org.codehaus.plexus.registry.Registry;
47 import org.codehaus.plexus.registry.RegistryListener;
48 import org.codehaus.plexus.util.SelectorUtils;
49
50 import java.io.File;
51 import java.io.IOException;
52 import java.util.ArrayList;
53 import java.util.Collection;
54 import java.util.Collections;
55 import java.util.HashMap;
56 import java.util.Iterator;
57 import java.util.List;
58 import java.util.Map;
59 import java.util.Properties;
60 import java.util.Map.Entry;
61
62 /**
63  * DefaultRepositoryProxyConnectors 
64  *
65  * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
66  * @version $Id$
67  * 
68  * @plexus.component role-hint="default"
69  */
70 public class DefaultRepositoryProxyConnectors
71     extends AbstractLogEnabled
72     implements RepositoryProxyConnectors, RegistryListener, Initializable
73 {
74     /**
75      * @plexus.requirement
76      */
77     private ArchivaConfiguration archivaConfiguration;
78
79     /**
80      * @plexus.requirement role="org.apache.maven.wagon.Wagon"
81      */
82     private Map/*<String,Wagon>*/wagons;
83
84     /**
85      * @plexus.requirement
86      */
87     private BidirectionalRepositoryLayoutFactory layoutFactory;
88
89     /**
90      * @plexus.requirement role="org.apache.maven.archiva.policies.PreDownloadPolicy"
91      */
92     private Map preDownloadPolicies;
93
94     /**
95      * @plexus.requirement role="org.apache.maven.archiva.policies.PostDownloadPolicy"
96      */
97     private Map postDownloadPolicies;
98
99     /**
100      * @plexus.requirement role-hint="default"
101      */
102     private UrlFailureCache urlFailureCache;
103
104     private Map proxyConnectorMap = new HashMap();
105
106     private Map networkProxyMap = new HashMap();
107
108     private List propertyNameTriggers = new ArrayList();
109
110     public File fetchFromProxies( ArchivaRepository repository, ArtifactReference artifact )
111         throws ProxyException
112     {
113         if ( !repository.isManaged() )
114         {
115             throw new ProxyException( "Can only proxy managed repositories." );
116         }
117
118         File localFile;
119         try
120         {
121             BidirectionalRepositoryLayout sourceLayout = layoutFactory.getLayout( repository.getLayoutType() );
122             String sourcePath = sourceLayout.toPath( artifact );
123             localFile = new File( repository.getUrl().getPath(), sourcePath );
124         }
125         catch ( LayoutException e )
126         {
127             throw new ProxyException( "Unable to proxy due to bad source repository layout definition: "
128                 + e.getMessage(), e );
129         }
130
131         Properties requestProperties = new Properties();
132         requestProperties.setProperty( "version", artifact.getVersion() );
133
134         List connectors = getProxyConnectors( repository );
135         Iterator it = connectors.iterator();
136         while ( it.hasNext() )
137         {
138             ProxyConnector connector = (ProxyConnector) it.next();
139             getLogger().debug( "Attempting connector: " + connector );
140             ArchivaRepository targetRepository = connector.getTargetRepository();
141             try
142             {
143                 BidirectionalRepositoryLayout targetLayout = layoutFactory.getLayout( targetRepository.getLayoutType() );
144                 String targetPath = targetLayout.toPath( artifact );
145
146                 getLogger().debug(
147                                    "Using target repository: " + targetRepository.getId() + " - layout: "
148                                        + targetRepository.getLayoutType() + " - targetPath: " + targetPath );
149
150                 File downloadedFile = transferFile( connector, targetRepository, targetPath, localFile,
151                                                     requestProperties );
152
153                 if ( fileExists( downloadedFile ) )
154                 {
155                     getLogger().info( "Successfully transfered: " + downloadedFile.getAbsolutePath() );
156                     return downloadedFile;
157                 }
158             }
159             catch ( LayoutException e )
160             {
161                 getLogger().error( "Unable to proxy due to bad layout definition: " + e.getMessage(), e );
162                 return null;
163             }
164         }
165
166         return null;
167     }
168     
169     public File fetchFromProxies( ArchivaRepository repository, VersionedReference metadata )
170         throws ProxyException
171     {
172         if ( !repository.isManaged() )
173         {
174             throw new ProxyException( "Can only proxy managed repositories." );
175         }
176
177         File localFile;
178         try
179         {
180             BidirectionalRepositoryLayout sourceLayout = layoutFactory.getLayout( repository.getLayoutType() );
181             String sourcePath = sourceLayout.toPath( metadata );
182             localFile = new File( repository.getUrl().getPath(), sourcePath );
183         }
184         catch ( LayoutException e )
185         {
186             throw new ProxyException( "Unable to proxy due to bad source repository layout definition: "
187                 + e.getMessage(), e );
188         }
189
190         Properties requestProperties = new Properties();
191
192         List connectors = getProxyConnectors( repository );
193         Iterator it = connectors.iterator();
194         while ( it.hasNext() )
195         {
196             ProxyConnector connector = (ProxyConnector) it.next();
197             ArchivaRepository targetRepository = connector.getTargetRepository();
198             try
199             {
200                 BidirectionalRepositoryLayout targetLayout = layoutFactory.getLayout( targetRepository.getLayoutType() );
201                 String targetPath = targetLayout.toPath( metadata );
202
203                 File downloadedFile = transferFile( connector, targetRepository, targetPath, localFile,
204                                                     requestProperties );
205
206                 if ( fileExists( downloadedFile ) )
207                 {
208                     getLogger().info( "Successfully transfered: " + downloadedFile.getAbsolutePath() );
209                     return downloadedFile;
210                 }
211             }
212             catch ( LayoutException e )
213             {
214                 getLogger().error( "Unable to proxy due to bad layout definition: " + e.getMessage(), e );
215                 return null;
216             }
217         }
218
219         return null;
220     }
221
222     public File fetchFromProxies( ArchivaRepository repository, ProjectReference metadata )
223         throws ProxyException
224     {
225         if ( !repository.isManaged() )
226         {
227             throw new ProxyException( "Can only proxy managed repositories." );
228         }
229
230         File localFile;
231         try
232         {
233             BidirectionalRepositoryLayout sourceLayout = layoutFactory.getLayout( repository.getLayoutType() );
234             String sourcePath = sourceLayout.toPath( metadata );
235             localFile = new File( repository.getUrl().getPath(), sourcePath );
236         }
237         catch ( LayoutException e )
238         {
239             throw new ProxyException( "Unable to proxy due to bad source repository layout definition: "
240                 + e.getMessage(), e );
241         }
242
243         Properties requestProperties = new Properties();
244
245         List connectors = getProxyConnectors( repository );
246         Iterator it = connectors.iterator();
247         while ( it.hasNext() )
248         {
249             ProxyConnector connector = (ProxyConnector) it.next();
250             ArchivaRepository targetRepository = connector.getTargetRepository();
251             try
252             {
253                 BidirectionalRepositoryLayout targetLayout = layoutFactory.getLayout( targetRepository.getLayoutType() );
254                 String targetPath = targetLayout.toPath( metadata );
255
256                 File downloadedFile = transferFile( connector, targetRepository, targetPath, localFile,
257                                                     requestProperties );
258
259                 if ( fileExists( downloadedFile ) )
260                 {
261                     getLogger().info( "Successfully transfered: " + downloadedFile.getAbsolutePath() );
262                     return downloadedFile;
263                 }
264             }
265             catch ( LayoutException e )
266             {
267                 getLogger().error( "Unable to proxy due to bad layout definition: " + e.getMessage(), e );
268                 return null;
269             }
270         }
271
272         return null;
273     }
274
275     private boolean fileExists( File file )
276     {
277         if ( file == null )
278         {
279             return false;
280         }
281
282         if ( !file.exists() )
283         {
284             return false;
285         }
286
287         if ( !file.isFile() )
288         {
289             return false;
290         }
291
292         return true;
293     }
294
295     /**
296      * Perform the transfer of the file.
297      * 
298      * @param connector
299      * @param targetRepository
300      * @param targetPath
301      * @param localFile
302      * @param requestProperties
303      * @return
304      * @throws ProxyException 
305      */
306     private File transferFile( ProxyConnector connector, ArchivaRepository targetRepository, String targetPath,
307                                File localFile, Properties requestProperties )
308         throws ProxyException
309     {
310         String url = targetRepository.getUrl().toString() + targetPath;
311         requestProperties.setProperty( "url", url );
312
313         // Handle pre-download policy
314         if ( !applyPolicies( connector.getPolicies(), this.preDownloadPolicies, requestProperties, localFile ) )
315         {
316             getLogger().info( "Failed pre-download policies - " + localFile.getAbsolutePath() );
317
318             if ( fileExists( localFile ) )
319             {
320                 return localFile;
321             }
322
323             return null;
324         }
325
326         // Is a whitelist defined?
327         if ( !isEmpty( connector.getWhitelist() ) )
328         {
329             // Path must belong to whitelist.
330             if ( !matchesPattern( targetPath, connector.getWhitelist() ) )
331             {
332                 getLogger().debug( "Path [" + targetPath + "] is not part of defined whitelist (skipping transfer)." );
333                 return null;
334             }
335         }
336
337         // Is target path part of blacklist?
338         if ( matchesPattern( targetPath, connector.getBlacklist() ) )
339         {
340             getLogger().debug( "Path [" + targetPath + "] is part of blacklist (skipping transfer)." );
341             return null;
342         }
343
344         Wagon wagon = null;
345         try
346         {
347             String protocol = targetRepository.getUrl().getProtocol();
348             wagon = (Wagon) wagons.get( protocol );
349             if ( wagon == null )
350             {
351                 throw new ProxyException( "Unsupported target repository protocol: " + protocol );
352             }
353
354             boolean connected = connectToRepository( connector, wagon, targetRepository );
355             if ( connected )
356             {
357                 localFile = transferSimpleFile( wagon, targetRepository, targetPath, localFile );
358
359                 transferChecksum( wagon, targetRepository, targetPath, localFile, ".sha1" );
360                 transferChecksum( wagon, targetRepository, targetPath, localFile, ".md5" );
361             }
362         }
363         catch ( ResourceDoesNotExistException e )
364         {
365             // Do not cache url here.
366             return null;
367         }
368         catch ( WagonException e )
369         {
370             urlFailureCache.cacheFailure( url );
371             return null;
372         }
373         finally
374         {
375             if ( wagon != null )
376             {
377                 try
378                 {
379                     wagon.disconnect();
380                 }
381                 catch ( ConnectionException e )
382                 {
383                     getLogger().warn( "Unable to disconnect wagon.", e );
384                 }
385             }
386         }
387
388         // Handle post-download policies.
389         if ( !applyPolicies( connector.getPolicies(), this.postDownloadPolicies, requestProperties, localFile ) )
390         {
391             getLogger().info( "Failed post-download policies - " + localFile.getAbsolutePath() );
392
393             if ( fileExists( localFile ) )
394             {
395                 return localFile;
396             }
397
398             return null;
399         }
400
401         // Everything passes.
402         return localFile;
403     }
404
405     private void transferChecksum( Wagon wagon, ArchivaRepository targetRepository, String targetPath, File localFile,
406                                    String type )
407         throws ProxyException
408     {
409         String url = targetRepository.getUrl().toString() + targetPath;
410
411         // Transfer checksum does not use the policy. 
412         if ( urlFailureCache.hasFailedBefore( url + type ) )
413         {
414             return;
415         }
416
417         try
418         {
419             File hashFile = new File( localFile.getAbsolutePath() + type );
420             transferSimpleFile( wagon, targetRepository, targetPath + type, hashFile );
421             getLogger().debug( "Checksum" + type + " Downloaded: " + hashFile );
422         }
423         catch ( ResourceDoesNotExistException e )
424         {
425             getLogger().debug( "Checksum" + type + " Not Download: " + e.getMessage() );
426         }
427         catch ( WagonException e )
428         {
429             urlFailureCache.cacheFailure( url + type );
430             getLogger().warn( "Transfer failed on checksum: " + url + " : " + e.getMessage(), e );
431         }
432     }
433
434     private File transferSimpleFile( Wagon wagon, ArchivaRepository targetRepository, String targetPath, File localFile )
435         throws ProxyException, WagonException
436     {
437         // Transfer the file.
438         File temp = null;
439
440         try
441         {
442             temp = new File( localFile.getAbsolutePath() + ".tmp" );
443
444             boolean success = false;
445
446             if ( localFile.exists() )
447             {
448                 getLogger().debug( "Retrieving " + targetPath + " from " + targetRepository.getName() );
449                 wagon.get( targetPath, temp );
450                 success = true;
451
452                 if ( temp.exists() )
453                 {
454                     moveTempToTarget( temp, localFile );
455                 }
456
457                 // You wouldn't get here on failure, a WagonException would have been thrown.
458                 getLogger().debug( "Downloaded successfully." );
459             }
460             else
461             {
462                 getLogger().debug( "Retrieving " + targetPath + " from " + targetRepository.getName() + " if updated" );
463                 success = wagon.getIfNewer( targetPath, temp, localFile.lastModified() );
464                 if ( !success )
465                 {
466                     getLogger().debug(
467                                        "Not downloaded, as local file is newer than remote side: "
468                                            + localFile.getAbsolutePath() );
469                 }
470                 else if ( temp.exists() )
471                 {
472                     getLogger().debug( "Downloaded successfully." );
473                     moveTempToTarget( temp, localFile );
474                 }
475             }
476
477             return localFile;
478         }
479         catch ( ResourceDoesNotExistException e )
480         {
481             getLogger().warn( "Resource does not exist: " + e.getMessage() );
482             throw e;
483         }
484         catch ( WagonException e )
485         {
486             getLogger().warn( "Download failure:" + e.getMessage(), e );
487             throw e;
488         }
489         finally
490         {
491             if ( temp != null )
492             {
493                 temp.delete();
494             }
495         }
496     }
497
498     private boolean applyPolicies( Properties policySettings, Map downloadPolicies, Properties request, File localFile )
499     {
500         Iterator it = downloadPolicies.entrySet().iterator();
501         while ( it.hasNext() )
502         {
503             Map.Entry entry = (Entry) it.next();
504             String key = (String) entry.getKey();
505             DownloadPolicy policy = (DownloadPolicy) entry.getValue();
506             String defaultSetting = policy.getDefaultPolicySetting();
507             String setting = policySettings.getProperty( key, defaultSetting );
508
509             getLogger().debug( "Applying [" + key + "] policy with [" + setting + "]" );
510             if ( !policy.applyPolicy( setting, request, localFile ) )
511             {
512                 getLogger().debug( "Didn't pass the [" + key + "] policy." );
513                 return false;
514             }
515         }
516         return true;
517     }
518
519     /**
520      * Used to move the temporary file to its real destination.  This is patterned from the way WagonManager handles
521      * its downloaded files.
522      *
523      * @param temp   The completed download file
524      * @param target The final location of the downloaded file
525      * @throws ProxyException when the temp file cannot replace the target file
526      */
527     private void moveTempToTarget( File temp, File target )
528         throws ProxyException
529     {
530         if ( target.exists() && !target.delete() )
531         {
532             throw new ProxyException( "Unable to overwrite existing target file: " + target.getAbsolutePath() );
533         }
534
535         if ( !temp.renameTo( target ) )
536         {
537             getLogger().warn( "Unable to rename tmp file to its final name... resorting to copy command." );
538
539             try
540             {
541                 FileUtils.copyFile( temp, target );
542             }
543             catch ( IOException e )
544             {
545                 throw new ProxyException( "Cannot copy tmp file to its final location", e );
546             }
547             finally
548             {
549                 temp.delete();
550             }
551         }
552     }
553
554     private boolean connectToRepository( ProxyConnector connector, Wagon wagon, ArchivaRepository targetRepository )
555     {
556         boolean connected = false;
557
558         ProxyInfo networkProxy = null;
559         synchronized ( this.networkProxyMap )
560         {
561             networkProxy = (ProxyInfo) this.networkProxyMap.get( connector.getProxyId() );
562         }
563
564         try
565         {
566             Repository wagonRepository = new Repository( targetRepository.getId(), targetRepository.getUrl().toString() );
567             if ( networkProxy != null )
568             {
569                 wagon.connect( wagonRepository, networkProxy );
570             }
571             else
572             {
573                 wagon.connect( wagonRepository );
574             }
575             connected = true;
576         }
577         catch ( ConnectionException e )
578         {
579             getLogger().info( "Could not connect to " + targetRepository.getName() + ": " + e.getMessage() );
580         }
581         catch ( AuthenticationException e )
582         {
583             getLogger().info( "Could not connect to " + targetRepository.getName() + ": " + e.getMessage() );
584         }
585
586         return connected;
587     }
588
589     private boolean matchesPattern( String path, List patterns )
590     {
591         if ( isEmpty( patterns ) )
592         {
593             return false;
594         }
595
596         Iterator it = patterns.iterator();
597         while ( it.hasNext() )
598         {
599             String pattern = (String) it.next();
600             if ( SelectorUtils.matchPath( pattern, path, false ) )
601             {
602                 return true;
603             }
604         }
605
606         return false;
607     }
608
609     public List getProxyConnectors( ArchivaRepository repository )
610     {
611         synchronized ( this.proxyConnectorMap )
612         {
613             List ret = (List) this.proxyConnectorMap.get( repository.getId() );
614             if ( ret == null )
615             {
616                 return Collections.EMPTY_LIST;
617             }
618             return ret;
619         }
620     }
621
622     public boolean hasProxies( ArchivaRepository repository )
623     {
624         synchronized ( this.proxyConnectorMap )
625         {
626             return this.proxyConnectorMap.containsKey( repository.getId() );
627         }
628     }
629
630     public void afterConfigurationChange( Registry registry, String propertyName, Object propertyValue )
631     {
632         if ( propertyNameTriggers.contains( propertyName ) )
633         {
634             initConnectorsAndNetworkProxies();
635         }
636     }
637
638     public void beforeConfigurationChange( Registry registry, String propertyName, Object propertyValue )
639     {
640         /* do nothing */
641     }
642
643     private void initConnectorsAndNetworkProxies()
644     {
645         Iterator it;
646
647         synchronized ( this.proxyConnectorMap )
648         {
649             this.proxyConnectorMap.clear();
650
651             List proxyConfigs = archivaConfiguration.getConfiguration().getProxyConnectors();
652             it = proxyConfigs.iterator();
653             while ( it.hasNext() )
654             {
655                 ProxyConnectorConfiguration proxyConfig = (ProxyConnectorConfiguration) it.next();
656                 String key = proxyConfig.getSourceRepoId();
657
658                 // Create connector object.
659                 ProxyConnector connector = new ProxyConnector();
660                 connector.setSourceRepository( getRepository( proxyConfig.getSourceRepoId() ) );
661                 connector.setTargetRepository( getRepository( proxyConfig.getTargetRepoId() ) );
662                 connector.setPolicies( proxyConfig.getPolicies() );
663
664                 // Copy any blacklist patterns.
665                 List blacklist = new ArrayList();
666                 if ( !isEmpty( proxyConfig.getBlackListPatterns() ) )
667                 {
668                     blacklist.addAll( proxyConfig.getBlackListPatterns() );
669                 }
670                 connector.setBlacklist( blacklist );
671
672                 // Copy any whitelist patterns.
673                 List whitelist = new ArrayList();
674                 if ( !isEmpty( proxyConfig.getWhiteListPatterns() ) )
675                 {
676                     whitelist.addAll( proxyConfig.getWhiteListPatterns() );
677                 }
678                 connector.setWhitelist( whitelist );
679
680                 // Get other connectors
681                 List connectors = (List) this.proxyConnectorMap.get( key );
682                 if ( connectors == null )
683                 {
684                     // Create if we are the first.
685                     connectors = new ArrayList();
686                 }
687
688                 // Add the connector.
689                 connectors.add( connector );
690
691                 // Set the key to the list of connectors.
692                 this.proxyConnectorMap.put( key, connectors );
693             }
694         }
695
696         synchronized ( this.networkProxyMap )
697         {
698             this.networkProxyMap.clear();
699
700             List networkProxies = archivaConfiguration.getConfiguration().getNetworkProxies();
701             it = networkProxies.iterator();
702             while ( it.hasNext() )
703             {
704                 NetworkProxyConfiguration networkProxyConfig = (NetworkProxyConfiguration) it.next();
705                 String key = networkProxyConfig.getId();
706
707                 ProxyInfo proxy = new ProxyInfo();
708
709                 proxy.setType( networkProxyConfig.getProtocol() );
710                 proxy.setHost( networkProxyConfig.getHost() );
711                 proxy.setPort( networkProxyConfig.getPort() );
712                 proxy.setUserName( networkProxyConfig.getUsername() );
713                 proxy.setPassword( networkProxyConfig.getPassword() );
714
715                 this.networkProxyMap.put( key, proxy );
716             }
717         }
718     }
719
720     private boolean isEmpty( Collection collection )
721     {
722         if ( collection == null )
723         {
724             return true;
725         }
726
727         return collection.isEmpty();
728     }
729
730     private ArchivaRepository getRepository( String repoId )
731     {
732         RepositoryConfiguration repoConfig = archivaConfiguration.getConfiguration().findRepositoryById( repoId );
733         if ( repoConfig == null )
734         {
735             return null;
736         }
737
738         ArchivaRepository repo = new ArchivaRepository( repoConfig.getId(), repoConfig.getName(), repoConfig.getUrl() );
739         repo.getModel().setLayoutName( repoConfig.getLayout() );
740         return repo;
741     }
742
743     public void initialize()
744         throws InitializationException
745     {
746         propertyNameTriggers.add( "repositories" );
747         propertyNameTriggers.add( "repository" );
748         propertyNameTriggers.add( "id" );
749         propertyNameTriggers.add( "name" );
750         propertyNameTriggers.add( "url" );
751         propertyNameTriggers.add( "layout" );
752         propertyNameTriggers.add( "releases" );
753         propertyNameTriggers.add( "snapshots" );
754         propertyNameTriggers.add( "indexed" );
755
756         propertyNameTriggers.add( "proxyConnectors" );
757         propertyNameTriggers.add( "proxyConnector" );
758         propertyNameTriggers.add( "sourceRepoId" );
759         propertyNameTriggers.add( "targetRepoId" );
760         propertyNameTriggers.add( "proxyId" );
761         propertyNameTriggers.add( "snapshotsPolicy" );
762         propertyNameTriggers.add( "releasePolicy" );
763         propertyNameTriggers.add( "checksumPolicy" );
764         propertyNameTriggers.add( "whiteListPatterns" );
765         propertyNameTriggers.add( "whiteListPattern" );
766         propertyNameTriggers.add( "blackListPatterns" );
767         propertyNameTriggers.add( "blackListPattern" );
768
769         propertyNameTriggers.add( "networkProxies" );
770         propertyNameTriggers.add( "networkProxy" );
771         propertyNameTriggers.add( "protocol" );
772         propertyNameTriggers.add( "host" );
773         propertyNameTriggers.add( "port" );
774         propertyNameTriggers.add( "username" );
775         propertyNameTriggers.add( "password" );
776
777         archivaConfiguration.addChangeListener( this );
778         initConnectorsAndNetworkProxies();
779     }
780 }