1 package org.apache.maven.continuum.management;
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.log4j.BasicConfigurator;
25 import org.apache.log4j.Level;
26 import org.apache.log4j.Logger;
27 import org.apache.maven.artifact.Artifact;
28 import org.apache.maven.artifact.factory.ArtifactFactory;
29 import org.apache.maven.artifact.manager.WagonManager;
30 import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
31 import org.apache.maven.artifact.repository.ArtifactRepository;
32 import org.apache.maven.artifact.repository.ArtifactRepositoryFactory;
33 import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout;
34 import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout;
35 import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
36 import org.apache.maven.artifact.resolver.ArtifactResolutionException;
37 import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
38 import org.apache.maven.artifact.resolver.ArtifactResolver;
39 import org.apache.maven.artifact.resolver.DebugResolutionListener;
40 import org.apache.maven.artifact.resolver.ResolutionListener;
41 import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
42 import org.apache.maven.artifact.resolver.filter.ExcludesArtifactFilter;
43 import org.apache.maven.continuum.management.util.PlexusFileSystemXmlApplicationContext;
44 import org.apache.maven.settings.MavenSettingsBuilder;
45 import org.apache.maven.settings.Mirror;
46 import org.apache.maven.settings.Profile;
47 import org.apache.maven.settings.Proxy;
48 import org.apache.maven.settings.Repository;
49 import org.apache.maven.settings.Server;
50 import org.apache.maven.settings.Settings;
51 import org.apache.maven.wagon.repository.RepositoryPermissions;
52 import org.codehaus.plexus.PlexusContainer;
53 import org.codehaus.plexus.PlexusContainerException;
54 import org.codehaus.plexus.component.repository.exception.ComponentLifecycleException;
55 import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
56 import org.codehaus.plexus.spring.PlexusClassPathXmlApplicationContext;
57 import org.codehaus.plexus.spring.PlexusContainerAdapter;
58 import org.codehaus.plexus.util.xml.Xpp3Dom;
59 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
60
61 import java.io.File;
62 import java.io.IOException;
63 import java.net.URL;
64 import java.net.URLClassLoader;
65 import java.util.ArrayList;
66 import java.util.Collection;
67 import java.util.Collections;
68 import java.util.Iterator;
69 import java.util.List;
70 import java.util.Map;
71 import java.util.Properties;
72
73
74
75
76
77
78
79 public class DataManagementCli
80 {
81 private static final Logger LOGGER = Logger.getLogger( DataManagementCli.class );
82
83 private static final String JAR_FILE_PREFIX = "jar:file:";
84
85 private static final String FILE_PREFIX = "file:";
86
87 private static final String SPRING_CONTEXT_LOC = "!/**/META-INF/spring-context.xml";
88
89 private static final String PLEXUS_XML_LOC = "!/**/META-INF/plexus/components.xml";
90
91 public static void main( String[] args )
92 throws Exception
93 {
94 Commands command = new Commands();
95
96 DatabaseFormat databaseFormat;
97 OperationMode mode;
98 SupportedDatabase databaseType;
99
100 try
101 {
102 Args.parse( command, args );
103 if ( command.help )
104 {
105 Args.usage( command );
106 return;
107 }
108 if ( command.version )
109 {
110 System.out.print( "continuum-data-management version " + getVersion() );
111 return;
112 }
113 databaseFormat = DatabaseFormat.valueOf( command.databaseFormat );
114 mode = OperationMode.valueOf( command.mode );
115 databaseType = SupportedDatabase.valueOf( command.databaseType );
116 }
117 catch ( IllegalArgumentException e )
118 {
119 System.err.println( e.getMessage() );
120 Args.usage( command );
121 return;
122 }
123
124 if ( command.directory.exists() && !command.directory.isDirectory() )
125 {
126 System.err.println( command.directory + " already exists and is not a directory." );
127 Args.usage( command );
128 return;
129 }
130
131 if ( !command.overwrite && mode == OperationMode.EXPORT && command.directory.exists() )
132 {
133 System.err.println(
134 command.directory + " already exists and will not be overwritten unless the -overwrite flag is used." );
135 Args.usage( command );
136 return;
137 }
138
139 if ( command.buildsJdbcUrl == null && command.usersJdbcUrl == null )
140 {
141 System.err.println( "You must specify one of -buildsJdbcUrl and -usersJdbcUrl" );
142 Args.usage( command );
143 return;
144 }
145
146 if ( command.usersJdbcUrl != null && databaseFormat == DatabaseFormat.CONTINUUM_103 )
147 {
148 System.err.println( "The -usersJdbcUrl option can not be used with Continuum 1.0.3 databases" );
149 Args.usage( command );
150 return;
151 }
152
153 if ( SupportedDatabase.OTHER.equals( databaseType ) )
154 {
155 if ( command.driverClass == null || command.artifactId == null || command.groupId == null ||
156 command.artifactVersion == null || command.password == null || command.username == null )
157 {
158 System.err.println(
159 "If OTHER databaseType is selected, -driverClass, -artifactId, -groupId, -artifactVersion, -username and -password must be provided together" );
160 Args.usage( command );
161 return;
162 }
163 databaseType.defaultParams = new DatabaseParams( command.driverClass, command.groupId, command.artifactId,
164 command.artifactVersion, command.username,
165 command.password );
166 }
167
168 BasicConfigurator.configure();
169 if ( command.debug )
170 {
171 Logger.getRootLogger().setLevel( Level.DEBUG );
172 Logger.getLogger( "JPOX" ).setLevel( Level.DEBUG );
173 }
174 else
175 {
176 Logger.getRootLogger().setLevel( Level.INFO );
177 Logger.getLogger( "JPOX" ).setLevel( Level.WARN );
178 }
179
180 if ( command.settings != null && !command.settings.isFile() )
181 {
182 System.err.println( command.settings + " not exists or is not a file." );
183 Args.usage( command );
184 return;
185 }
186
187 if ( command.buildsJdbcUrl != null )
188 {
189 LOGGER.info( "Processing Continuum database..." );
190 processDatabase( databaseType, databaseFormat, mode, command.buildsJdbcUrl, command.directory,
191 command.settings, databaseFormat.getContinuumToolRoleHint(), "data-management-jdo",
192 "continuum", command.strict );
193 }
194
195 if ( command.usersJdbcUrl != null )
196 {
197 LOGGER.info( "Processing Redback database..." );
198 processDatabase( databaseType, databaseFormat, mode, command.usersJdbcUrl, command.directory,
199 command.settings, databaseFormat.getRedbackToolRoleHint(), "data-management-redback-jdo",
200 "redback", command.strict );
201 }
202 }
203
204 private static void processDatabase( SupportedDatabase databaseType, DatabaseFormat databaseFormat,
205 OperationMode mode, String jdbcUrl, File directory, File setting,
206 String toolRoleHint, String managementArtifactId, String configRoleHint,
207 boolean strict )
208 throws PlexusContainerException, ComponentLookupException, ComponentLifecycleException,
209 ArtifactNotFoundException, ArtifactResolutionException, IOException
210 {
211 String applicationVersion = getVersion();
212
213 DatabaseParams params = new DatabaseParams( databaseType.defaultParams );
214 params.setUrl( jdbcUrl );
215
216 PlexusClassPathXmlApplicationContext classPathApplicationContext = new PlexusClassPathXmlApplicationContext(
217 new String[]{"classpath*:/META-INF/spring-context.xml", "classpath*:/META-INF/plexus/components.xml",
218 "classpath*:/META-INF/plexus/plexus.xml"} );
219
220 PlexusContainerAdapter container = new PlexusContainerAdapter();
221 container.setApplicationContext( classPathApplicationContext );
222
223 initializeWagon( container, setting );
224
225 List<Artifact> artifacts = new ArrayList<Artifact>();
226 artifacts.addAll( downloadArtifact( container, params.getGroupId(), params.getArtifactId(), params.getVersion(),
227 setting ) );
228 artifacts.addAll( downloadArtifact( container, "org.apache.continuum", managementArtifactId, applicationVersion,
229 setting ) );
230 artifacts.addAll( downloadArtifact( container, "jpox", "jpox", databaseFormat.getJpoxVersion(), setting ) );
231
232 List<String> jars = new ArrayList<String>();
233
234
235 List<String> exclusions = new ArrayList<String>();
236 URLClassLoader cp = (URLClassLoader) DataManagementCli.class.getClassLoader();
237 List<URL> jarUrls = new ArrayList<URL>();
238
239 for ( URL url : cp.getURLs() )
240 {
241 String urlEF = url.toExternalForm();
242 if ( urlEF.endsWith( "target/classes/" ) )
243 {
244 int idEndIdx = urlEF.length() - 16;
245 String id = urlEF.substring( urlEF.lastIndexOf( '/', idEndIdx - 1 ) + 1, idEndIdx );
246
247 if ( !"data-management-api".equals( id ) && !"data-management-cli".equals( id ) &&
248 !"continuum-legacy".equals( id ) && !"continuum-model".equals( id ) &&
249 !"redback-legacy".equals( id ) )
250 {
251 LOGGER.debug( "[IDE Help] Adding '" + id + "' as an exclusion and using one from classpath" );
252 exclusions.add( "org.apache.continuum:" + id );
253 jars.add( url.getPath() );
254 jarUrls.add( url );
255 }
256 }
257
258
259 if ( urlEF.contains( "jpox-enhancer" ) )
260 {
261 LOGGER.debug( "[IDE Help] Adding 'jpox-enhancer' as an exclusion and using one from classpath" );
262 jars.add( url.getPath() );
263 jarUrls.add( url );
264 }
265 }
266
267 ArtifactFilter filter = new ExcludesArtifactFilter( exclusions );
268
269 for ( Artifact a : artifacts )
270 {
271 if ( "jpox".equals( a.getGroupId() ) && "jpox".equals( a.getArtifactId() ) )
272 {
273 if ( a.getVersion().equals( databaseFormat.getJpoxVersion() ) )
274 {
275 LOGGER.debug( "Adding artifact: " + a.getFile() );
276 jars.add( JAR_FILE_PREFIX + a.getFile().getAbsolutePath() + SPRING_CONTEXT_LOC );
277 jars.add( JAR_FILE_PREFIX + a.getFile().getAbsolutePath() + PLEXUS_XML_LOC );
278 jarUrls.add( new URL( FILE_PREFIX + a.getFile().getAbsolutePath() ) );
279 }
280 }
281 else if ( filter.include( a ) )
282 {
283 LOGGER.debug( "Adding artifact: " + a.getFile() );
284 jars.add( JAR_FILE_PREFIX + a.getFile().getAbsolutePath() + SPRING_CONTEXT_LOC );
285 jars.add( JAR_FILE_PREFIX + a.getFile().getAbsolutePath() + PLEXUS_XML_LOC );
286 jarUrls.add( new URL( FILE_PREFIX + a.getFile().getAbsolutePath() ) );
287 }
288 }
289
290 URLClassLoader newClassLoader = new URLClassLoader( (URL[]) jarUrls.toArray( new URL[jarUrls.size()] ), cp );
291 Thread.currentThread().setContextClassLoader( newClassLoader );
292 classPathApplicationContext.setClassLoader( newClassLoader );
293
294 PlexusFileSystemXmlApplicationContext fileSystemApplicationContext = new PlexusFileSystemXmlApplicationContext(
295 (String[]) jars.toArray( new String[jars.size()] ), classPathApplicationContext );
296 fileSystemApplicationContext.setClassLoader( newClassLoader );
297 container.setApplicationContext( fileSystemApplicationContext );
298
299 DatabaseFactoryConfigurator configurator = (DatabaseFactoryConfigurator) container.lookup(
300 DatabaseFactoryConfigurator.class.getName(), configRoleHint );
301 configurator.configure( params );
302
303 DataManagementTool manager = (DataManagementTool) container.lookup( DataManagementTool.class.getName(),
304 toolRoleHint );
305
306 if ( mode == OperationMode.EXPORT )
307 {
308 manager.backupDatabase( directory );
309 }
310 else if ( mode == OperationMode.IMPORT )
311 {
312 manager.eraseDatabase();
313 manager.restoreDatabase( directory, strict );
314 }
315 }
316
317 private static void initializeWagon( PlexusContainerAdapter container, File setting )
318 throws ComponentLookupException, ComponentLifecycleException, IOException
319 {
320 WagonManager wagonManager = (WagonManager) container.lookup( WagonManager.ROLE );
321
322 Settings settings = getSettings( container, setting );
323
324 try
325 {
326 Proxy proxy = settings.getActiveProxy();
327
328 if ( proxy != null )
329 {
330 if ( proxy.getHost() == null )
331 {
332 throw new IOException( "Proxy in settings.xml has no host" );
333 }
334
335 wagonManager.addProxy( proxy.getProtocol(), proxy.getHost(), proxy.getPort(), proxy.getUsername(),
336 proxy.getPassword(), proxy.getNonProxyHosts() );
337 }
338
339 for ( Iterator i = settings.getServers().iterator(); i.hasNext(); )
340 {
341 Server server = (Server) i.next();
342
343 wagonManager.addAuthenticationInfo( server.getId(), server.getUsername(), server.getPassword(),
344 server.getPrivateKey(), server.getPassphrase() );
345
346 wagonManager.addPermissionInfo( server.getId(), server.getFilePermissions(),
347 server.getDirectoryPermissions() );
348
349 if ( server.getConfiguration() != null )
350 {
351 wagonManager.addConfiguration( server.getId(), (Xpp3Dom) server.getConfiguration() );
352 }
353 }
354
355 RepositoryPermissions defaultPermissions = new RepositoryPermissions();
356
357 defaultPermissions.setDirectoryMode( "775" );
358
359 defaultPermissions.setFileMode( "664" );
360
361 wagonManager.setDefaultRepositoryPermissions( defaultPermissions );
362
363 for ( Iterator i = settings.getMirrors().iterator(); i.hasNext(); )
364 {
365 Mirror mirror = (Mirror) i.next();
366
367 wagonManager.addMirror( mirror.getId(), mirror.getMirrorOf(), mirror.getUrl() );
368 }
369 }
370 finally
371 {
372 container.release( wagonManager );
373 }
374
375 }
376
377 private static Collection<Artifact> downloadArtifact( PlexusContainer container, String groupId, String artifactId,
378 String version, File setting )
379 throws ComponentLookupException, ArtifactNotFoundException, ArtifactResolutionException, IOException
380 {
381 ArtifactRepositoryFactory factory = (ArtifactRepositoryFactory) container.lookup(
382 ArtifactRepositoryFactory.ROLE );
383
384 DefaultRepositoryLayout layout = (DefaultRepositoryLayout) container.lookup( ArtifactRepositoryLayout.ROLE,
385 "default" );
386
387 ArtifactRepository localRepository = factory.createArtifactRepository( "local", getLocalRepositoryURL(
388 container, setting ), layout, null, null );
389
390 List<ArtifactRepository> remoteRepositories = new ArrayList<ArtifactRepository>();
391 remoteRepositories.add( factory.createArtifactRepository( "central", "http://repo1.maven.org/maven2", layout,
392 null, null ) );
393
394
395 Settings settings = getSettings( container, setting );
396 List<String> profileIds = settings.getActiveProfiles();
397 Map<String, Profile> profilesAsMap = settings.getProfilesAsMap();
398 if ( profileIds != null && !profileIds.isEmpty() )
399 {
400 for ( String profileId : profileIds )
401 {
402 Profile profile = profilesAsMap.get( profileId );
403 if ( profile != null )
404 {
405 List<Repository> repos = profile.getRepositories();
406 if ( repos != null && !repos.isEmpty() )
407 {
408 for ( Repository repo : repos )
409 {
410 remoteRepositories.add( factory.createArtifactRepository( repo.getId(), repo.getUrl(),
411 layout, null, null ) );
412 }
413 }
414 }
415 }
416 }
417
418 ArtifactFactory artifactFactory = (ArtifactFactory) container.lookup( ArtifactFactory.ROLE );
419 Artifact artifact = artifactFactory.createArtifact( groupId, artifactId, version, Artifact.SCOPE_RUNTIME,
420 "jar" );
421 Artifact dummyArtifact = artifactFactory.createProjectArtifact( "dummy", "dummy", "1.0" );
422
423 if ( artifact.isSnapshot() )
424 {
425 remoteRepositories.add( factory.createArtifactRepository( "apache.snapshots",
426 "http://people.apache.org/repo/m2-snapshot-repository",
427 layout, null, null ) );
428 }
429
430 ArtifactResolver resolver = (ArtifactResolver) container.lookup( ArtifactResolver.ROLE );
431
432 List<String> exclusions = new ArrayList<String>();
433 exclusions.add( "org.apache.continuum:data-management-api" );
434 exclusions.add( "org.codehaus.plexus:plexus-component-api" );
435 exclusions.add( "org.codehaus.plexus:plexus-container-default" );
436 exclusions.add( "org.slf4j:slf4j-api" );
437 exclusions.add( "log4j:log4j" );
438
439 ArtifactFilter filter = new ExcludesArtifactFilter( exclusions );
440
441 List<? extends ResolutionListener> listeners;
442 if ( LOGGER.isDebugEnabled() )
443 {
444 listeners = Collections.singletonList( new DebugResolutionListener( container.getLogger() ) );
445 }
446 else
447 {
448 listeners = Collections.emptyList();
449 }
450
451 ArtifactMetadataSource source = (ArtifactMetadataSource) container.lookup( ArtifactMetadataSource.ROLE,
452 "maven" );
453 ArtifactResolutionResult result = resolver.resolveTransitively( Collections.singleton( artifact ),
454 dummyArtifact, Collections.emptyMap(),
455 localRepository, remoteRepositories, source,
456 filter, listeners );
457
458 return result.getArtifacts();
459 }
460
461 private static String getLocalRepositoryURL( PlexusContainer container, File setting )
462 throws ComponentLookupException, IOException
463 {
464 String repositoryPath;
465 File settingsFile = new File( System.getProperty( "user.home" ), ".m2/settings.xml" );
466 if ( setting != null )
467 {
468 Settings settings = getSettings( container, setting );
469 repositoryPath = new File( settings.getLocalRepository() ).toURL().toString();
470 }
471 else if ( !settingsFile.exists() )
472 {
473 repositoryPath = new File( System.getProperty( "user.home" ), ".m2/repository" ).toURL().toString();
474 }
475 else
476 {
477 Settings settings = getSettings( container, null );
478 repositoryPath = new File( settings.getLocalRepository() ).toURL().toString();
479 }
480 return repositoryPath;
481 }
482
483 private static Settings getSettings( PlexusContainer container, File setting )
484 throws ComponentLookupException, IOException
485 {
486 MavenSettingsBuilder mavenSettingsBuilder = (MavenSettingsBuilder) container.lookup(
487 MavenSettingsBuilder.class.getName() );
488 try
489 {
490 if ( setting != null )
491 {
492 return mavenSettingsBuilder.buildSettings( setting, false );
493 }
494 else
495 {
496 return mavenSettingsBuilder.buildSettings( false );
497 }
498 }
499 catch ( XmlPullParserException e )
500 {
501 e.printStackTrace();
502 throw new IOException( "Can't read settings.xml. " + e.getMessage() );
503 }
504 }
505
506 private static String getVersion()
507 throws IOException
508 {
509 Properties properties = new Properties();
510 properties.load( DataManagementCli.class.getResourceAsStream(
511 "/META-INF/maven/org.apache.continuum/data-management-api/pom.properties" ) );
512 return properties.getProperty( "version" );
513 }
514
515 private static class Commands
516 {
517
518 @Argument( description = "Display help information", value = "help", alias = "h" )
519 private boolean help;
520
521 @Argument( description = "Display version information", value = "version", alias = "v" )
522 private boolean version;
523
524 @Argument(
525 description = "The JDBC URL for the Continuum database that contains the data to convert, or to import the data into",
526 value = "buildsJdbcUrl" )
527 private String buildsJdbcUrl;
528
529 @Argument(
530 description = "The JDBC URL for the Redback database that contains the data to convert, or to import the data into",
531 value = "usersJdbcUrl" )
532 private String usersJdbcUrl;
533
534
535 @Argument(
536 description = "Format of the database. Valid values are CONTINUUM_103, CONTINUUM_109, CONTINUUM_11. Default is CONTINUUM_11." )
537 private String databaseFormat = DatabaseFormat.CONTINUUM_11.toString();
538
539
540
541
542
543
544
545 @Argument(
546 description = "The directory to export the data to, or import the data from. Default is 'backups' in the current working directory.",
547 value = "directory" )
548 private File directory = new File( "backups" );
549
550 @Argument(
551 description = "Mode of operation. Valid values are IMPORT and EXPORT. Default is EXPORT.",
552 value = "mode" )
553 private String mode = OperationMode.EXPORT.toString();
554
555 @Argument(
556 description = "Whether to overwrite the designated directory if it already exists in export mode. Default is false.",
557 value = "overwrite" )
558 private boolean overwrite;
559
560 @Argument(
561 description = "The type of database to use. Currently supported values are DERBY_10_1. The default value is DERBY_10_1.",
562 value = "databaseType" )
563 private String databaseType = SupportedDatabase.DERBY_10_1.toString();
564
565 @Argument( description = "JDBC driver class", value = "driverClass", required = false )
566 private String driverClass;
567
568 @Argument( description = "JDBC driver groupId", value = "groupId", required = false )
569 private String groupId;
570
571 @Argument( description = "JDBC driver artifactId", value = "artifactId", required = false )
572 private String artifactId;
573
574 @Argument( description = "Artifact version of the JDBC driver class",
575 value = "artifactVersion",
576 required = false )
577 private String artifactVersion;
578
579 @Argument( description = "Username", value = "username", required = false )
580 private String username;
581
582 @Argument( description = "Password", value = "password", required = false )
583 private String password;
584
585 @Argument(
586 description = "Turn on debugging information. Default is off.",
587 value = "debug" )
588 private boolean debug;
589
590 @Argument( description = "Alternate path for the user settings file", value = "settings", required = false,
591 alias = "s" )
592 private File settings;
593
594 @Argument( description = "Run on strict mode. Default is false.", value = "strict" )
595 private boolean strict;
596 }
597
598 private enum OperationMode
599 {
600 IMPORT, EXPORT
601 }
602
603 private enum SupportedDatabase
604 {
605 DERBY_10_1( new DatabaseParams( "org.apache.derby.jdbc.EmbeddedDriver", "org.apache.derby", "derby", "10.1.3.1",
606 "sa", "" ) ),
607
608 OTHER( new DatabaseParams( null, null, null, null, null, null ) );
609
610 private DatabaseParams defaultParams;
611
612 SupportedDatabase( DatabaseParams defaultParams )
613 {
614 this.defaultParams = defaultParams;
615 }
616 }
617 }