| package org.apache.maven.continuum.management; |
| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| import com.sampullara.cli.Args; |
| import com.sampullara.cli.Argument; |
| import org.apache.log4j.BasicConfigurator; |
| import org.apache.log4j.Level; |
| import org.apache.log4j.Logger; |
| import org.apache.maven.artifact.Artifact; |
| import org.apache.maven.artifact.factory.ArtifactFactory; |
| import org.apache.maven.artifact.manager.WagonManager; |
| import org.apache.maven.artifact.metadata.ArtifactMetadataSource; |
| import org.apache.maven.artifact.repository.ArtifactRepository; |
| import org.apache.maven.artifact.repository.ArtifactRepositoryFactory; |
| import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout; |
| import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout; |
| import org.apache.maven.artifact.resolver.ArtifactNotFoundException; |
| import org.apache.maven.artifact.resolver.ArtifactResolutionException; |
| import org.apache.maven.artifact.resolver.ArtifactResolutionResult; |
| import org.apache.maven.artifact.resolver.ArtifactResolver; |
| import org.apache.maven.artifact.resolver.filter.ArtifactFilter; |
| import org.apache.maven.artifact.resolver.filter.ExcludesArtifactFilter; |
| import org.apache.maven.settings.MavenSettingsBuilder; |
| import org.apache.maven.settings.Mirror; |
| import org.apache.maven.settings.Profile; |
| import org.apache.maven.settings.Proxy; |
| import org.apache.maven.settings.Repository; |
| import org.apache.maven.settings.Server; |
| import org.apache.maven.settings.Settings; |
| import org.apache.maven.wagon.repository.RepositoryPermissions; |
| import org.codehaus.plexus.DefaultPlexusContainer; |
| import org.codehaus.plexus.PlexusContainer; |
| import org.codehaus.plexus.PlexusContainerException; |
| import org.codehaus.plexus.classworlds.realm.ClassRealm; |
| import org.codehaus.plexus.component.repository.exception.ComponentLifecycleException; |
| import org.codehaus.plexus.component.repository.exception.ComponentLookupException; |
| import org.codehaus.plexus.util.xml.Xpp3Dom; |
| import org.codehaus.plexus.util.xml.pull.XmlPullParserException; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.net.URL; |
| import java.net.URLClassLoader; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Properties; |
| |
| /** |
| * An application for performing database upgrades from old Continuum and Redback versions. A suitable tool until it |
| * is natively incorporated into Continuum itself. |
| * @version $Id$ |
| */ |
| public class DataManagementCli |
| { |
| private static final Logger LOGGER = Logger.getLogger( DataManagementCli.class ); |
| |
| public static void main( String[] args ) |
| throws Exception |
| { |
| Commands command = new Commands(); |
| |
| DatabaseFormat databaseFormat; |
| OperationMode mode; |
| SupportedDatabase databaseType; |
| |
| try |
| { |
| Args.parse( command, args ); |
| if ( command.help ) |
| { |
| Args.usage( command ); |
| return; |
| } |
| if ( command.version ) |
| { |
| System.out.print( "continuum-data-management version " + getVersion() ); |
| return; |
| } |
| databaseFormat = DatabaseFormat.valueOf( command.databaseFormat ); |
| mode = OperationMode.valueOf( command.mode ); |
| databaseType = SupportedDatabase.valueOf( command.databaseType ); |
| } |
| catch ( IllegalArgumentException e ) |
| { |
| System.err.println( e.getMessage() ); |
| Args.usage( command ); |
| return; |
| } |
| |
| if ( command.directory.exists() && !command.directory.isDirectory() ) |
| { |
| System.err.println( command.directory + " already exists and is not a directory." ); |
| Args.usage( command ); |
| return; |
| } |
| |
| if ( !command.overwrite && mode == OperationMode.EXPORT && command.directory.exists() ) |
| { |
| System.err.println( |
| command.directory + " already exists and will not be overwritten unless the -overwrite flag is used." ); |
| Args.usage( command ); |
| return; |
| } |
| |
| if ( command.buildsJdbcUrl == null && command.usersJdbcUrl == null ) |
| { |
| System.err.println( "You must specify one of -buildsJdbcUrl and -usersJdbcUrl" ); |
| Args.usage( command ); |
| return; |
| } |
| |
| if ( command.usersJdbcUrl != null && databaseFormat == DatabaseFormat.CONTINUUM_103 ) |
| { |
| System.err.println( "The -usersJdbcUrl option can not be used with Continuum 1.0.3 databases" ); |
| Args.usage( command ); |
| return; |
| } |
| |
| if ( SupportedDatabase.OTHER.equals( databaseType ) ) |
| { |
| if ( command.driverClass == null || command.artifactId == null || command.groupId == null || |
| command.artifactVersion == null || command.password == null || command.username == null ) |
| { |
| System.err.println( |
| "If OTHER databaseType is selected, -driverClass, -artifactId, -groupId, -artifactVersion, -username and -password must be provided together" ); |
| Args.usage( command ); |
| return; |
| } |
| databaseType.defaultParams = new DatabaseParams( command.driverClass, command.groupId, command.artifactId, |
| command.artifactVersion, command.username, |
| command.password ); |
| } |
| |
| BasicConfigurator.configure(); |
| if ( command.debug ) |
| { |
| Logger.getRootLogger().setLevel( Level.DEBUG ); |
| Logger.getLogger( "JPOX" ).setLevel( Level.DEBUG ); |
| } |
| else |
| { |
| Logger.getRootLogger().setLevel( Level.INFO ); |
| } |
| |
| if ( command.buildsJdbcUrl != null ) |
| { |
| LOGGER.info( "Processing Continuum database..." ); |
| processDatabase( databaseType, databaseFormat, mode, command.buildsJdbcUrl, command.directory, |
| databaseFormat.getContinuumToolRoleHint(), "data-management-jdo", "continuum" ); |
| } |
| |
| if ( command.usersJdbcUrl != null ) |
| { |
| LOGGER.info( "Processing Redback database..." ); |
| processDatabase( databaseType, databaseFormat, mode, command.usersJdbcUrl, command.directory, |
| databaseFormat.getRedbackToolRoleHint(), "data-management-redback-jdo", "redback" ); |
| } |
| } |
| |
| private static void processDatabase( SupportedDatabase databaseType, DatabaseFormat databaseFormat, |
| OperationMode mode, String jdbcUrl, File directory, String toolRoleHint, |
| String managementArtifactId, String configRoleHint ) |
| throws PlexusContainerException, ComponentLookupException, ComponentLifecycleException, |
| ArtifactNotFoundException, ArtifactResolutionException, IOException |
| { |
| String applicationVersion = getVersion(); |
| |
| DatabaseParams params = new DatabaseParams( databaseType.defaultParams ); |
| params.setUrl( jdbcUrl ); |
| |
| DefaultPlexusContainer container = new DefaultPlexusContainer(); |
| |
| initializeWagon( container ); |
| |
| List<Artifact> artifacts = new ArrayList<Artifact>(); |
| artifacts.addAll( |
| downloadArtifact( container, params.getGroupId(), params.getArtifactId(), params.getVersion() ) ); |
| artifacts.addAll( |
| downloadArtifact( container, "org.apache.maven.continuum", managementArtifactId, applicationVersion ) ); |
| artifacts.addAll( downloadArtifact( container, "jpox", "jpox", databaseFormat.getJpoxVersion() ) ); |
| |
| List<File> jars = new ArrayList<File>(); |
| |
| // Little hack to make it work more nicely in the IDE |
| List<String> exclusions = new ArrayList<String>(); |
| URLClassLoader cp = (URLClassLoader) DataManagementCli.class.getClassLoader(); |
| for ( URL url : cp.getURLs() ) |
| { |
| String urlEF = url.toExternalForm(); |
| if ( urlEF.endsWith( "target/classes/" ) ) |
| { |
| int idEndIdx = urlEF.length() - 16; |
| String id = urlEF.substring( urlEF.lastIndexOf( '/', idEndIdx - 1 ) + 1, idEndIdx ); |
| // continuum-legacy included because the IDE doesn't do the proper assembly of enhanced classes and JDO metadata |
| if ( !"data-management-api".equals( id ) && !"data-management-cli".equals( id ) && |
| !"continuum-legacy".equals( id ) && !"continuum-model".equals( id ) && |
| !"redback-legacy".equals( id ) ) |
| { |
| exclusions.add( "org.apache.maven.continuum:" + id ); |
| jars.add( new File( url.getPath() ) ); |
| } |
| } |
| |
| // Sometimes finds its way into the IDE. Make sure it is loaded in the extra classloader too |
| if ( urlEF.contains( "jpox-enhancer" ) ) |
| { |
| jars.add( new File( url.getPath() ) ); |
| } |
| } |
| ArtifactFilter filter = new ExcludesArtifactFilter( exclusions ); |
| |
| for ( Artifact a : artifacts ) |
| { |
| if ( "jpox".equals( a.getGroupId() ) && "jpox".equals( a.getArtifactId() ) ) |
| { |
| if ( a.getVersion().equals( databaseFormat.getJpoxVersion() ) ) |
| { |
| jars.add( a.getFile() ); |
| } |
| } |
| else if ( filter.include( a ) ) |
| { |
| jars.add( a.getFile() ); |
| } |
| } |
| |
| ClassRealm realm = container.createComponentRealm( "app", jars ); |
| |
| ClassLoader oldLoader = Thread.currentThread().getContextClassLoader(); |
| Thread.currentThread().setContextClassLoader( realm ); |
| |
| ClassRealm oldRealm = container.setLookupRealm( realm ); |
| |
| DatabaseFactoryConfigurator configurator = (DatabaseFactoryConfigurator) container.lookup( |
| DatabaseFactoryConfigurator.class.getName(), configRoleHint, realm ); |
| configurator.configure( params ); |
| |
| DataManagementTool manager = |
| (DataManagementTool) container.lookup( DataManagementTool.class.getName(), toolRoleHint, realm ); |
| |
| if ( mode == OperationMode.EXPORT ) |
| { |
| manager.backupDatabase( directory ); |
| } |
| else if ( mode == OperationMode.IMPORT ) |
| { |
| manager.eraseDatabase(); |
| manager.restoreDatabase( directory ); |
| } |
| |
| container.setLookupRealm( oldRealm ); |
| Thread.currentThread().setContextClassLoader( oldLoader ); |
| } |
| |
| private static void initializeWagon( DefaultPlexusContainer container ) |
| throws ComponentLookupException, ComponentLifecycleException, IOException |
| { |
| WagonManager wagonManager = (WagonManager) container.lookup( WagonManager.ROLE ); |
| |
| Settings settings = getSettings( container ); |
| |
| try |
| { |
| Proxy proxy = settings.getActiveProxy(); |
| |
| if ( proxy != null ) |
| { |
| if ( proxy.getHost() == null ) |
| { |
| throw new IOException( "Proxy in settings.xml has no host" ); |
| } |
| |
| wagonManager.addProxy( proxy.getProtocol(), proxy.getHost(), proxy.getPort(), proxy.getUsername(), |
| proxy.getPassword(), proxy.getNonProxyHosts() ); |
| } |
| |
| for ( Iterator i = settings.getServers().iterator(); i.hasNext(); ) |
| { |
| Server server = (Server) i.next(); |
| |
| wagonManager.addAuthenticationInfo( server.getId(), server.getUsername(), server.getPassword(), |
| server.getPrivateKey(), server.getPassphrase() ); |
| |
| wagonManager.addPermissionInfo( server.getId(), server.getFilePermissions(), |
| server.getDirectoryPermissions() ); |
| |
| if ( server.getConfiguration() != null ) |
| { |
| wagonManager.addConfiguration( server.getId(), (Xpp3Dom) server.getConfiguration() ); |
| } |
| } |
| |
| RepositoryPermissions defaultPermissions = new RepositoryPermissions(); |
| |
| defaultPermissions.setDirectoryMode( "775" ); |
| |
| defaultPermissions.setFileMode( "664" ); |
| |
| wagonManager.setDefaultRepositoryPermissions( defaultPermissions ); |
| |
| for ( Iterator i = settings.getMirrors().iterator(); i.hasNext(); ) |
| { |
| Mirror mirror = (Mirror) i.next(); |
| |
| wagonManager.addMirror( mirror.getId(), mirror.getMirrorOf(), mirror.getUrl() ); |
| } |
| } |
| finally |
| { |
| container.release( wagonManager ); |
| } |
| |
| } |
| |
| private static Collection<Artifact> downloadArtifact( PlexusContainer container, String groupId, String artifactId, |
| String version ) |
| throws ComponentLookupException, ArtifactNotFoundException, ArtifactResolutionException, IOException |
| { |
| ArtifactRepositoryFactory factory = |
| (ArtifactRepositoryFactory) container.lookup( ArtifactRepositoryFactory.ROLE ); |
| |
| DefaultRepositoryLayout layout = |
| (DefaultRepositoryLayout) container.lookup( ArtifactRepositoryLayout.ROLE, "default" ); |
| |
| ArtifactRepository localRepository = |
| factory.createArtifactRepository( "local", getLocalRepositoryURL( container ), layout, null, null ); |
| |
| List<ArtifactRepository> remoteRepositories = new ArrayList<ArtifactRepository>(); |
| remoteRepositories.add( |
| factory.createArtifactRepository( "central", "http://repo1.maven.org/maven2", layout, null, null ) ); |
| //Load extra repositories from active profile |
| |
| Settings settings = getSettings( container ); |
| List<String> profileIds = settings.getActiveProfiles(); |
| List<Profile> profiles = settings.getProfiles(); |
| Map<String, Profile> profilesAsMap = settings.getProfilesAsMap(); |
| if ( profileIds != null && !profileIds.isEmpty() ) |
| { |
| for ( String profileId : profileIds ) |
| { |
| Profile profile = profilesAsMap.get( profileId ); |
| List<Repository> repos = profile.getRepositories(); |
| if ( repos != null && !repos.isEmpty() ) |
| { |
| for ( Repository repo : repos ) |
| { |
| remoteRepositories.add( factory.createArtifactRepository( repo.getId(), repo |
| .getUrl(), layout, null, null ) ); |
| } |
| } |
| } |
| } |
| |
| ArtifactFactory artifactFactory = (ArtifactFactory) container.lookup( ArtifactFactory.ROLE ); |
| Artifact artifact = |
| artifactFactory.createArtifact( groupId, artifactId, version, Artifact.SCOPE_RUNTIME, "jar" ); |
| Artifact dummyArtifact = artifactFactory.createProjectArtifact( "dummy", "dummy", "1.0" ); |
| |
| if ( artifact.isSnapshot() ) |
| { |
| remoteRepositories.add( factory.createArtifactRepository( "apache.snapshots", |
| "http://people.apache.org/repo/m2-snapshot-repository", |
| layout, null, null ) ); |
| } |
| |
| ArtifactResolver resolver = (ArtifactResolver) container.lookup( ArtifactResolver.ROLE ); |
| |
| List<String> exclusions = new ArrayList<String>(); |
| exclusions.add( "org.apache.maven.continuum:data-management-api" ); |
| exclusions.add( "org.codehaus.plexus:plexus-component-api" ); |
| exclusions.add( "org.codehaus.plexus:plexus-container-default" ); |
| exclusions.add( "stax:stax-api" ); |
| exclusions.add( "log4j:log4j" ); |
| |
| ArtifactFilter filter = new ExcludesArtifactFilter( exclusions ); |
| |
| ArtifactMetadataSource source = |
| (ArtifactMetadataSource) container.lookup( ArtifactMetadataSource.ROLE, "maven" ); |
| ArtifactResolutionResult result = resolver.resolveTransitively( Collections.singleton( artifact ), |
| dummyArtifact, localRepository, |
| remoteRepositories, source, filter ); |
| |
| return result.getArtifacts(); |
| } |
| |
| private static String getLocalRepositoryURL( PlexusContainer container ) |
| throws ComponentLookupException, IOException |
| { |
| File settingsFile = new File( System.getProperty( "user.home" ), ".m2/settings.xml" ); |
| if ( !settingsFile.exists() ) |
| { |
| return new File( System.getProperty( "user.home" ), ".m2/repository" ).toURL().toString(); |
| } |
| else |
| { |
| Settings settings = getSettings( container ); |
| return new File( settings.getLocalRepository() ).toURL().toString(); |
| } |
| } |
| |
| private static Settings getSettings( PlexusContainer container ) |
| throws ComponentLookupException, IOException |
| { |
| MavenSettingsBuilder mavenSettingsBuilder = |
| (MavenSettingsBuilder) container.lookup( MavenSettingsBuilder.class.getName() ); |
| try |
| { |
| return mavenSettingsBuilder.buildSettings( false ); |
| } |
| catch ( XmlPullParserException e ) |
| { |
| e.printStackTrace(); |
| throw new IOException( "Can't read settings.xml. " + e.getMessage() ); |
| } |
| } |
| |
| private static String getVersion() |
| throws IOException |
| { |
| Properties properties = new Properties(); |
| properties.load( DataManagementCli.class.getResourceAsStream( |
| "/META-INF/maven/org.apache.maven.continuum/data-management-api/pom.properties" ) ); |
| return properties.getProperty( "version" ); |
| } |
| |
| private static class Commands |
| { |
| |
| @Argument(description = "Display help information", value = "help", alias = "h") |
| private boolean help; |
| |
| @Argument(description = "Display version information", value = "version", alias = "v") |
| private boolean version; |
| |
| @Argument( |
| description = "The JDBC URL for the Continuum database that contains the data to convert, or to import the data into", |
| value = "buildsJdbcUrl") |
| private String buildsJdbcUrl; |
| |
| @Argument( |
| description = "The JDBC URL for the Redback database that contains the data to convert, or to import the data into", |
| value = "usersJdbcUrl") |
| private String usersJdbcUrl; |
| |
| // TODO: ability to use the enum directly would be nice |
| @Argument( |
| description = "Format of the database. Valid values are CONTINUUM_103, CONTINUUM_109, CONTINUUM_11. Default is CONTINUUM_11.") |
| private String databaseFormat = DatabaseFormat.CONTINUUM_11.toString(); |
| |
| /* TODO: not yet supported |
| @Argument( |
| description = "Format of the backup directory. Valid values are CONTINUUM_103, CONTINUUM_109, CONTINUUM_11. Default is CONTINUUM_11.") |
| private String dataFileFormat = DatabaseFormat.CONTINUUM_11.toString(); |
| */ |
| |
| @Argument( |
| description = "The directory to export the data to, or import the data from. Default is 'backups' in the current working directory.", |
| value = "directory") |
| private File directory = new File( "backups" ); |
| |
| @Argument( |
| description = "Mode of operation. Valid values are IMPORT and EXPORT. Default is EXPORT.", |
| value = "mode") |
| private String mode = OperationMode.EXPORT.toString(); |
| |
| @Argument( |
| description = "Whether to overwrite the designated directory if it already exists in export mode. Default is false.", |
| value = "overwrite") |
| private boolean overwrite; |
| |
| @Argument( |
| description = "The type of database to use. Currently supported values are DERBY_10_1. The default value is DERBY_10_1.", |
| value = "databaseType") |
| private String databaseType = SupportedDatabase.DERBY_10_1.toString(); |
| |
| @Argument(description = "JDBC driver class", value = "driverClass", required = false) |
| private String driverClass; |
| |
| @Argument(description = "JDBC driver groupId", value = "groupId", required = false) |
| private String groupId; |
| |
| @Argument(description = "JDBC driver artifactId", value = "artifactId", required = false) |
| private String artifactId; |
| |
| @Argument(description = "Artifact version of the JDBC driver class", |
| value = "artifactVersion", |
| required = false) |
| private String artifactVersion; |
| |
| @Argument(description = "Username", value = "username", required = false) |
| private String username; |
| |
| @Argument(description = "Password", value = "password", required = false) |
| private String password; |
| |
| @Argument( |
| description = "Turn on debugging information. Default is off.", |
| value = "debug") |
| private boolean debug; |
| } |
| |
| private enum OperationMode |
| { |
| IMPORT, EXPORT |
| } |
| |
| private enum SupportedDatabase |
| { |
| DERBY_10_1( new DatabaseParams( "org.apache.derby.jdbc.EmbeddedDriver", "org.apache.derby", "derby", "10.1.3.1", |
| "sa", "" ) ), |
| |
| OTHER( new DatabaseParams( null, null, null, null, null, null ) ); |
| |
| private DatabaseParams defaultParams; |
| |
| SupportedDatabase( DatabaseParams defaultParams ) |
| { |
| this.defaultParams = defaultParams; |
| } |
| } |
| } |