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 public class DataManagementCli
79 {
80 private static final Logger LOGGER = Logger.getLogger( DataManagementCli.class );
81
82 private static final String JAR_FILE_PREFIX = "jar:file:";
83
84 private static final String FILE_PREFIX = "file:";
85
86 private static final String SPRING_CONTEXT_LOC = "!/**/META-INF/spring-context.xml";
87
88 private static final String PLEXUS_XML_LOC = "!/**/META-INF/plexus/components.xml";
89
90 public static void main( String[] args )
91 throws Exception
92 {
93 Commands command = new Commands();
94
95 DatabaseFormat databaseFormat;
96 OperationMode mode;
97 SupportedDatabase databaseType;
98
99 try
100 {
101 Args.parse( command, args );
102 if ( command.help )
103 {
104 Args.usage( command );
105 return;
106 }
107 if ( command.version )
108 {
109 System.out.print( "continuum-data-management version " + getVersion() );
110 return;
111 }
112 databaseFormat = DatabaseFormat.valueOf( command.databaseFormat );
113 mode = OperationMode.valueOf( command.mode );
114 databaseType = SupportedDatabase.valueOf( command.databaseType );
115 }
116 catch ( IllegalArgumentException e )
117 {
118 System.err.println( e.getMessage() );
119 Args.usage( command );
120 return;
121 }
122
123 if ( command.directory.exists() && !command.directory.isDirectory() )
124 {
125 System.err.println( command.directory + " already exists and is not a directory." );
126 Args.usage( command );
127 return;
128 }
129
130 if ( !command.overwrite && mode == OperationMode.EXPORT && command.directory.exists() )
131 {
132 System.err.println(
133 command.directory + " already exists and will not be overwritten unless the -overwrite flag is used." );
134 Args.usage( command );
135 return;
136 }
137
138 if ( command.buildsJdbcUrl == null && command.usersJdbcUrl == null )
139 {
140 System.err.println( "You must specify one of -buildsJdbcUrl and -usersJdbcUrl" );
141 Args.usage( command );
142 return;
143 }
144
145 if ( command.usersJdbcUrl != null && databaseFormat == DatabaseFormat.CONTINUUM_103 )
146 {
147 System.err.println( "The -usersJdbcUrl option can not be used with Continuum 1.0.3 databases" );
148 Args.usage( command );
149 return;
150 }
151
152 if ( SupportedDatabase.OTHER.equals( databaseType ) )
153 {
154 if ( command.driverClass == null || command.artifactId == null || command.groupId == null ||
155 command.artifactVersion == null || command.password == null || command.username == null )
156 {
157 System.err.println(
158 "If OTHER databaseType is selected, -driverClass, -artifactId, -groupId, -artifactVersion, -username and -password must be provided together" );
159 Args.usage( command );
160 return;
161 }
162 databaseType.defaultParams = new DatabaseParams( command.driverClass, command.groupId, command.artifactId,
163 command.artifactVersion, command.username,
164 command.password );
165 }
166
167 BasicConfigurator.configure();
168 if ( command.debug )
169 {
170 Logger.getRootLogger().setLevel( Level.DEBUG );
171 Logger.getLogger( "JPOX" ).setLevel( Level.DEBUG );
172 }
173 else
174 {
175 Logger.getRootLogger().setLevel( Level.INFO );
176 Logger.getLogger( "JPOX" ).setLevel( Level.WARN );
177 }
178
179 if ( command.settings != null && !command.settings.isFile() )
180 {
181 System.err.println( command.settings + " not exists or is not a file." );
182 Args.usage( command );
183 return;
184 }
185
186 if ( command.buildsJdbcUrl != null )
187 {
188 LOGGER.info( "Processing Continuum database..." );
189 processDatabase( databaseType, databaseFormat, mode, command.buildsJdbcUrl, command.directory,
190 command.settings, databaseFormat.getContinuumToolRoleHint(), "data-management-jdo",
191 "continuum", command.strict );
192 }
193
194 if ( command.usersJdbcUrl != null )
195 {
196 LOGGER.info( "Processing Redback database..." );
197 processDatabase( databaseType, databaseFormat, mode, command.usersJdbcUrl, command.directory,
198 command.settings, databaseFormat.getRedbackToolRoleHint(), "data-management-redback-jdo",
199 "redback", command.strict );
200 }
201 }
202
203 private static void processDatabase( SupportedDatabase databaseType, DatabaseFormat databaseFormat,
204 OperationMode mode, String jdbcUrl, File directory, File setting,
205 String toolRoleHint, String managementArtifactId, String configRoleHint,
206 boolean strict )
207 throws PlexusContainerException, ComponentLookupException, ComponentLifecycleException,
208 ArtifactNotFoundException, ArtifactResolutionException, IOException
209 {
210 String applicationVersion = getVersion();
211
212 DatabaseParams params = new DatabaseParams( databaseType.defaultParams );
213 params.setUrl( jdbcUrl );
214
215 PlexusClassPathXmlApplicationContext classPathApplicationContext = new PlexusClassPathXmlApplicationContext(
216 new String[]{"classpath*:/META-INF/spring-context.xml", "classpath*:/META-INF/plexus/components.xml",
217 "classpath*:/META-INF/plexus/plexus.xml"} );
218
219 PlexusContainerAdapter container = new PlexusContainerAdapter();
220 container.setApplicationContext( classPathApplicationContext );
221
222 initializeWagon( container, setting );
223
224 List<Artifact> artifacts = new ArrayList<Artifact>();
225 artifacts.addAll(
226 downloadArtifact( container, params.getGroupId(), params.getArtifactId(),
227 params.getVersion(), setting ) );
228 artifacts.addAll(
229 downloadArtifact( container, "org.apache.continuum", managementArtifactId,
230 applicationVersion, setting ) );
231 artifacts.addAll( downloadArtifact( container, "jpox", "jpox", databaseFormat.getJpoxVersion(), setting ) );
232
233 List<String> jars = new ArrayList<String>();
234
235
236 List<String> exclusions = new ArrayList<String>();
237 URLClassLoader cp = (URLClassLoader) DataManagementCli.class.getClassLoader();
238 List<URL> jarUrls = new ArrayList<URL>();
239
240 for ( URL url : cp.getURLs() )
241 {
242 String urlEF = url.toExternalForm();
243 if ( urlEF.endsWith( "target/classes/" ) )
244 {
245 int idEndIdx = urlEF.length() - 16;
246 String id = urlEF.substring( urlEF.lastIndexOf( '/', idEndIdx - 1 ) + 1, idEndIdx );
247
248 if ( !"data-management-api".equals( id ) && !"data-management-cli".equals( id ) &&
249 !"continuum-legacy".equals( id ) && !"continuum-model".equals( id ) &&
250 !"redback-legacy".equals( id ) )
251 {
252 LOGGER.debug( "[IDE Help] Adding '" + id + "' as an exclusion and using one from classpath" );
253 exclusions.add( "org.apache.continuum:" + id );
254 jars.add( url.getPath() );
255 jarUrls.add( url );
256 }
257 }
258
259
260 if ( urlEF.contains( "jpox-enhancer" ) )
261 {
262 LOGGER.debug( "[IDE Help] Adding 'jpox-enhancer' as an exclusion and using one from classpath" );
263 jars.add( url.getPath() );
264 jarUrls.add( url );
265 }
266 }
267
268 ArtifactFilter filter = new ExcludesArtifactFilter( exclusions );
269
270 for ( Artifact a : artifacts )
271 {
272 if ( "jpox".equals( a.getGroupId() ) && "jpox".equals( a.getArtifactId() ) )
273 {
274 if ( a.getVersion().equals( databaseFormat.getJpoxVersion() ) )
275 {
276 LOGGER.debug( "Adding artifact: " + a.getFile() );
277 jars.add( JAR_FILE_PREFIX + a.getFile().getAbsolutePath() + SPRING_CONTEXT_LOC );
278 jars.add( JAR_FILE_PREFIX + a.getFile().getAbsolutePath() + PLEXUS_XML_LOC );
279 jarUrls.add( new URL( FILE_PREFIX + a.getFile().getAbsolutePath() ) );
280 }
281 }
282 else if ( filter.include( a ) )
283 {
284 LOGGER.debug( "Adding artifact: " + a.getFile() );
285 jars.add( JAR_FILE_PREFIX + a.getFile().getAbsolutePath() + SPRING_CONTEXT_LOC );
286 jars.add( JAR_FILE_PREFIX + a.getFile().getAbsolutePath() + PLEXUS_XML_LOC );
287 jarUrls.add( new URL( FILE_PREFIX + a.getFile().getAbsolutePath() ) );
288 }
289 }
290
291 URLClassLoader newClassLoader = new URLClassLoader( (URL[]) jarUrls.toArray( new URL[jarUrls.size()] ), cp );
292 Thread.currentThread().setContextClassLoader( newClassLoader );
293 classPathApplicationContext.setClassLoader( newClassLoader );
294
295 PlexusFileSystemXmlApplicationContext fileSystemApplicationContext = new PlexusFileSystemXmlApplicationContext(
296 (String[]) jars.toArray( new String[jars.size()] ), classPathApplicationContext );
297 fileSystemApplicationContext.setClassLoader( newClassLoader );
298 container.setApplicationContext( fileSystemApplicationContext );
299
300 DatabaseFactoryConfigurator configurator = (DatabaseFactoryConfigurator) container.lookup(
301 DatabaseFactoryConfigurator.class.getName(), configRoleHint );
302 configurator.configure( params );
303
304 DataManagementTool manager =
305 (DataManagementTool) container.lookup( DataManagementTool.class.getName(), toolRoleHint );
306
307 if ( mode == OperationMode.EXPORT )
308 {
309 manager.backupDatabase( directory );
310 }
311 else if ( mode == OperationMode.IMPORT )
312 {
313 manager.eraseDatabase();
314 manager.restoreDatabase( directory, strict );
315 }
316 }
317
318 private static void initializeWagon( PlexusContainerAdapter container, File setting )
319 throws ComponentLookupException, ComponentLifecycleException, IOException
320 {
321 WagonManager wagonManager = (WagonManager) container.lookup( WagonManager.ROLE );
322
323 Settings settings = getSettings( container, setting );
324
325 try
326 {
327 Proxy proxy = settings.getActiveProxy();
328
329 if ( proxy != null )
330 {
331 if ( proxy.getHost() == null )
332 {
333 throw new IOException( "Proxy in settings.xml has no host" );
334 }
335
336 wagonManager.addProxy( proxy.getProtocol(), proxy.getHost(), proxy.getPort(), proxy.getUsername(),
337 proxy.getPassword(), proxy.getNonProxyHosts() );
338 }
339
340 for ( Iterator i = settings.getServers().iterator(); i.hasNext(); )
341 {
342 Server server = (Server) i.next();
343
344 wagonManager.addAuthenticationInfo( server.getId(), server.getUsername(), server.getPassword(),
345 server.getPrivateKey(), server.getPassphrase() );
346
347 wagonManager.addPermissionInfo( server.getId(), server.getFilePermissions(),
348 server.getDirectoryPermissions() );
349
350 if ( server.getConfiguration() != null )
351 {
352 wagonManager.addConfiguration( server.getId(), (Xpp3Dom) server.getConfiguration() );
353 }
354 }
355
356 RepositoryPermissions defaultPermissions = new RepositoryPermissions();
357
358 defaultPermissions.setDirectoryMode( "775" );
359
360 defaultPermissions.setFileMode( "664" );
361
362 wagonManager.setDefaultRepositoryPermissions( defaultPermissions );
363
364 for ( Iterator i = settings.getMirrors().iterator(); i.hasNext(); )
365 {
366 Mirror mirror = (Mirror) i.next();
367
368 wagonManager.addMirror( mirror.getId(), mirror.getMirrorOf(), mirror.getUrl() );
369 }
370 }
371 finally
372 {
373 container.release( wagonManager );
374 }
375
376 }
377
378 private static Collection<Artifact> downloadArtifact( PlexusContainer container, String groupId, String artifactId,
379 String version, File setting )
380 throws ComponentLookupException, ArtifactNotFoundException, ArtifactResolutionException, IOException
381 {
382 ArtifactRepositoryFactory factory =
383 (ArtifactRepositoryFactory) container.lookup( ArtifactRepositoryFactory.ROLE );
384
385 DefaultRepositoryLayout layout =
386 (DefaultRepositoryLayout) container.lookup( ArtifactRepositoryLayout.ROLE, "default" );
387
388 ArtifactRepository localRepository =
389 factory.createArtifactRepository( "local", getLocalRepositoryURL( container, setting ), layout, null, null );
390
391 List<ArtifactRepository> remoteRepositories = new ArrayList<ArtifactRepository>();
392 remoteRepositories.add(
393 factory.createArtifactRepository( "central", "http://repo1.maven.org/maven2", layout, null, null ) );
394
395
396 Settings settings = getSettings( container, setting );
397 List<String> profileIds = settings.getActiveProfiles();
398 Map<String, Profile> profilesAsMap = settings.getProfilesAsMap();
399 if ( profileIds != null && !profileIds.isEmpty() )
400 {
401 for ( String profileId : profileIds )
402 {
403 Profile profile = profilesAsMap.get( profileId );
404 if ( profile != null )
405 {
406 List<Repository> repos = profile.getRepositories();
407 if ( repos != null && !repos.isEmpty() )
408 {
409 for ( Repository repo : repos )
410 {
411 remoteRepositories.add( factory.createArtifactRepository( repo.getId(), repo.getUrl(),
412 layout, null, null ) );
413 }
414 }
415 }
416 }
417 }
418
419 ArtifactFactory artifactFactory = (ArtifactFactory) container.lookup( ArtifactFactory.ROLE );
420 Artifact artifact =
421 artifactFactory.createArtifact( groupId, artifactId, version, Artifact.SCOPE_RUNTIME, "jar" );
422 Artifact dummyArtifact = artifactFactory.createProjectArtifact( "dummy", "dummy", "1.0" );
423
424 if ( artifact.isSnapshot() )
425 {
426 remoteRepositories.add( factory.createArtifactRepository( "apache.snapshots",
427 "http://people.apache.org/repo/m2-snapshot-repository",
428 layout, null, null ) );
429 }
430
431 ArtifactResolver resolver = (ArtifactResolver) container.lookup( ArtifactResolver.ROLE );
432
433 List<String> exclusions = new ArrayList<String>();
434 exclusions.add( "org.apache.continuum:data-management-api" );
435 exclusions.add( "org.codehaus.plexus:plexus-component-api" );
436 exclusions.add( "org.codehaus.plexus:plexus-container-default" );
437 exclusions.add( "org.slf4j:slf4j-api" );
438 exclusions.add( "log4j:log4j" );
439
440 ArtifactFilter filter = new ExcludesArtifactFilter( exclusions );
441
442 List<? extends ResolutionListener> listeners;
443 if ( LOGGER.isDebugEnabled() )
444 {
445 listeners = Collections.singletonList( new DebugResolutionListener( container.getLogger() ) );
446 }
447 else
448 {
449 listeners = Collections.emptyList();
450 }
451
452 ArtifactMetadataSource source =
453 (ArtifactMetadataSource) container.lookup( ArtifactMetadataSource.ROLE, "maven" );
454 ArtifactResolutionResult result =
455 resolver.resolveTransitively( Collections.singleton( artifact ), dummyArtifact, Collections.emptyMap(),
456 localRepository, remoteRepositories, source, 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 =
487 (MavenSettingsBuilder) container.lookup( 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, alias = "s" )
591 private File settings;
592
593 @Argument(description = "Run on strict mode. Default is false.", value="strict")
594 private boolean strict;
595 }
596
597 private enum OperationMode
598 {
599 IMPORT, EXPORT
600 }
601
602 private enum SupportedDatabase
603 {
604 DERBY_10_1( new DatabaseParams( "org.apache.derby.jdbc.EmbeddedDriver", "org.apache.derby", "derby", "10.1.3.1",
605 "sa", "" ) ),
606
607 OTHER( new DatabaseParams( null, null, null, null, null, null ) );
608
609 private DatabaseParams defaultParams;
610
611 SupportedDatabase( DatabaseParams defaultParams )
612 {
613 this.defaultParams = defaultParams;
614 }
615 }
616 }