1 package org.apache.maven.continuum.xmlrpc.backup;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import com.sampullara.cli.Args;
23 import com.sampullara.cli.Argument;
24 import org.apache.continuum.xmlrpc.release.ContinuumReleaseResult;
25 import org.apache.continuum.xmlrpc.repository.DirectoryPurgeConfiguration;
26 import org.apache.continuum.xmlrpc.repository.LocalRepository;
27 import org.apache.continuum.xmlrpc.repository.RepositoryPurgeConfiguration;
28 import org.apache.log4j.BasicConfigurator;
29 import org.apache.log4j.Level;
30 import org.apache.log4j.Logger;
31 import org.apache.maven.continuum.xmlrpc.client.ContinuumXmlRpcClient;
32 import org.apache.maven.continuum.xmlrpc.project.BuildDefinition;
33 import org.apache.maven.continuum.xmlrpc.project.BuildDefinitionTemplate;
34 import org.apache.maven.continuum.xmlrpc.project.BuildResult;
35 import org.apache.maven.continuum.xmlrpc.project.BuildResultSummary;
36 import org.apache.maven.continuum.xmlrpc.project.Project;
37 import org.apache.maven.continuum.xmlrpc.project.ProjectDependency;
38 import org.apache.maven.continuum.xmlrpc.project.ProjectDeveloper;
39 import org.apache.maven.continuum.xmlrpc.project.ProjectGroup;
40 import org.apache.maven.continuum.xmlrpc.project.ProjectNotifier;
41 import org.apache.maven.continuum.xmlrpc.project.ProjectSummary;
42 import org.apache.maven.continuum.xmlrpc.project.Schedule;
43 import org.apache.maven.continuum.xmlrpc.scm.ChangeFile;
44 import org.apache.maven.continuum.xmlrpc.scm.ChangeSet;
45 import org.apache.maven.continuum.xmlrpc.scm.ScmResult;
46 import org.apache.maven.continuum.xmlrpc.system.Installation;
47 import org.apache.maven.continuum.xmlrpc.system.Profile;
48
49 import java.io.File;
50 import java.io.FileWriter;
51 import java.io.IOException;
52 import java.io.PrintWriter;
53 import java.lang.reflect.Field;
54 import java.net.URL;
55 import java.util.ArrayList;
56 import java.util.Arrays;
57 import java.util.List;
58 import java.util.Map;
59 import java.util.Properties;
60 import java.util.Set;
61
62 public class Backup
63 {
64 private static final Logger LOGGER = Logger.getLogger( Backup.class );
65
66 private static ContinuumXmlRpcClient client;
67
68 private static int indent = 0;
69
70 private static PrintWriter writer;
71
72 public static void main( String[] args )
73 throws Exception
74 {
75 Commands command = new Commands();
76
77 try
78 {
79 Args.parse( command, args );
80
81 if ( command.help )
82 {
83 Args.usage( command );
84 return;
85 }
86 if ( command.version )
87 {
88 System.out.println( "continuum-xmlrpc-backup version: " + getVersion() );
89 return;
90 }
91 if ( command.url == null )
92 {
93 System.out.println( "You must specified the Continuum XMLRPC URL" );
94 Args.usage( command );
95 return;
96 }
97 if ( command.username == null )
98 {
99 System.out.println( "You must specified the Continuum username" );
100 Args.usage( command );
101 return;
102 }
103 if ( command.password == null )
104 {
105 System.out.println( "You must specified the Continuum password" );
106 Args.usage( command );
107 return;
108 }
109 }
110 catch ( IllegalArgumentException e )
111 {
112 System.err.println( e.getMessage() );
113 Args.usage( command );
114 return;
115 }
116
117 BasicConfigurator.configure();
118 if ( command.debug )
119 {
120 Logger.getRootLogger().setLevel( Level.DEBUG );
121 }
122 else
123 {
124 Logger.getRootLogger().setLevel( Level.INFO );
125 }
126
127 LOGGER.info( "Connection to " + command.url + "with username '" + command.username + "'..." );
128 client = new ContinuumXmlRpcClient( command.url, command.username, command.password );
129 LOGGER.info( "connected" );
130
131 File out = command.outputFile;
132 if ( out == null )
133 {
134 out = new File( "backup/builds.xml" );
135 }
136 out.getParentFile().mkdirs();
137
138 if ( !command.overwrite && out.exists() )
139 {
140 System.err.println( out.getAbsolutePath() +
141 " already exists and will not be overwritten unless the -overwrite flag is used." );
142 Args.usage( command );
143 return;
144 }
145
146 writer = new PrintWriter( new FileWriter( out ) );
147
148 writer.println( "<?xml version='1.0' encoding='UTF-8'?>" );
149 startTag( "continuumDatabase", true );
150 backupSystemConfiguration();
151 backupAllSchedules();
152 backupAllInstallations();
153 backupAllProfiles();
154 backupAllBuildDefinitionTemplates();
155 backupAllProjectGroup();
156 backupAllLocalRepositories();
157 backupAllRepositoryPurgeConfigurations();
158 backupAllDirectoryPurgeConfigurations();
159 endTag( "continuumDatabase", true );
160 writer.close();
161 LOGGER.info( "Done." );
162 }
163
164 private static String getVersion()
165 throws IOException
166 {
167 Properties properties = new Properties();
168 properties.load( Backup.class.getResourceAsStream(
169 "/META-INF/maven/org.apache.maven.continuum/continuum-xmlrpc-backup/pom.properties" ) );
170 return properties.getProperty( "version" );
171 }
172
173 private static class Commands
174 {
175
176 @Argument( description = "Display help information", value = "help", alias = "h" )
177 private boolean help;
178
179 @Argument( description = "Display version information", value = "version", alias = "v" )
180 private boolean version;
181
182 @Argument( description = "Continuum XMLRPC URL", value = "url" )
183 private URL url;
184
185 @Argument( description = "Username", value = "username", alias = "u" )
186 private String username;
187
188 @Argument( description = "Password", value = "password", alias = "p" )
189 private String password;
190
191 @Argument( description = "Backup file", value = "outputFile", alias = "o" )
192 private File outputFile;
193
194 @Argument(
195 description = "Whether to overwrite the designated backup file if it already exists in export mode. Default is false.",
196 value = "overwrite" )
197 private boolean overwrite;
198
199 @Argument(
200 description = "Turn on debugging information. Default is off.",
201 value = "debug" )
202 private boolean debug;
203 }
204
205 private static void backupSystemConfiguration()
206 throws Exception
207 {
208 LOGGER.info( "Backup system configuration" );
209 writeObject( client.getSystemConfiguration(), "systemConfiguration", true );
210 }
211
212 private static void backupAllSchedules()
213 throws Exception
214 {
215 LOGGER.info( "Backup schedules" );
216 List<Schedule> schedules = client.getSchedules();
217 if ( schedules != null && !schedules.isEmpty() )
218 {
219 startTag( "schedules", true );
220 for ( Schedule schedule : schedules )
221 {
222 LOGGER.debug( "Backup schedule " + schedule.getName() );
223 writeObject( schedule, "schedule", true );
224 }
225 endTag( "schedules", true );
226 }
227 }
228
229 private static void backupAllInstallations()
230 throws Exception
231 {
232 LOGGER.info( "Backup installations" );
233 List<Installation> installs = client.getInstallations();
234 if ( installs != null && !installs.isEmpty() )
235 {
236 startTag( "installations", true );
237 for ( Installation install : installs )
238 {
239 LOGGER.debug( "Backup installation " + install.getName() );
240 writeObject( install, "installation", true );
241 }
242 endTag( "installations", true );
243 }
244 }
245
246 private static void backupAllBuildDefinitionTemplates()
247 throws Exception
248 {
249 LOGGER.info( "Backup Build Definitions Templates" );
250 List<BuildDefinitionTemplate> bdts = client.getBuildDefinitionTemplates();
251 if ( bdts != null && !bdts.isEmpty() )
252 {
253 startTag( "buildDefinitionTemplates", true );
254 for ( BuildDefinitionTemplate bdt : bdts )
255 {
256 LOGGER.debug( "Backup build definition template " + bdt.getName() );
257 startTag( "buildDefinitionTemplate", true );
258 writeSimpleFields( bdt );
259
260 List<BuildDefinition> bds = bdt.getBuildDefinitions();
261 if ( bds != null && !bds.isEmpty() )
262 {
263 for ( BuildDefinition bd : bds )
264 {
265 backupBuildDefinition( bd );
266 }
267 }
268 endTag( "buildDefinitionTemplate", true );
269 }
270 endTag( "buildDefinitionTemplates", true );
271 }
272 }
273
274 private static void backupAllProfiles()
275 throws Exception
276 {
277 LOGGER.info( "Backup profiles" );
278 List<Profile> profiles = client.getProfiles();
279 if ( profiles != null && !profiles.isEmpty() )
280 {
281 startTag( "profiles", true );
282 for ( Profile p : profiles )
283 {
284 LOGGER.debug( "Backup profile " + p.getName() );
285 writeProfile( p );
286 }
287 endTag( "profiles", true );
288 }
289 }
290
291 private static void backupAllProjectGroup()
292 throws Exception
293 {
294 LOGGER.info( "Backup project groups" );
295 List<ProjectGroup> pgs = client.getAllProjectGroupsWithAllDetails();
296 if ( pgs != null && !pgs.isEmpty() )
297 {
298 startTag( "projectGroups", true );
299 for ( ProjectGroup pg : pgs )
300 {
301 backupProjectGroup( pg );
302 }
303 endTag( "projectGroups", true );
304 }
305 }
306
307 private static void backupProjectGroup( ProjectGroup pg )
308 throws Exception
309 {
310 if ( pg == null )
311 {
312 return;
313 }
314
315 LOGGER.debug( "Backup project group " + pg.getName() );
316 startTag( "projectGroup", true );
317 writeSimpleFields( pg );
318
319 if ( pg.getProjects() != null && !pg.getProjects().isEmpty() )
320 {
321 startTag( "projects", true );
322 for ( ProjectSummary ps : (List<ProjectSummary>) pg.getProjects() )
323 {
324 backupProject( ps );
325 }
326 endTag( "projects", true );
327 }
328
329 if ( pg.getBuildDefinitions() != null && !pg.getBuildDefinitions().isEmpty() )
330 {
331 startTag( "buildDefinitions", true );
332 for ( BuildDefinition bd : (List<BuildDefinition>) pg.getBuildDefinitions() )
333 {
334 backupBuildDefinition( bd );
335 }
336 endTag( "buildDefinitions", true );
337 }
338
339 if ( pg.getNotifiers() != null && !pg.getNotifiers().isEmpty() )
340 {
341 startTag( "notifiers", true );
342 for ( ProjectNotifier notif : (List<ProjectNotifier>) pg.getNotifiers() )
343 {
344 backupNotifier( notif );
345 }
346 endTag( "notifiers", true );
347 }
348
349 backupContinuumReleaseResultsForProjectGroup( pg.getId() );
350 endTag( "projectGroup", true );
351 }
352
353 private static void backupProject( ProjectSummary ps )
354 throws Exception
355 {
356 if ( ps == null )
357 {
358 return;
359 }
360
361 LOGGER.debug( "Backup project " + ps.getName() );
362
363 Project p = client.getProjectWithAllDetails( ps.getId() );
364 startTag( "project", true );
365 writeSimpleFields( p );
366
367 if ( p.getProjectGroup() != null )
368 {
369 writeTagWithParameter( "projectGroup", "id", String.valueOf( p.getProjectGroup().getId() ) );
370 }
371
372 if ( p.getDevelopers() != null && !p.getDevelopers().isEmpty() )
373 {
374 startTag( "developers", true );
375 for ( ProjectDeveloper pd : (List<ProjectDeveloper>) p.getDevelopers() )
376 {
377 writeObject( pd, "developer", true );
378 }
379 endTag( "developers", true );
380 }
381
382 if ( p.getDependencies() != null && !p.getDependencies().isEmpty() )
383 {
384 startTag( "dependencies", true );
385 for ( ProjectDependency pd : (List<ProjectDependency>) p.getDependencies() )
386 {
387 writeObject( pd, "dependency", true );
388 }
389 endTag( "dependencies", true );
390 }
391
392 if ( p.getBuildDefinitions() != null && !p.getBuildDefinitions().isEmpty() )
393 {
394 startTag( "buildDefinitions", true );
395 for ( BuildDefinition bd : (List<BuildDefinition>) p.getBuildDefinitions() )
396 {
397 backupBuildDefinition( bd );
398 }
399 endTag( "buildDefinitions", true );
400 }
401
402 if ( p.getNotifiers() != null && !p.getNotifiers().isEmpty() )
403 {
404 startTag( "notifiers", true );
405 for ( ProjectNotifier notif : (List<ProjectNotifier>) p.getNotifiers() )
406 {
407 backupNotifier( notif );
408 }
409 endTag( "notifiers", true );
410 }
411
412 List<BuildResultSummary> brs = client.getBuildResultsForProject( p.getId() );
413 if ( brs != null && !brs.isEmpty() )
414 {
415 startTag( "buildResults", true );
416 for ( BuildResultSummary brSummary : brs )
417 {
418 BuildResult br = client.getBuildResult( p.getId(), brSummary.getId() );
419 backupBuildResult( br );
420 }
421 endTag( "buildResults", true );
422 }
423 endTag( "project", true );
424 }
425
426 private static void backupBuildResult( BuildResult br )
427 throws Exception
428 {
429 if ( br == null )
430 {
431 return;
432 }
433
434 startTag( "buildResult", true );
435 writeSimpleFields( br );
436
437 if ( br.getProject() != null )
438 {
439 writeTagWithParameter( "project", "id", String.valueOf( br.getProject().getId() ) );
440 }
441
442 if ( br.getBuildDefinition() != null )
443 {
444 writeTagWithParameter( "buildDefinition", "id", String.valueOf( br.getBuildDefinition().getId() ) );
445 }
446
447 if ( br.getModifiedDependencies() != null && !br.getModifiedDependencies().isEmpty() )
448 {
449 startTag( "dependencies", true );
450 for ( ProjectDependency pd : (List<ProjectDependency>) br.getModifiedDependencies() )
451 {
452 writeObject( pd, "dependency", true );
453 }
454 endTag( "dependencies", true );
455 }
456 endTag( "buildResult", true );
457 }
458
459 private static void writeSimpleFields( Object obj )
460 throws Exception
461 {
462 if ( obj == null )
463 {
464 return;
465 }
466
467 for ( Field f : getFieldsIncludingSuperclasses( obj.getClass() ) )
468 {
469 if ( "modelEncoding".equals( f.getName() ) )
470 {
471 continue;
472 }
473
474 if ( !f.isAccessible() )
475 {
476 f.setAccessible( true );
477 }
478
479 if ( f.getType().getName().equals( "int" ) || f.getType().getName().equals( "long" ) ||
480 f.getType().getName().equals( "boolean" ) || f.getType().getName().equals( "java.lang.String" ) )
481 {
482 Object value = f.get( obj );
483 if ( value != null )
484 {
485 startTag( f.getName(), false );
486 writer.print( value );
487 endTag( f.getName(), false );
488 }
489 }
490 else if ( ScmResult.class.getName().equals( f.getType().getName() ) )
491 {
492 writeScmResult( (ScmResult) f.get( obj ) );
493 }
494 else if ( ChangeFile.class.getName().equals( f.getType().getName() ) )
495 {
496 writeObject( f.get( obj ), "changeFile", true );
497 }
498 else if ( Profile.class.getName().equals( f.getType().getName() ) )
499 {
500 writeProfile( (Profile) f.get( obj ) );
501 }
502 else
503 {
504
505
506 }
507 }
508 }
509
510 private static void writeObject( Object obj, String tagName, boolean addNewLine )
511 throws Exception
512 {
513 if ( obj == null )
514 {
515 return;
516 }
517 startTag( tagName, addNewLine );
518 writeSimpleFields( obj );
519 endTag( tagName, addNewLine );
520 }
521
522 private static void backupBuildDefinition( BuildDefinition buildDef )
523 throws Exception
524 {
525 if ( buildDef == null )
526 {
527 return;
528 }
529 startTag( "buildDefinition", true );
530 writeSimpleFields( buildDef );
531 if ( buildDef.getSchedule() != null )
532 {
533 writeTagWithParameter( "schedule", "id", String.valueOf( buildDef.getSchedule().getId() ) );
534 }
535 endTag( "buildDefinition", true );
536 }
537
538 private static void backupNotifier( ProjectNotifier notifier )
539 throws Exception
540 {
541 startTag( "notifier", true );
542 writeSimpleFields( notifier );
543
544 Map conf = notifier.getConfiguration();
545 startTag( "configuration", true );
546 for ( String key : (Set<String>) conf.keySet() )
547 {
548 startTag( key, false );
549 writer.print( conf.get( key ) );
550 endTag( key, false );
551 }
552 endTag( "configuration", true );
553
554 endTag( "notifier", true );
555 }
556
557 private static void writeProfile( Profile profile )
558 throws Exception
559 {
560 if ( profile == null )
561 {
562 return;
563 }
564
565 startTag( "profile", true );
566 writeSimpleFields( profile );
567
568 if ( profile.getEnvironmentVariables() != null && !profile.getEnvironmentVariables().isEmpty() )
569 {
570 startTag( "environmentVariables", true );
571 for ( Installation env : (List<Installation>) profile.getEnvironmentVariables() )
572 {
573 writeTagWithParameter( "environmentVariable", "installationId", String.valueOf(
574 env.getInstallationId() ) );
575 }
576 endTag( "environmentVariables", true );
577 }
578
579 if ( profile.getJdk() != null )
580 {
581 writeTagWithParameter( "jdk", "installationId", String.valueOf( profile.getJdk().getInstallationId() ) );
582 }
583
584 if ( profile.getBuilder() != null )
585 {
586 writeTagWithParameter( "builder", "installationId", String.valueOf(
587 profile.getBuilder().getInstallationId() ) );
588 }
589
590 endTag( "profile", true );
591 }
592
593 private static void writeScmResult( ScmResult scmResult )
594 throws Exception
595 {
596 if ( scmResult == null )
597 {
598 return;
599 }
600
601 startTag( "scmResult", true );
602 writeSimpleFields( scmResult );
603
604 if ( scmResult.getChanges() != null && !scmResult.getChanges().isEmpty() )
605 {
606 startTag( "changeSets", true );
607 for ( ChangeSet cs : (List<ChangeSet>) scmResult.getChanges() )
608 {
609 writeObject( cs, "changeSet", true );
610 }
611 endTag( "changeSets", true );
612 }
613 endTag( "scmResult", true );
614 }
615
616 private static void startTag( String tagName, boolean addNewLineAfter )
617 {
618 writer.print( getIndent() );
619 writer.print( "<" );
620 writer.print( tagName );
621 writer.print( ">" );
622 if ( addNewLineAfter )
623 {
624 writer.println();
625 indent++;
626 }
627 }
628
629 private static void endTag( String tagName, boolean isOnNewLine )
630 {
631 if ( isOnNewLine )
632 {
633 indent--;
634 writer.print( getIndent() );
635 }
636 writer.print( "</" );
637 writer.print( tagName );
638 writer.println( ">" );
639 }
640
641 private static void writeTagWithParameter( String tagName, String parameterName, String parameterValue )
642 {
643 writer.print( getIndent() );
644 writer.print( "<" );
645 writer.print( tagName );
646 writer.print( " " );
647 writer.print( parameterName );
648 writer.print( "=\"" );
649 writer.print( parameterValue );
650 writer.print( "\"></" );
651 writer.print( tagName );
652 writer.println( ">" );
653 }
654
655 private static String getIndent()
656 {
657 String result = "";
658 for ( int i = 0; i < indent; i++ )
659 {
660 result += " ";
661 }
662 return result;
663 }
664
665 private static List<Field> getFieldsIncludingSuperclasses( Class clazz )
666 {
667 List<Field> fields = new ArrayList<Field>( Arrays.asList( clazz.getDeclaredFields() ) );
668
669 Class superclass = clazz.getSuperclass();
670
671 if ( superclass != null )
672 {
673 fields.addAll( getFieldsIncludingSuperclasses( superclass ) );
674 }
675
676 return fields;
677 }
678
679 private static void backupAllLocalRepositories()
680 throws Exception
681 {
682 LOGGER.info( "Backup local repositories" );
683 List<LocalRepository> repos = client.getAllLocalRepositories();
684 if ( repos != null && !repos.isEmpty() )
685 {
686 startTag( "localRepositories", true );
687 for ( LocalRepository repo : repos )
688 {
689 LOGGER.debug( "Backup local repository " + repo.getName() );
690 writeObject( repo, "localRepository", true );
691 }
692 endTag( "localRepositories", true );
693 }
694 }
695
696 private static void backupAllRepositoryPurgeConfigurations()
697 throws Exception
698 {
699 LOGGER.info( "Backup repository purge configurations" );
700 List<RepositoryPurgeConfiguration> purgeConfigs = client.getAllRepositoryPurgeConfigurations();
701 if ( purgeConfigs != null && !purgeConfigs.isEmpty() )
702 {
703 startTag( "repositoryPurgeConfigurations", true );
704 for ( RepositoryPurgeConfiguration purgeConfig : purgeConfigs )
705 {
706 LOGGER.debug( "Backup repository purge configuration" );
707 backupRepositoryPurgeConfiguration( purgeConfig );
708 }
709 endTag( "repositoryPurgeConfigurations", true );
710 }
711 }
712
713 private static void backupRepositoryPurgeConfiguration( RepositoryPurgeConfiguration repoPurge )
714 throws Exception
715 {
716 if ( repoPurge == null )
717 {
718 return;
719 }
720 startTag( "repositoryPurgeConfiguration", true );
721 writeSimpleFields( repoPurge );
722
723 if ( repoPurge.getRepository() != null )
724 {
725 writeTagWithParameter( "repository", "id", String.valueOf( repoPurge.getRepository().getId() ) );
726 }
727
728 if ( repoPurge.getSchedule() != null )
729 {
730 writeTagWithParameter( "schedule", "id", String.valueOf( repoPurge.getSchedule().getId() ) );
731 }
732 endTag( "repositoryPurgeConfiguration", true );
733 }
734
735 private static void backupAllDirectoryPurgeConfigurations()
736 throws Exception
737 {
738 LOGGER.info( "Backup repository purge configurations" );
739 List<DirectoryPurgeConfiguration> purgeConfigs = client.getAllDirectoryPurgeConfigurations();
740 if ( purgeConfigs != null && !purgeConfigs.isEmpty() )
741 {
742 startTag( "directoryPurgeConfigurations", true );
743 for ( DirectoryPurgeConfiguration purgeConfig : purgeConfigs )
744 {
745 LOGGER.debug( "Backup directory purge configuration" );
746 backupDirectoryPurgeConfiguration( purgeConfig );
747 }
748 endTag( "directoryPurgeConfigurations", true );
749 }
750 }
751
752 private static void backupDirectoryPurgeConfiguration( DirectoryPurgeConfiguration dirPurge )
753 throws Exception
754 {
755 if ( dirPurge == null )
756 {
757 return;
758 }
759 startTag( "directoryPurgeConfiguration", true );
760 writeSimpleFields( dirPurge );
761
762 if ( dirPurge.getSchedule() != null )
763 {
764 writeTagWithParameter( "schedule", "id", String.valueOf( dirPurge.getSchedule().getId() ) );
765 }
766 endTag( "directoryPurgeConfiguration", true );
767 }
768
769 private static void backupContinuumReleaseResultsForProjectGroup( int projectGroupId )
770 throws Exception
771 {
772 LOGGER.info( "Backup release results" );
773 List<ContinuumReleaseResult> results = client.getReleaseResultsForProjectGroup( projectGroupId );
774 if ( results != null && !results.isEmpty() )
775 {
776 startTag( "continuumReleaseResults", true );
777 for ( ContinuumReleaseResult result : results )
778 {
779 LOGGER.debug( "Backup release result" );
780 backupContinuumReleaseResult( result );
781 }
782 endTag( "continuumReleaseResults", true );
783 }
784 }
785
786 private static void backupContinuumReleaseResult( ContinuumReleaseResult result )
787 throws Exception
788 {
789 if ( result == null )
790 {
791 return;
792 }
793 startTag( "continuumReleaseResult", true );
794 writeSimpleFields( result );
795
796 if ( result.getProjectGroup() != null )
797 {
798 writeTagWithParameter( "projectGroup", "id", String.valueOf( result.getProjectGroup().getId() ) );
799 }
800 if ( result.getProject() != null )
801 {
802 writeTagWithParameter( "project", "id", String.valueOf( result.getProject().getId() ) );
803 }
804 endTag( "continuumReleaseResult", true );
805 }
806 }