[MRM-980] ability to merge repositories

submitted by Patti Arachchige Eshan Sudharaka

o added audit logs for merging repos
o fixed merging, merge from staging to managed repo instead of the other way around
o change the repository id in artifact browse from staging one to managed one after the merging is done 
o removed unused actions in struts.xml and gave proper name for methods in MergeAction class

additional changes made on top of the patch:
o fixed the title and messages displayed in the merge jsp page
o fixed formatting of merge action


git-svn-id: https://svn.apache.org/repos/asf/archiva/branches/archiva-MRM-980@979534 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Maria Odea B. Ching 2010-07-27 05:07:48 +00:00
parent 4af6b292e4
commit e711ec5177
8 changed files with 317 additions and 288 deletions

View File

@ -22,13 +22,16 @@ package org.apache.maven.archiva.web.action;
import com.opensymphony.xwork2.Validateable;
import com.opensymphony.xwork2.Preparable;
import org.apache.archiva.audit.Auditable;
import org.apache.archiva.audit.AuditEvent;
import org.apache.archiva.stagerepository.merge.Maven2RepositoryMerger;
import org.apache.archiva.metadata.model.ArtifactMetadata;
import org.apache.archiva.metadata.repository.filter.Filter;
import org.apache.archiva.metadata.repository.filter.IncludesFilter;
import org.apache.archiva.metadata.repository.MetadataRepository;
import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration;
import org.apache.maven.archiva.configuration.Configuration;
import org.apache.maven.archiva.configuration.ArchivaConfiguration;
import java.io.File;
import java.util.List;
/**
@ -49,6 +52,11 @@ public class MergeAction
*/
private ArchivaConfiguration configuration;
/**
* @plexus.requirement role-hint="default"
*/
private MetadataRepository metadataRepository;
private ManagedRepositoryConfiguration repository;
private String repoid;
@ -57,9 +65,13 @@ public class MergeAction
private final String action = "merge";
List<ArtifactMetadata> conflictList;
private final String noConflicts = "NO CONFLICTS";
public String doMerge()
private final String hasConflicts = "CONFLICTS";
private List<ArtifactMetadata> conflictSourceArtifacts;
public String getConflicts()
{
targetRepoId = repoid + "-stage";
Configuration config = configuration.getConfiguration();
@ -67,18 +79,56 @@ public class MergeAction
if ( targetRepoConfig != null )
{
return hasConflicts;
try
}
else
{
return ERROR;
}
}
public String doMerge()
throws Exception
{
try
{
List<ArtifactMetadata> sourceArtifacts = metadataRepository.getArtifacts( targetRepoId );
repositoryMerger.merge( targetRepoId, repoid );
triggerAuditEvent( targetRepoId, "file-eshan", AuditEvent.MERGING_REPOSITORIES );
for ( ArtifactMetadata metadata : sourceArtifacts )
{
repositoryMerger.merge( repoid, targetRepoId );
}
catch ( Exception e )
{
return ERROR;
triggerAuditEvent( targetRepoId, metadata.getId(), AuditEvent.MERGING_REPOSITORIES );
}
return SUCCESS;
}
else
catch ( Exception ex )
{
return ERROR;
}
}
public String mergeBySkippingConflicts()
{
try
{
List<ArtifactMetadata> sourceArtifacts = metadataRepository.getArtifacts( targetRepoId );
sourceArtifacts.removeAll( conflictSourceArtifacts );
Filter<ArtifactMetadata> artifactsWithOutConflicts =
new IncludesFilter<ArtifactMetadata>( sourceArtifacts );
repositoryMerger.merge( targetRepoId, repoid, artifactsWithOutConflicts );
for ( ArtifactMetadata metadata : sourceArtifacts )
{
triggerAuditEvent( targetRepoId, metadata.getId(), AuditEvent.MERGING_REPOSITORIES );
//
}
return SUCCESS;
}
catch ( Exception ex )
{
return ERROR;
}
@ -91,22 +141,15 @@ public class MergeAction
try
{
conflictList = repositoryMerger.mergeWithOutConflictArtifacts( repoid, targetRepoId );
conflictSourceArtifacts = repositoryMerger.getConflictsartifacts( targetRepoId, repoid );
}
catch ( Exception e )
{
return ERROR;
}
return SUCCESS;
}
public void prepare()
throws Exception
{
this.repository = new ManagedRepositoryConfiguration();
}
public ManagedRepositoryConfiguration getRepository()
{
return repository;
@ -117,6 +160,24 @@ public class MergeAction
this.repository = repository;
}
public void prepare()
throws Exception
{
targetRepoId = repoid + "-stage";
conflictSourceArtifacts = repositoryMerger.getConflictsartifacts( targetRepoId, repoid );
this.repository = new ManagedRepositoryConfiguration();
}
public String getTargetRepoId()
{
return targetRepoId;
}
public void setTargetRepoId( String targetRepoId )
{
this.targetRepoId = targetRepoId;
}
public String getRepoid()
{
return repoid;
@ -126,4 +187,15 @@ public class MergeAction
{
this.repoid = repoid;
}
public List<ArtifactMetadata> getConflictSourceArtifacts()
{
return conflictSourceArtifacts;
}
public void setConflictSourceArtifacts( List<ArtifactMetadata> conflictSourceArtifacts )
{
this.conflictSourceArtifacts = conflictSourceArtifacts;
}
}

View File

@ -359,18 +359,12 @@
<interceptor-ref name="configuredPrepareParamsStack"/>
</action>
<action name="conflictsArtifacts" class="mergeAction" method="mergeWithOutConlficts" >
<result name="success"> /WEB-INF/jsp/mergeActionResults.jsp</result>
<interceptor-ref name="configuredPrepareParamsStack"/>
</action>
<action name="merge" class="mergeAction" method="doMerge" >
<!--<result name="input"> /WEB-INF/jsp/mergeAction.jsp</result>-->
<!--<result name="conflictsAvailable"> /WEB-INF/jsp/mergeExcludeConflicts.jsp</result>-->
<result name="success"> /WEB-INF/jsp/mergeActionResults.jsp</result>
<interceptor-ref name="configuredPrepareParamsStack"/>
</action>
<action name="merge" class="mergeAction" method="getConflicts">
<result name="CONFLICTS">/WEB-INF/jsp/mergeExcludeConflicts.jsp</result>
<result name="success">/WEB-INF/jsp/mergeActionResults.jsp</result>
<interceptor-ref name="configuredPrepareParamsStack"/>
</action>
<action name="deleteRemoteRepository" class="deleteRemoteRepositoryAction" method="input">
<result name="input">/WEB-INF/jsp/admin/deleteRemoteRepository.jsp</result>

View File

@ -1,21 +1,22 @@
<%@ page import="java.io.File" %>
<%--
~ 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.
--%>
~ 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.
--%>
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
@ -115,63 +116,6 @@
<h3 class="repository">${repository.name}</h3>
<table class="infoTable">
<tr>
<th>Groups</th>
<td>
<c:forEach items="${repositoryToGroupMap[repository.id]}" varStatus="i" var="group">
${group}<c:if test="${!i.last}">,</c:if>
</c:forEach>
</td>
</tr>
<tr>
<th>Delete Released Snapshots</th>
<td class="${repository.deleteReleasedSnapshots ? 'donemark' : 'errormark'} booleanIcon"></td>
</tr>
<tr>
<th>Repository Purge By Days Older Than</th>
<td>${repository.daysOlder}</td>
</tr>
<tr>
<th>Repository Purge By Retention Count</th>
<td>${repository.retentionCount}</td>
</tr>
<tr>
<th>Scanning Cron</th>
<td>${repository.refreshCronExpression}</td>
</tr>
<tr>
<th>
Actions
</th>
<td>
<c:choose>
<c:when test="${empty (stats)}">
No Statistics Available.
</c:when>
<c:otherwise>
<table>
<tr>
<th>Last Scanned</th>
<td>${stats.scanStartTime}</td>
</tr>
<tr>
<th>Duration</th>
<td>${stats.duration} ms</td>
</tr>
<tr>
<th>Total File Count</th>
<td>${stats.totalFileCount}
</tr>
<tr>
<th>New Files Found</th>
<td>${stats.newFileCount}
</tr>
</table>
</c:otherwise>
</c:choose>
</td>
</tr>
<c:if test="${!empty (repositoryToGroupMap[repository.id])}">
<tr>
<th>Groups</th>
<td>
@ -180,20 +124,10 @@
</c:forEach>
</td>
</tr>
</c:if>
<tr>
<th>Releases Included</th>
<td class="${repository.releases ? 'donemark' : 'errormark'} booleanIcon"></td>
</tr>
<tr>
<th>Snapshots Included</th>
<td class="${repository.snapshots ? 'donemark' : 'errormark'} booleanIcon"></td>
</tr>
<c:if test="${repository.snapshots}">
<tr>
<th>Delete Released Snapshots</th>
<td class="${repository.deleteReleasedSnapshots ? 'donemark' : 'errormark'} booleanIcon"></td>
</tr>
</tr>
<tr>
<th>Repository Purge By Days Older Than</th>
<td>${repository.daysOlder}</td>
@ -202,12 +136,6 @@
<th>Repository Purge By Retention Count</th>
<td>${repository.retentionCount}</td>
</tr>
</c:if>
<tr>
<th>Scanned</th>
<td class="${repository.scanned ? 'donemark' : 'errormark'} booleanIcon"></td>
</tr>
<c:if test="${repository.scanned}">
<tr>
<th>Scanning Cron</th>
<td>${repository.refreshCronExpression}</td>
@ -217,25 +145,6 @@
Actions
</th>
<td>
<redback:ifAuthorized permission="archiva-run-indexer">
<s:form action="indexRepository" theme="simple">
<s:hidden name="repoid" value="%{#attr.repository.id}"/>
<table>
<tr>
<td><s:checkbox name="scanAll" value="scanAll"/>Process All Artifacts</td>
</tr>
<tr>
<td><s:submit value="Scan Repository Now"/></td>
</tr>
</table>
</s:form>
</redback:ifAuthorized>
</td>
</tr>
<tr>
<th>Stats</th>
<td>
<c:set var="stats" value="${repositoryStatistics[repository.id]}"/>
<c:choose>
<c:when test="${empty (stats)}">
No Statistics Available.
@ -263,51 +172,144 @@
</c:choose>
</td>
</tr>
</c:if>
<tr>
<th>POM Snippet</th>
<td>
<archiva:copy-paste-snippet object="${repository}" wrapper="toggle"/>
</td>
<c:if test="${!empty (repositoryToGroupMap[repository.id])}">
<tr>
<th>Groups</th>
<td>
<c:forEach items="${repositoryToGroupMap[repository.id]}" varStatus="i" var="group">
${group}<c:if test="${!i.last}">,</c:if>
</c:forEach>
</td>
</tr>
</c:if>
<tr>
<th>Releases Included</th>
<td class="${repository.releases ? 'donemark' : 'errormark'} booleanIcon"></td>
</tr>
<tr>
<th>Snapshots Included</th>
<td class="${repository.snapshots ? 'donemark' : 'errormark'} booleanIcon"></td>
</tr>
<c:if test="${repository.snapshots}">
<tr>
<th>Delete Released Snapshots</th>
<td class="${repository.deleteReleasedSnapshots ? 'donemark' : 'errormark'} booleanIcon"></td>
</tr>
<tr>
<th>Repository Purge By Days Older Than</th>
<td>${repository.daysOlder}</td>
</tr>
<tr>
<th>Repository Purge By Retention Count</th>
<td>${repository.retentionCount}</td>
</tr>
</c:if>
<tr>
<th>Scanned</th>
<td class="${repository.scanned ? 'donemark' : 'errormark'} booleanIcon"></td>
</tr>
<c:if test="${repository.scanned}">
<tr>
<th>Scanning Cron</th>
<td>${repository.refreshCronExpression}</td>
</tr>
<tr>
<th>
Actions
</th>
<td>
<redback:ifAuthorized permission="archiva-run-indexer">
<s:form action="indexRepository" theme="simple">
<s:hidden name="repoid" value="%{#attr.repository.id}"/>
<table>
<tr>
<td><s:checkbox name="scanAll" value="scanAll"/>Process All Artifacts</td>
</tr>
<tr>
<td><s:submit value="Scan Repository Now"/></td>
</tr>
</table>
</s:form>
</redback:ifAuthorized>
</td>
</tr>
<tr>
<th>Stats</th>
<td>
<c:set var="stats" value="${repositoryStatistics[repository.id]}"/>
<c:choose>
<c:when test="${empty (stats)}">
No Statistics Available.
</c:when>
<c:otherwise>
<table>
<tr>
<th>Last Scanned</th>
<td>${stats.scanStartTime}</td>
</tr>
<tr>
<th>Duration</th>
<td>${stats.duration} ms</td>
</tr>
<tr>
<th>Total File Count</th>
<td>${stats.totalFileCount}
</tr>
<tr>
<th>New Files Found</th>
<td>${stats.newFileCount}
</tr>
</table>
</c:otherwise>
</c:choose>
</td>
</tr>
</c:if>
<tr>
<th>POM Snippet</th>
<td>
<archiva:copy-paste-snippet object="${repository}" wrapper="toggle"/>
</td>
</tr>
</tr>
<c:set var="str" value="${repository.id}"/>
<jsp:useBean id="str" type="java.lang.String"/>
<c:if test='<%= !( (str.equalsIgnoreCase("internal") ) || (str.equalsIgnoreCase( "snapshots" )) )%>'>
<tr>
<th>Merge Actions</th>
<td >
<redback:ifAuthorized permission="archiva-run-indexer">
<s:form action="merge" theme="simple">
<s:hidden name="repoid" value="%{#attr.repository.id}"/>
<table>
<tr>
<td><s:submit value="Merge All"/></td>
</tr>
<c:set var="str" value="${repository.id}" />
<jsp:useBean id="str" type="java.lang.String" scope="page"/>
<c:set var="location" value="${repository.location}"/>
<jsp:useBean id="location" type="java.lang.String" scope="page"/>
</table>
</s:form>
</redback:ifAuthorized>
</td>
<td align="left">
<redback:ifAuthorized permission="archiva-run-indexer">
<s:form action="conflictsArtifacts" theme="simple">
<s:hidden name="repoid" value="%{#attr.repository.id}"/>
<table>
<tr>
<td align="left" ><s:submit value="Merge With skip"/></td>
</tr>
<c:if
test='<%= !( (str.equalsIgnoreCase("internal") ) || (str.equalsIgnoreCase( "snapshots" )) ) &&
new File (new File(location ).getParent() ,str + "-stage" ).exists()%>'>
<tr>
<th>
stage repository location
</th>
<td>
${repository.location}${'-stage'}
</td>
</tr>
<tr>
<th>Merge Actions</th>
<td>
<redback:ifAuthorized permission="archiva-run-indexer">
<s:form action="merge" theme="simple">
<s:hidden name="repoid" value="%{#attr.repository.id}"/>
<%--<s:hidden name="repository" value="%{repository}"/>--%>
<table>
<tr>
<td><s:submit value="Merge"/></td>
</tr>
</table>
</s:form>
</redback:ifAuthorized>
</td>
</tr>
</table>
</s:form>
</redback:ifAuthorized>
</td>
</tr>
</c:if>
</c:if>
</table>

View File

@ -23,121 +23,78 @@
<html>
<head>
<title>Admin: Add Managed Repository</title>
<title>Admin: Merge Staging Repository</title>
<s:head/>
</head>
<body>
<h1>Admin: Add Managed Repository</h1>
<h1>Admin: Merge Staging Repository</h1>
<div class="warningbox">
<p>
<strong>WARNING: There Are some conflicts Artifacts.</strong>
</p>
</div>
<p>
Are you sure you want to merge the repository?
</p>
<p>
Are you sure you want to merge the Repository
<%--<c:choose>--%>
<%--<c:when test="${action == 'merge'}">add</c:when>--%>
<%--<c:otherwise>update</c:otherwise>--%>
<%--</c:choose>--%>
<div class="infobox">
<table class="infotable">
</p>
<div class="infobox">
<table class="infotable">
<tr>
<td>ID:</td>
<td><code>${repository.id}</code></td>
</tr>
<tr>
<td>Name:</td>
<td>${repository.name}</td>
</tr>
<tr>
<td>Directory:</td>
<td>${repository.location}</td>
</tr>
<tr>
<td>Index Directory:</td>
<td>${repository.indexDir}</td>
</tr>
<tr>
<td>Type:</td>
<%--td>${repository.layout}</td--%>
<td>
<c:choose>
<c:when test="${repository.layout == 'default'}">
Maven 2.x Repository
</c:when>
<c:otherwise>
Maven 1.x Repository
</c:otherwise>
</c:choose>
</td>
</tr>
<tr>
<td>Cron:</td>
<td>${repository.refreshCronExpression}</td>
</tr>
<tr>
<td>Repository Purge By Days Older Than:</td>
<td>${repository.daysOlder}</td>
</tr>
<tr>
<td>Repository Purge By Retention Count:</td>
<td>${repository.retentionCount}</td>
</tr>
<tr>
<td>Releases Included:</td>
<td class="${repository.releases ? 'donemark' : 'errormark'} booleanIcon">
</tr>
<tr>
<td>Snapshots Included:</td>
<td class="${repository.snapshots ? 'donemark' : 'errormark'} booleanIcon">
</tr>
<tr>
<td>Scannable:</td>
<td class="${repository.scanned ? 'donemark' : 'errormark'} booleanIcon">
</tr>
<tr>
<td>Delete Released Snapshots:</td>
<td class="${repository.deleteReleasedSnapshots ? 'donemark' : 'errormark'} booleanIcon">
</tr>
</table>
</div>
<s:form method="post" action="%{action}" namespace="/admin" validate="true" theme="simple">
<div class="buttons">
<s:hidden name="repository.id" value="%{#attr.repository.id}"/>
<s:hidden name="repository.name" value="%{#attr.repository.name}"/>
<s:hidden name="repository.location" value="%{#attr.repository.location}"/>
<s:hidden name="repository.indexDir" value="%{#attr.repository.indexDir}"/>
<s:hidden name="repository.layout" value="%{#attr.repository.layout}"/>
<s:hidden name="repository.refreshCronExpression" value="%{#attr.repository.refreshCronExpression}"/>
<s:hidden name="repository.daysOlder" value="%{#attr.repository.daysOlder}"/>
<s:hidden name="repository.retentionCount" value="%{#attr.repository.retentionCount}"/>
<s:hidden name="repository.releases" value="%{#attr.repository.releases}"/>
<s:hidden name="repository.snapshots" value="%{#attr.repository.snapshots}"/>
<s:hidden name="repository.scanned" value="%{#attr.repository.scanned}"/>
<s:hidden name="repository.deleteReleasedSnapshots" value="%{#attr.repository.deleteReleasedSnapshots}"/>
<%--<c:choose>--%>
<s:submit value="Save" method="merge"/>
<%--<c:when test="${action == 'addRepository'}">--%>
<%--<s:submit value="Save" method="confirmAdd"/>--%>
<%--</c:when>--%>
<%--<c:otherwise>--%>
<%--<s:submit value="Save" method="confirmUpdate"/>--%>
<%--</c:otherwise>--%>
<%--</c:choose>--%>
<s:submit value="Cancel" method="execute"/>
</div>
</s:form>
<c:choose>
<c:when test="${empty (conflictSourceArtifacts)}">
<h1>No conflicting artifacts</h1>
<s:form method="post" action="merge" namespace="/admin" validate="false" theme="simple">
<s:hidden name="repoid"/>
<div class="buttons">
<s:submit value="MergeAll" method="doMerge"/>
</div>
</s:form>
</c:when>
<c:otherwise>
<div class="warningbox">
<p>
<strong>WARNING: The following are the artifacts in conflict.</strong>
</p>
</div>
<c:forEach items="${conflictSourceArtifacts}" var="artifact">
<tr>
<td>Artifact Id :</td>
<td><code>${artifact.id}</code></td>
</tr>
</c:forEach>
<tr>
<td>
<s:form action="merge" method="post" namespace="/admin" validate="false">
<s:hidden name="repoid"/>
<div class="buttons">
<table>
<tr>
<td>
<table>
<tr>
<td>
<s:submit value="MergeAll" method="doMerge"/>
</td>
</tr>
</table>
</td>
<td>
<table>
<tr>
<td>
<s:submit value="Merge With Skipp" method="mergeBySkippingConflicts"/>
</td>
</tr>
</table>
</td>
</tr>
</table>
</div>
</s:form>
</td>
</tr>
</c:otherwise>
</c:choose>
</table>
</div>
</body>
</html>

View File

@ -69,6 +69,8 @@ public class AuditEvent
public static final String REMOVE_SCANNED = "Removed in Filesystem";
public static final String MERGING_REPOSITORIES = "Merging repositories";
// configuration events
public static final String ADD_MANAGED_REPO = "Added Managed Repository";

View File

@ -33,7 +33,8 @@ public class MetadataAuditListener
public void auditEvent( AuditEvent event )
{
// for now we only log upload events, some of the others are quite noisy
if ( event.getAction().equals( AuditEvent.CREATE_FILE ) || event.getAction().equals( AuditEvent.UPLOAD_FILE ) )
if ( event.getAction().equals( AuditEvent.CREATE_FILE ) || event.getAction().equals( AuditEvent.UPLOAD_FILE ) ||
event.getAction().equals( AuditEvent.MERGING_REPOSITORIES ) )
{
auditManager.addAuditEvent( event );
}

View File

@ -89,6 +89,7 @@ public class Maven2RepositoryMerger
List<ArtifactMetadata> artifactsInSourceRepo = metadataRepository.getArtifacts( sourceRepoId );
for ( ArtifactMetadata artifactMetadata : artifactsInSourceRepo )
{
artifactMetadata.setRepositoryId( targetRepoId );
createFolderStructure( sourceRepoId, targetRepoId, artifactMetadata );
}
}
@ -325,7 +326,7 @@ public class Maven2RepositoryMerger
return metadata;
}
public List<ArtifactMetadata> mergeWithOutConflictArtifacts( String sourceRepo, String targetRepo )
public List<ArtifactMetadata> getConflictsartifacts( String sourceRepo, String targetRepo )
throws Exception
{
@ -354,7 +355,7 @@ public class Maven2RepositoryMerger
sourceArtifacts.removeAll( conflictsArtifacts );
Filter<ArtifactMetadata> artifactsWithOutConflicts = new IncludesFilter<ArtifactMetadata>( sourceArtifacts );
merge( sourceRepo, targetRepo, artifactsWithOutConflicts );
// merge( sourceRepo, targetRepo, artifactsWithOutConflicts );
return conflictsArtifacts;
}

View File

@ -154,7 +154,7 @@ public class Maven2RepositoryMergerTest
when( metadataRepository.getArtifacts( sourceRepoId ) ).thenReturn( sourceRepoArtifactsList );
when( metadataRepository.getArtifacts( TEST_REPO_ID ) ).thenReturn( targetRepoArtifactsList );
assertEquals( 1, repositoryMerger.mergeWithOutConflictArtifacts( sourceRepoId, TEST_REPO_ID ).size() );
assertEquals( 1, repositoryMerger.getConflictsartifacts( sourceRepoId, TEST_REPO_ID ).size() );
verify( metadataRepository ).getArtifacts( TEST_REPO_ID );
}