From: Brett Porter Date: Tue, 19 Jan 2010 06:15:09 +0000 (+0000) Subject: merged recent changes from trunk, and reimplemented incoming audit log functionality... X-Git-Tag: archiva-1.4-M1~1017^2~43 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=606c82cbcad53a9f2c0dd1212ce302e3c8626910;p=archiva.git merged recent changes from trunk, and reimplemented incoming audit log functionality as a plugin using the metadata repository git-svn-id: https://svn.apache.org/repos/asf/archiva/branches/MRM-1025@900664 13f79535-47bb-0310-9956-ffa450edef68 --- 606c82cbcad53a9f2c0dd1212ce302e3c8626910 diff --cc archiva-cli/pom.xml index 724a4860c,a74efdbaf..aae3c57eb --- a/archiva-cli/pom.xml +++ b/archiva-cli/pom.xml @@@ -35,15 -35,9 +35,14 @@@ org.apache.archiva archiva-dependency-tree-consumer - 1.3-SNAPSHOT + 1.4-SNAPSHOT runtime + + org.apache.archiva + archiva-metadata-consumer - 1.3-SNAPSHOT + runtime + org.apache.archiva archiva-xml-tools @@@ -101,7 -95,7 +100,7 @@@ -- xerces:xercesImpl ++ xerces:xercesImpl xml-apis:xml-apis xalan:xalan commons-beanutils:commons-beanutils diff --cc archiva-modules/archiva-base/archiva-consumers/archiva-metadata-consumer/pom.xml index ac3e6748a,000000000..1b87b3f0b mode 100644,000000..100644 --- a/archiva-modules/archiva-base/archiva-consumers/archiva-metadata-consumer/pom.xml +++ b/archiva-modules/archiva-base/archiva-consumers/archiva-metadata-consumer/pom.xml @@@ -1,48 -1,0 +1,48 @@@ + + + + + 4.0.0 + + archiva-consumers + org.apache.archiva - 1.3-SNAPSHOT ++ 1.4-SNAPSHOT + + archiva-metadata-consumer + Archiva Metadata Consumer + + + org.apache.archiva + archiva-consumer-api + + + org.apache.archiva + metadata-model + + + org.apache.archiva + metadata-repository-api + + + org.apache.archiva + archiva-repository-layer + + + diff --cc archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/maven/archiva/repository/audit/AuditEvent.java index ead95b502,ead95b502..ec3556562 --- a/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/maven/archiva/repository/audit/AuditEvent.java +++ b/archiva-modules/archiva-base/archiva-repository-layer/src/main/java/org/apache/maven/archiva/repository/audit/AuditEvent.java @@@ -19,13 -19,13 +19,25 @@@ package org.apache.maven.archiva.reposi * under the License. */ ++import java.text.ParseException; ++import java.text.SimpleDateFormat; ++import java.util.Calendar; ++import java.util.Date; ++import java.util.HashMap; ++import java.util.Map; ++ ++import org.apache.archiva.metadata.model.MetadataFacet; ++ /** * AuditEvent -- * ++ * * @version $Id$ */ public class AuditEvent ++ implements MetadataFacet { ++ public static final String TIMESTAMP_FORMAT = "yyyy/MM/dd/HHmmss.SSS"; ++ public static final String CREATE_DIR = "Created Directory"; public static final String CREATE_FILE = "Created File"; @@@ -82,47 -82,47 +94,51 @@@ public static final String DISABLE_REPO_CONSUMER = "Disabled Content Consumer"; -- public static final String ENABLE_DB_CONSUMER = "Enabled Database Consumer"; -- -- public static final String DISABLE_DB_CONSUMER = "Disabled Database Consumer"; -- public static final String ADD_PATTERN = "Added File Type Pattern"; public static final String REMOVE_PATTERN = "Removed File Type Pattern"; -- public static final String DB_SCHEDULE = "Modified Scanning Schedule"; -- private String repositoryId; private String userId; private String remoteIP; ++ // TODO: change to artifact reference? does it ever refer to just a path? ++ private String resource; private String action; ++ private Date timestamp; ++ ++ public static final String FACET_ID = "org.apache.archiva.audit"; ++ public AuditEvent() { /* do nothing */ } ++ public AuditEvent( String name, String repositoryId ) ++ { ++ try ++ { ++ timestamp = new SimpleDateFormat( TIMESTAMP_FORMAT ).parse( name ); ++ } ++ catch ( ParseException e ) ++ { ++ throw new IllegalArgumentException( "Improperly formatted timestamp for audit log event: " + name ); ++ } ++ this.repositoryId = repositoryId; ++ } ++ public AuditEvent( String repoId, String user, String resource, String action ) { this.repositoryId = repoId; this.userId = user; this.resource = resource; this.action = action; -- } -- -- public AuditEvent( String user, String resource, String action ) -- { -- this( null, user, resource, action ); -- } -- -- public AuditEvent( String principal, String action2 ) -- { -- this( null, principal, action2 ); ++ this.timestamp = Calendar.getInstance().getTime(); } public String getRepositoryId() @@@ -174,4 -174,4 +190,113 @@@ { this.remoteIP = remoteIP; } ++ ++ public Date getTimestamp() ++ { ++ return timestamp; ++ } ++ ++ public void setTimestamp( Date timestamp ) ++ { ++ this.timestamp = timestamp; ++ } ++ ++ public String getFacetId() ++ { ++ return FACET_ID; ++ } ++ ++ public String getName() ++ { ++ return new SimpleDateFormat( TIMESTAMP_FORMAT ).format( timestamp ); ++ } ++ ++ public Map toProperties() ++ { ++ Map properties = new HashMap(); ++ properties.put( "action", this.action ); ++ if ( this.userId != null ) ++ { ++ properties.put( "user", this.userId ); ++ } ++ if ( this.remoteIP != null ) ++ { ++ properties.put( "remoteIP", this.remoteIP ); ++ } ++ if ( this.resource != null ) ++ { ++ properties.put( "resource", this.resource ); ++ } ++ return properties; ++ } ++ ++ public void fromProperties( Map properties ) ++ { ++ this.action = properties.get( "action" ); ++ this.remoteIP = properties.get( "remoteIP" ); ++ this.userId = properties.get( "user" ); ++ this.resource = properties.get( "resource" ); ++ } ++ ++ @Override ++ public boolean equals( Object o ) ++ { ++ if ( this == o ) ++ { ++ return true; ++ } ++ if ( o == null || getClass() != o.getClass() ) ++ { ++ return false; ++ } ++ ++ AuditEvent that = (AuditEvent) o; ++ ++ if ( !action.equals( that.action ) ) ++ { ++ return false; ++ } ++ if ( remoteIP != null ? !remoteIP.equals( that.remoteIP ) : that.remoteIP != null ) ++ { ++ return false; ++ } ++ if ( repositoryId != null ? !repositoryId.equals( that.repositoryId ) : that.repositoryId != null ) ++ { ++ return false; ++ } ++ if ( resource != null ? !resource.equals( that.resource ) : that.resource != null ) ++ { ++ return false; ++ } ++ if ( !timestamp.equals( that.timestamp ) ) ++ { ++ return false; ++ } ++ if ( userId != null ? !userId.equals( that.userId ) : that.userId != null ) ++ { ++ return false; ++ } ++ ++ return true; ++ } ++ ++ @Override ++ public int hashCode() ++ { ++ int result = repositoryId != null ? repositoryId.hashCode() : 0; ++ result = 31 * result + ( userId != null ? userId.hashCode() : 0 ); ++ result = 31 * result + ( remoteIP != null ? remoteIP.hashCode() : 0 ); ++ result = 31 * result + ( resource != null ? resource.hashCode() : 0 ); ++ result = 31 * result + action.hashCode(); ++ result = 31 * result + timestamp.hashCode(); ++ return result; ++ } ++ ++ @Override ++ public String toString() ++ { ++ return "AuditEvent{" + "repositoryId='" + repositoryId + '\'' + ", userId='" + userId + '\'' + ", remoteIP='" + ++ remoteIP + '\'' + ", resource='" + resource + '\'' + ", action='" + action + '\'' + ", timestamp=" + ++ timestamp + '}'; ++ } } diff --cc archiva-modules/archiva-base/archiva-repository-scanner/pom.xml index bafad290f,000000000..59a853f03 mode 100644,000000..100644 --- a/archiva-modules/archiva-base/archiva-repository-scanner/pom.xml +++ b/archiva-modules/archiva-base/archiva-repository-scanner/pom.xml @@@ -1,54 -1,0 +1,54 @@@ + + + + + 4.0.0 + + archiva-base + org.apache.archiva - 1.3-SNAPSHOT ++ 1.4-SNAPSHOT + + archiva-repository-scanner + Archiva Repository Scanner + + + org.apache.archiva + archiva-consumer-api + + + org.apache.archiva + archiva-configuration + + + org.springframework + spring-context + + + org.codehaus.plexus + plexus-spring + test + + + org.slf4j + slf4j-simple + test + + + diff --cc archiva-modules/archiva-scheduler/archiva-scheduler-api/pom.xml index ffcbe736b,000000000..13d30a2b6 mode 100644,000000..100644 --- a/archiva-modules/archiva-scheduler/archiva-scheduler-api/pom.xml +++ b/archiva-modules/archiva-scheduler/archiva-scheduler-api/pom.xml @@@ -1,18 -1,0 +1,18 @@@ + + + 4.0.0 + + archiva-scheduler + org.apache.archiva - 1.3-SNAPSHOT ++ 1.4-SNAPSHOT + + archiva-scheduler-api + Archiva Base :: Scheduled Tasks :: API + + + org.codehaus.plexus + plexus-taskqueue + + + diff --cc archiva-modules/archiva-scheduler/archiva-scheduler-indexing/pom.xml index 412d05472,000000000..78e0fac8b mode 100644,000000..100644 --- a/archiva-modules/archiva-scheduler/archiva-scheduler-indexing/pom.xml +++ b/archiva-modules/archiva-scheduler/archiva-scheduler-indexing/pom.xml @@@ -1,62 -1,0 +1,62 @@@ + + + 4.0.0 + + archiva-scheduler + org.apache.archiva - 1.3-SNAPSHOT ++ 1.4-SNAPSHOT + + archiva-scheduler-indexing + Archiva Base :: Scheduled Tasks :: Indexing + + + org.apache.archiva + archiva-scheduler-api + + + org.apache.archiva + archiva-configuration + + + org.sonatype.nexus + nexus-indexer + + + org.slf4j + slf4j-api + + + org.slf4j + slf4j-simple + test + + + org.codehaus.plexus + plexus-spring + test + + + + + + org.codehaus.plexus + plexus-component-metadata + + + merge + + merge-metadata + + + + ${basedir}/src/main/resources/META-INF/plexus/components.xml + ${project.build.outputDirectory}/META-INF/plexus/components.xml + + + + + + + + diff --cc archiva-modules/archiva-scheduler/archiva-scheduler-repository/pom.xml index e92553661,000000000..7fa0d043f mode 100644,000000..100644 --- a/archiva-modules/archiva-scheduler/archiva-scheduler-repository/pom.xml +++ b/archiva-modules/archiva-scheduler/archiva-scheduler-repository/pom.xml @@@ -1,76 -1,0 +1,76 @@@ + + + 4.0.0 + + archiva-scheduler + org.apache.archiva - 1.3-SNAPSHOT ++ 1.4-SNAPSHOT + + archiva-scheduler-repository + Archiva Base :: Scheduled Tasks :: Repository Scanning + + + org.apache.archiva + archiva-scheduler-api + + + org.apache.archiva + repository-statistics + + + org.apache.archiva + archiva-configuration + + + org.apache.archiva + archiva-repository-scanner + + + org.apache.archiva + archiva-repository-layer + test + + + org.codehaus.plexus + plexus-quartz + + + org.slf4j + slf4j-api + + + org.codehaus.plexus + plexus-spring + test + + + org.slf4j + slf4j-simple + test + + + + + + org.codehaus.plexus + plexus-component-metadata + + + merge + process-resources + + merge-metadata + + + + ${basedir}/src/main/components-fragment.xml + ${project.build.outputDirectory}/META-INF/plexus/components.xml + + + + + + + + diff --cc archiva-modules/archiva-scheduler/pom.xml index 9da400013,000000000..079aa23a8 mode 100644,000000..100644 --- a/archiva-modules/archiva-scheduler/pom.xml +++ b/archiva-modules/archiva-scheduler/pom.xml @@@ -1,36 -1,0 +1,36 @@@ + + + + + + org.apache.archiva + archiva-modules - 1.3-SNAPSHOT ++ 1.4-SNAPSHOT + + 4.0.0 + archiva-scheduler + pom + Archiva Base :: Scheduled Tasks + + archiva-scheduler-api + archiva-scheduler-indexing + archiva-scheduler-repository + + diff --cc archiva-modules/archiva-web/archiva-webapp/pom.xml index b39989c85,bf8056ef7..9edf9a8ff --- a/archiva-modules/archiva-web/archiva-webapp/pom.xml +++ b/archiva-modules/archiva-web/archiva-webapp/pom.xml @@@ -35,11 -35,16 +35,15 @@@ org.apache.archiva - archiva-report-manager + problem-reports + + org.apache.archiva - archiva-artifact-reports - runtime ++ audit + org.apache.archiva - archiva-scheduled + archiva-scheduler-repository org.apache.archiva @@@ -63,9 -72,7 +67,8 @@@ org.apache.archiva - archiva-database-consumers + archiva-metadata-consumer - 1.3-SNAPSHOT + runtime org.apache.archiva @@@ -247,27 -254,6 +250,23 @@@ atlassian-xmlrpc-binder-server-spring runtime + + org.apache.archiva + metadata-model - 1.3-SNAPSHOT + + + org.apache.archiva + metadata-repository-api - 1.3-SNAPSHOT + + + org.apache.archiva + metadata-repository-file - 1.3-SNAPSHOT + runtime + + + org.apache.archiva + maven2-repository - 1.3-SNAPSHOT + diff --cc archiva-modules/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/PlexusActionSupport.java index 5a1d04da7,ad1d38875..9fc4d979c --- a/archiva-modules/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/PlexusActionSupport.java +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/PlexusActionSupport.java @@@ -89,7 -106,7 +89,7 @@@ public abstract class PlexusActionSuppo protected void triggerAuditEvent( String resource, String action ) { -- AuditEvent event = new AuditEvent( getPrincipal(), resource, action ); ++ AuditEvent event = new AuditEvent( null, getPrincipal(), resource, action ); event.setRemoteIP( getRemoteAddr() ); for ( AuditListener listener : auditListeners ) @@@ -100,7 -117,7 +100,7 @@@ protected void triggerAuditEvent( String action ) { -- AuditEvent event = new AuditEvent( getPrincipal(), action ); ++ AuditEvent event = new AuditEvent( null, getPrincipal(), null, action ); event.setRemoteIP( getRemoteAddr() ); for ( AuditListener listener : auditListeners ) diff --cc archiva-modules/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/UploadAction.java index d560c5a06,0796850c9..0919ee8a1 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/UploadAction.java +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/UploadAction.java @@@ -144,12 -146,14 +144,12 @@@ public class UploadActio private RepositoryContentFactory repositoryFactory; /** - * @plexus.requirement + * @plexus.requirement role="org.apache.archiva.scheduler.ArchivaTaskScheduler" role-hint="repository" */ private ArchivaTaskScheduler scheduler; - + private ChecksumAlgorithm[] algorithms = new ChecksumAlgorithm[]{ChecksumAlgorithm.SHA1, ChecksumAlgorithm.MD5}; - private ProjectModelWriter pomWriter = new ProjectModel400Writer(); - public void setArtifact( File file ) { this.artifactFile = file; diff --cc archiva-modules/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/reports/ViewAuditLogReportAction.java index 000000000,298a6d996..d2244cfa5 mode 000000,100644..100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/reports/ViewAuditLogReportAction.java +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/reports/ViewAuditLogReportAction.java @@@ -1,0 -1,433 +1,381 @@@ + package org.apache.maven.archiva.web.action.reports; + + /* + * 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 java.util.ArrayList; + import java.util.Calendar; + import java.util.Collections; + import java.util.Date; + import java.util.List; + + import javax.servlet.http.HttpServletRequest; + ++import org.apache.archiva.audit.AuditManager; + import org.apache.commons.lang.StringUtils; + import org.apache.commons.lang.time.DateUtils; -import org.apache.maven.archiva.database.ArchivaAuditLogsDao; -import org.apache.maven.archiva.database.ArchivaDAO; -import org.apache.maven.archiva.database.ArchivaDatabaseException; -import org.apache.maven.archiva.database.ObjectNotFoundException; -import org.apache.maven.archiva.database.SimpleConstraint; -import org.apache.maven.archiva.database.constraints.ArchivaAuditLogsConstraint; -import org.apache.maven.archiva.database.constraints.MostRecentArchivaAuditLogsConstraint; -import org.apache.maven.archiva.model.ArchivaAuditLogs; + import org.apache.maven.archiva.repository.audit.AuditEvent; + import org.apache.maven.archiva.security.AccessDeniedException; + import org.apache.maven.archiva.security.ArchivaSecurityException; + import org.apache.maven.archiva.security.PrincipalNotFoundException; + import org.apache.maven.archiva.security.UserRepositories; + import org.apache.maven.archiva.web.action.PlexusActionSupport; + import org.apache.struts2.interceptor.ServletRequestAware; + import org.codehaus.redback.integration.interceptor.SecureAction; + import org.codehaus.redback.integration.interceptor.SecureActionBundle; + import org.codehaus.redback.integration.interceptor.SecureActionException; + + import com.opensymphony.xwork2.Preparable; + + /** + * @plexus.component role="com.opensymphony.xwork2.Action" role-hint="viewAuditLogReport" + * instantiation-strategy="per-lookup" + */ + public class ViewAuditLogReportAction + extends PlexusActionSupport + implements SecureAction, ServletRequestAware, Preparable + { + protected HttpServletRequest request; + + /** + * @plexus.requirement + */ + private UserRepositories userRepositories; + - /** - * @plexus.requirement role-hint="jdo" - */ - private ArchivaAuditLogsDao auditLogsDao; - - /** - * @plexus.requirement role-hint="jdo" - */ - private ArchivaDAO dao; - + private String repository; + + private List repositories; + + private String groupId; + + private String artifactId; + + private String startDate; + + private String endDate; + + private int rowCount = 30; + + private int page = 1; + + private String prev; + + private String next; + + protected boolean isLastPage = true; + - private List auditLogs; ++ private List auditLogs; + + private static final String ALL_REPOSITORIES = "all"; + + protected int[] range = new int[2]; + + private String initial = "true"; + + private String headerName; + + private static final String HEADER_LATEST_EVENTS = "Latest Events"; + + private static final String HEADER_RESULTS = "Results"; + + private String[] datePatterns = new String[] { "MM/dd/yy", "MM/dd/yyyy", "MMMMM/dd/yyyy", "MMMMM/dd/yy", + "dd MMMMM yyyy", "dd/MM/yy", "dd/MM/yyyy", "yyyy/MM/dd", "yyyy-MM-dd", "yyyy-dd-MM", "MM-dd-yyyy", + "MM-dd-yy" }; + ++ /** ++ * @plexus.requirement ++ */ ++ private AuditManager auditManager; ++ + public SecureActionBundle getSecureActionBundle() + throws SecureActionException + { + return null; + } + + public void setServletRequest( HttpServletRequest request ) + { + this.request = request; + } + + @SuppressWarnings( "unchecked" ) + public void prepare() + throws Exception + { + repositories = new ArrayList(); + repositories.add( ALL_REPOSITORIES ); + repositories.addAll( getObservableRepositories() ); + + auditLogs = null; + groupId = ""; + artifactId = ""; + repository = ""; + + if( Boolean.parseBoolean( initial ) ) + { + headerName = HEADER_LATEST_EVENTS; + } + else + { + headerName = HEADER_RESULTS; + } + - SimpleConstraint constraint = new MostRecentArchivaAuditLogsConstraint(); - auditLogs = (List) dao.query( constraint ); ++ auditLogs = auditManager.getMostRecentAuditEvents(); + } + + public String execute() + throws Exception + { - auditLogs = null; - String artifact = ""; - - if ( groupId != null && !"".equals( groupId.trim() ) ) - { - artifact = groupId + ( ( artifactId != null && !"".equals( artifactId.trim() ) ) ? ( "/" + artifactId + "/%" ) : "%" ); - } - else - { - artifact = ( artifactId != null && !"".equals( artifactId.trim() ) ) ? ( "%" + artifactId + "%" ) : ""; - } - - Date startDateInDF = null; - Date endDateInDF = null; ++ Date startDateInDF; ++ Date endDateInDF; + if ( startDate == null || "".equals( startDate ) ) + { + Calendar cal = Calendar.getInstance(); + cal.set( Calendar.HOUR, 0 ); + cal.set( Calendar.MINUTE, 0 ); + cal.set( Calendar.SECOND, 0 ); + + startDateInDF = cal.getTime(); + } + else + { + startDateInDF = DateUtils.parseDate( startDate, datePatterns ); + } + + if ( endDate == null || "".equals( endDate ) ) + { + endDateInDF = Calendar.getInstance().getTime(); + } + else + { + endDateInDF = DateUtils.parseDate( endDate, datePatterns ); + Calendar cal = Calendar.getInstance(); + cal.setTime( endDateInDF ); + cal.set( Calendar.HOUR, 23 ); + cal.set( Calendar.MINUTE, 59 ); + cal.set( Calendar.SECOND, 59 ); + + endDateInDF = cal.getTime(); + } + + range[0] = ( page - 1 ) * rowCount; + range[1] = ( page * rowCount ) + 1; - - ArchivaAuditLogsConstraint constraint = null; - if ( !repository.equals( ALL_REPOSITORIES ) ) - { - constraint = - new ArchivaAuditLogsConstraint( range, artifact, repository, AuditEvent.UPLOAD_FILE, startDateInDF, endDateInDF ); - } - else - { - constraint = - new ArchivaAuditLogsConstraint( range, artifact, null, AuditEvent.UPLOAD_FILE, startDateInDF, endDateInDF ); - } + - try - { - auditLogs = auditLogsDao.queryAuditLogs( constraint ); - if( auditLogs.isEmpty() ) - { - addActionError( "No audit logs found." ); - initial = "true"; - } - else - { - initial = "false"; - } - - headerName = HEADER_RESULTS; - paginate(); - } - catch ( ObjectNotFoundException e ) ++ String repo = repository.equals( ALL_REPOSITORIES ) ? null : repository; ++ // TODO: query by artifact ++ auditLogs = auditManager.getAuditEventsInRange( repo, startDateInDF, endDateInDF ); ++ ++ if( auditLogs.isEmpty() ) + { + addActionError( "No audit logs found." ); - return ERROR; ++ initial = "true"; + } - catch ( ArchivaDatabaseException e ) ++ else + { - addActionError( "Error occurred while querying audit logs." ); - return ERROR; ++ initial = "false"; + } + ++ headerName = HEADER_RESULTS; ++ paginate(); ++ + return SUCCESS; + } + + private void paginate() + { + if ( auditLogs.size() <= rowCount ) + { + isLastPage = true; + } + else + { + isLastPage = false; + auditLogs.remove( rowCount ); + } + + prev = + request.getRequestURL() + "?page=" + ( page - 1 ) + "&rowCount=" + rowCount + "&groupId=" + groupId + + "&artifactId=" + artifactId + "&repository=" + repository + "&startDate=" + startDate + "&endDate=" + + endDate; + + next = + request.getRequestURL() + "?page=" + ( page + 1 ) + "&rowCount=" + rowCount + "&groupId=" + groupId + + "&artifactId=" + artifactId + "&repository=" + repository + "&startDate=" + startDate + "&endDate=" + + endDate; + + prev = StringUtils.replace( prev, " ", "%20" ); + next = StringUtils.replace( next, " ", "%20" ); + } + + private List getObservableRepositories() + { + try + { + return userRepositories.getObservableRepositoryIds( getPrincipal() ); + } + catch ( PrincipalNotFoundException e ) + { + log.warn( e.getMessage(), e ); + } + catch ( AccessDeniedException e ) + { + log.warn( e.getMessage(), e ); + } + catch ( ArchivaSecurityException e ) + { + log.warn( e.getMessage(), e ); + } + return Collections.emptyList(); + } + + public String getRepository() + { + return repository; + } + + public void setRepository( String repository ) + { + this.repository = repository; + } + + public List getRepositories() + { + return repositories; + } + + public void setRepositories( List repositories ) + { + this.repositories = repositories; + } + + public String getGroupId() + { + return groupId; + } + + public void setGroupId( String groupId ) + { + this.groupId = groupId; + } + + public String getArtifactId() + { + return artifactId; + } + + public void setArtifactId( String artifactId ) + { + this.artifactId = artifactId; + } + - public List getAuditLogs() ++ public List getAuditLogs() + { + return auditLogs; + } + - public void setAuditLogs( List auditLogs ) - { - this.auditLogs = auditLogs; - } - + public int getRowCount() + { + return rowCount; + } + + public void setRowCount( int rowCount ) + { + this.rowCount = rowCount; + } + + public String getStartDate() + { + return startDate; + } + + public void setStartDate( String startDate ) + { + this.startDate = startDate; + } + + public String getEndDate() + { + return endDate; + } + + public void setEndDate( String endDate ) + { + this.endDate = endDate; + } + + public int getPage() + { + return page; + } + + public void setPage( int page ) + { + this.page = page; + } + + public boolean getIsLastPage() + { + return isLastPage; + } + + public void setIsLastPage( boolean isLastPage ) + { + this.isLastPage = isLastPage; + } + + public String getPrev() + { + return prev; + } + + public void setPrev( String prev ) + { + this.prev = prev; + } + + public String getNext() + { + return next; + } + + public void setNext( String next ) + { + this.next = next; + } + + public String getInitial() + { + return initial; + } + + public void setInitial( String initial ) + { + this.initial = initial; + } + + public String getHeaderName() + { + return headerName; + } + + public void setHeaderName( String headerName ) + { + this.headerName = headerName; + } + } diff --cc archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/reports/auditLogReport.jsp index 000000000,664f8e7fa..02d029550 mode 000000,100644..100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/reports/auditLogReport.jsp +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/reports/auditLogReport.jsp @@@ -1,0 -1,152 +1,152 @@@ + <%-- + ~ 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. + --%> + + <%@ taglib prefix="s" uri="/struts-tags" %> + <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + <%@ taglib uri="http://www.extremecomponents.org" prefix="ec" %> + + + + + Audit Log Report + + - - - ++ ++ ++ + + + + +

Audit Log Report

+ +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Repository:
Group ID:
Artifact ID:
Start Date: + <%-- + + --%> +
End Date: + <%-- + + --%> +
Row Count:
+
+
+ +

+ +

+ +

${headerName}

+

+ +

+ + + + + + - ++ + + + + + + - ++ + - - - ++ ++ ++ + + +
EventRepositoryArtifactResourceEvent DateUsername
${auditLog.event}${auditLog.action}${auditLog.repositoryId}${auditLog.artifact}${auditLog.eventDate}${auditLog.username}${auditLog.resource}${auditLog.timestamp}${auditLog.userId}
+ + + << + Page: ${page} + + >> +
+
+ +
+ + +
+ + + diff --cc archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/reports/statisticsReport.jsp index 990b5833e,152cdec39..1db6f6b05 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/reports/statisticsReport.jsp +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/reports/statisticsReport.jsp @@@ -154,21 -154,33 +154,29 @@@ Archetypes Jars Wars + Ears + Exes + Dlls + Zips - Deployments - Downloads - + - ${stats.repositoryId} - ${stats.fileCount} - ${stats.totalSize} - ${stats.artifactCount} - ${stats.groupCount} - ${stats.projectCount} - ${stats.pluginCount} - ${stats.archetypeCount} - ${stats.jarCount} - ${stats.warCount} - ${stats.earCount} - ${stats.exeCount} - ${stats.dllCount} - ${stats.zipCount} - ${stats.deploymentCount} - ${stats.downloadCount} - + ${selectedRepositories[i.count-1]} + ${stats.totalFileCount} + ${stats.totalArtifactFileSize} + ${stats.totalArtifactCount} + ${stats.totalGroupCount} + ${stats.totalProjectCount} + ${stats.totalCountForType['maven-plugin']} + ${stats.totalCountForType['maven-archetype']} + ${stats.totalCountForType['jar']} + ${stats.totalCountForType['war']} ++ ${stats.totalCountForType['ear']} ++ ${stats.totalCountForType['exe']} ++ ${stats.totalCountForType['dll']} ++ ${stats.totalCountForType['zip']} + @@@ -187,21 -199,33 +195,29 @@@ Archetypes Jars Wars + Ears + Exes + Dlls + Zips - Deployments - Downloads - + - ${stats.dateOfScan} - ${stats.fileCount} - ${stats.totalSize} - ${stats.artifactCount} - ${stats.groupCount} - ${stats.projectCount} - ${stats.pluginCount} - ${stats.archetypeCount} - ${stats.jarCount} - ${stats.warCount} - ${stats.earCount} - ${stats.exeCount} - ${stats.dllCount} - ${stats.zipCount} - ${stats.deploymentCount} - ${stats.downloadCount} - + ${stats.scanStartTime} + ${stats.totalFileCount} + ${stats.totalArtifactFileSize} + ${stats.totalArtifactCount} + ${stats.totalGroupCount} + ${stats.totalProjectCount} + ${stats.totalCountForType['maven-plugin']} + ${stats.totalCountForType['maven-archetype']} + ${stats.totalCountForType['jar']} + ${stats.totalCountForType['war']} ++ ${stats.totalCountForType['ear']} ++ ${stats.totalCountForType['exe']} ++ ${stats.totalCountForType['dll']} ++ ${stats.totalCountForType['zip']} + diff --cc archiva-modules/archiva-web/archiva-webapp/src/main/webapp/css/site.css index ef9c51eac,996614608..9f9322a5f --- a/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/css/site.css +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/css/site.css @@@ -340,13 -437,44 +340,50 @@@ div.versions a.expand color: gray; } +#messages { + background-color: yellow; + border: 1px solid orange; + margin-top: 2em; +} + +#messages ul { + list-style-image: url(../images/icon_warning_sml.gif) +} + + table.auditlogs { + text-align: center; + font-family: Verdana, Geneva, Arial, Helvetica, sans-serif ; + font-weight: normal; + font-size: 11px; + color: #fff; + width: 100%; + background-color: #666; + border: 0px; + border-collapse: collapse; + border-spacing: 0px; + } + + table.auditlogs th { + background-color: #666; + color: #fff; + padding: 4px; + text-align: center; + border-bottom: 2px #fff solid; + font-size: 12px; + font-weight: bold; + } + + table.auditlogs td { + background-color: #CCC; + color: #000; + padding: 4px; + text-align: center; + border: 1px #fff solid; + } + + div.auditLogReportResults { + border: 1px dashed #DFDEDE; + margin-bottom: 15px; + margin-left: 2px; + padding: 5px; + } - - - - diff --cc archiva-modules/archiva-web/archiva-webapp/src/test/java/org/apache/maven/archiva/web/action/UploadActionTest.java index 9131af530,1af6eda88..8a98bd2d6 --- a/archiva-modules/archiva-web/archiva-webapp/src/test/java/org/apache/maven/archiva/web/action/UploadActionTest.java +++ b/archiva-modules/archiva-web/archiva-webapp/src/test/java/org/apache/maven/archiva/web/action/UploadActionTest.java @@@ -48,10 -51,10 +48,6 @@@ import org.easymock.classextension.Mock public class UploadActionTest extends PlexusInSpringTestCase { -- private ArchivaTaskScheduler scheduler; -- -- private MockControl schedulerControl; -- private UploadAction uploadAction; private ArchivaConfiguration archivaConfig; @@@ -61,25 -64,33 +57,25 @@@ private RepositoryContentFactory repoFactory; private MockControl repoFactoryControl; - + - private ArchivaAuditLogsDao auditLogsDao; - - private MockControl auditLogsDaoControl; - private static final String REPOSITORY_ID = "test-repo"; - private Configuration config; + private Configuration config; public void setUp() throws Exception { super.setUp(); -- schedulerControl = MockControl.createControl( ArchivaTaskScheduler.class ); -- scheduler = (ArchivaTaskScheduler) schedulerControl.getMock(); ++ MockControl schedulerControl = MockControl.createControl( ArchivaTaskScheduler.class ); ++ ArchivaTaskScheduler scheduler = (ArchivaTaskScheduler) schedulerControl.getMock(); archivaConfigControl = MockControl.createControl( ArchivaConfiguration.class ); archivaConfig = (ArchivaConfiguration) archivaConfigControl.getMock(); repoFactoryControl = MockClassControl.createControl( RepositoryContentFactory.class ); repoFactory = (RepositoryContentFactory) repoFactoryControl.getMock(); - + - auditLogsDaoControl = MockControl.createControl( ArchivaAuditLogsDao.class ); - auditLogsDaoControl.setDefaultMatcher( MockControl.ALWAYS_MATCHER ); - auditLogsDao = (ArchivaAuditLogsDao) auditLogsDaoControl.getMock(); - uploadAction = new UploadAction(); uploadAction.setScheduler( scheduler ); uploadAction.setConfiguration( archivaConfig ); @@@ -411,9 -435,11 +407,9 @@@ archivaConfigControl.expectAndReturn( archivaConfig.getConfiguration(), config ); repoFactoryControl.expectAndReturn( repoFactory.getManagedRepositoryContent( REPOSITORY_ID ), content ); - auditLogsDaoControl.expectAndReturn( auditLogsDao.saveAuditLogs( new ArchivaAuditLogs() ), null ); -- ++ archivaConfigControl.replay(); repoFactoryControl.replay(); - auditLogsDaoControl.replay(); String returnString = uploadAction.doUpload(); assertEquals( Action.SUCCESS, returnString ); @@@ -505,10 -540,12 +501,10 @@@ archivaConfigControl.expectAndReturn( archivaConfig.getConfiguration(), config, 2 ); repoFactoryControl.expectAndReturn( repoFactory.getManagedRepositoryContent( REPOSITORY_ID ), content, 2 ); - auditLogsDaoControl.expectAndReturn( auditLogsDao.saveAuditLogs( new ArchivaAuditLogs() ), null, 2 ); -- ++ archivaConfigControl.replay(); repoFactoryControl.replay(); - auditLogsDaoControl.replay(); -- ++ String returnString = uploadAction.doUpload(); assertEquals( Action.SUCCESS, returnString ); diff --cc archiva-modules/archiva-web/archiva-webapp/src/test/java/org/apache/maven/archiva/web/action/admin/repositories/AddManagedRepositoryActionTest.java index ef32ea249,7accb4b7c..61479d247 --- a/archiva-modules/archiva-web/archiva-webapp/src/test/java/org/apache/maven/archiva/web/action/admin/repositories/AddManagedRepositoryActionTest.java +++ b/archiva-modules/archiva-web/archiva-webapp/src/test/java/org/apache/maven/archiva/web/action/admin/repositories/AddManagedRepositoryActionTest.java @@@ -51,7 -53,11 +51,7 @@@ public class AddManagedRepositoryAction private MockControl archivaConfigurationControl; private ArchivaConfiguration archivaConfiguration; - + - private ArchivaAuditLogsDao auditLogsDao; - - private MockControl auditLogsDaoControl; - private static final String REPO_ID = "repo-ident"; private File location; diff --cc archiva-modules/archiva-web/archiva-webapp/src/test/java/org/apache/maven/archiva/web/action/admin/repositories/DeleteManagedRepositoryActionTest.java index d4f631679,ccd69f49f..a7bf0b97e --- a/archiva-modules/archiva-web/archiva-webapp/src/test/java/org/apache/maven/archiva/web/action/admin/repositories/DeleteManagedRepositoryActionTest.java +++ b/archiva-modules/archiva-web/archiva-webapp/src/test/java/org/apache/maven/archiva/web/action/admin/repositories/DeleteManagedRepositoryActionTest.java @@@ -79,7 -86,12 +79,7 @@@ public class DeleteManagedRepositoryAct archivaConfigurationControl = MockControl.createControl( ArchivaConfiguration.class ); archivaConfiguration = (ArchivaConfiguration) archivaConfigurationControl.getMock(); action.setArchivaConfiguration( archivaConfiguration ); - + - auditLogsDaoControl = MockControl.createControl( ArchivaAuditLogsDao.class ); - auditLogsDaoControl.setDefaultMatcher( MockControl.ALWAYS_MATCHER ); - auditLogsDao = (ArchivaAuditLogsDao) auditLogsDaoControl.getMock(); - action.setAuditLogsDao( auditLogsDao ); - roleManagerControl = MockControl.createControl( RoleManager.class ); roleManager = (RoleManager) roleManagerControl.getMock(); action.setRoleManager( roleManager ); @@@ -146,8 -144,13 +147,8 @@@ Configuration configuration = prepDeletionTest( createRepository(), 4 ); - auditLogsDaoControl.expectAndReturn( auditLogsDao.saveAuditLogs( new ArchivaAuditLogs() ), null ); - auditLogsDaoControl.replay(); - -- String status = action.deleteEntry(); ++ String status = action.deleteEntry(); - auditLogsDaoControl.verify(); - assertEquals( Action.SUCCESS, status ); assertTrue( configuration.getManagedRepositories().isEmpty() ); @@@ -167,8 -165,13 +168,8 @@@ Configuration configuration = prepDeletionTest( createRepository(), 4 ); - auditLogsDaoControl.expectAndReturn( auditLogsDao.saveAuditLogs( new ArchivaAuditLogs() ), null ); - auditLogsDaoControl.replay(); - String status = action.deleteContents(); - + - auditLogsDaoControl.verify(); - assertEquals( Action.SUCCESS, status ); assertTrue( configuration.getManagedRepositories().isEmpty() ); @@@ -193,7 -191,12 +194,8 @@@ assertEquals( 1, configuration.getProxyConnectors().size() ); - auditLogsDaoControl.expectAndReturn( auditLogsDao.saveAuditLogs( new ArchivaAuditLogs() ), null ); - auditLogsDaoControl.replay(); - String status = action.deleteContents(); + - auditLogsDaoControl.verify(); assertEquals( Action.SUCCESS, status ); assertTrue( configuration.getManagedRepositories().isEmpty() ); @@@ -207,10 -208,9 +209,11 @@@ public void testDeleteRepositoryCancelled() throws Exception { + repositoryStatisticsManagerControl.replay(); + ManagedRepositoryConfiguration originalRepository = createRepository(); Configuration configuration = prepDeletionTest( originalRepository, 3 ); + String status = action.execute(); assertEquals( Action.SUCCESS, status ); diff --cc archiva-modules/archiva-web/archiva-webapp/src/test/java/org/apache/maven/archiva/web/action/admin/repositories/EditManagedRepositoryActionTest.java index f9b4a7ae6,6f9bdf162..7c894ea32 --- a/archiva-modules/archiva-web/archiva-webapp/src/test/java/org/apache/maven/archiva/web/action/admin/repositories/EditManagedRepositoryActionTest.java +++ b/archiva-modules/archiva-web/archiva-webapp/src/test/java/org/apache/maven/archiva/web/action/admin/repositories/EditManagedRepositoryActionTest.java @@@ -140,10 -178,13 +140,10 @@@ public class EditManagedRepositoryActio ManagedRepositoryConfiguration repository = action.getRepository(); populateRepository( repository ); repository.setName( "new repo name" ); - + - auditLogsDaoControl.expectAndReturn( auditLogsDao.saveAuditLogs( new ArchivaAuditLogs() ), null ); - auditLogsDaoControl.replay(); - String status = action.commit(); assertEquals( Action.SUCCESS, status ); - + ManagedRepositoryConfiguration newRepository = createRepository(); newRepository.setName( "new repo name" ); assertRepositoryEquals( repository, newRepository ); diff --cc archiva-modules/archiva-web/archiva-webapp/src/test/resources/org/apache/maven/archiva/web/action/admin/connectors/proxy/EditProxyConnectorActionTest.xml index dc32d3f3d,a0b969b58..c01790ca8 --- a/archiva-modules/archiva-web/archiva-webapp/src/test/resources/org/apache/maven/archiva/web/action/admin/connectors/proxy/EditProxyConnectorActionTest.xml +++ b/archiva-modules/archiva-web/archiva-webapp/src/test/resources/org/apache/maven/archiva/web/action/admin/connectors/proxy/EditProxyConnectorActionTest.xml @@@ -38,7 -38,11 +38,7 @@@ 1800 - - - - org.apache.maven.archiva.database.ArchivaAuditLogsDao - jdo - org.apache.maven.archiva.web.action.admin.repositories.ArchivaAuditLogsDaoStub + + diff --cc archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/maven/archiva/webdav/ArchivaDavResource.java index eff87127e,ec4f1b97b..23727d0b4 --- a/archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/maven/archiva/webdav/ArchivaDavResource.java +++ b/archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/maven/archiva/webdav/ArchivaDavResource.java @@@ -403,9 -410,9 +403,15 @@@ public class ArchivaDavResourc private void triggerAuditEvent( DavResource member, String event ) throws DavException { String path = logicalResource + "/" + member.getDisplayName(); -- -- triggerAuditEvent( checkDavResourceIsArchivaDavResource( member ).remoteAddr, locator.getRepositoryId(), path, -- event ); ++ ++ ArchivaDavResource resource = checkDavResourceIsArchivaDavResource( member ); ++ AuditEvent auditEvent = new AuditEvent( locator.getRepositoryId(), resource.principal, path, event ); ++ auditEvent.setRemoteIP( resource.remoteAddr ); ++ ++ for ( AuditListener listener : auditListeners ) ++ { ++ listener.auditEvent( auditEvent ); ++ } } public void move( DavResource destination ) diff --cc archiva-modules/archiva-web/archiva-webdav/src/test/java/org/apache/maven/archiva/webdav/ArchivaDavResourceFactoryTest.java index b2f38fdb4,e5973b08c..0d30388bb --- a/archiva-modules/archiva-web/archiva-webdav/src/test/java/org/apache/maven/archiva/webdav/ArchivaDavResourceFactoryTest.java +++ b/archiva-modules/archiva-web/archiva-webdav/src/test/java/org/apache/maven/archiva/webdav/ArchivaDavResourceFactoryTest.java @@@ -81,7 -83,11 +81,7 @@@ public class ArchivaDavResourceFactoryT private MockControl repoContentFactoryControl; private RepositoryContentFactory repoFactory; - + - private ArchivaAuditLogsDao auditLogsDao; - - private MockControl auditLogsDaoControl; - public void setUp() throws Exception { @@@ -96,7 -102,11 +96,7 @@@ archivaConfigurationControl = MockControl.createControl( ArchivaConfiguration.class ); archivaConfiguration = (ArchivaConfiguration) archivaConfigurationControl.getMock(); - + - auditLogsDaoControl = MockControl.createControl( ArchivaAuditLogsDao.class ); - auditLogsDaoControl.setDefaultMatcher( MockControl.ALWAYS_MATCHER ); - auditLogsDao = (ArchivaAuditLogsDao) auditLogsDaoControl.getMock(); - config = new Configuration(); config.addManagedRepository( createManagedRepository( RELEASES_REPO, new File( getBasedir(), "target/test-classes/" + diff --cc archiva-modules/metadata/content-model.txt index 4d6565816,000000000..547af9ec8 mode 100644,000000..100644 --- a/archiva-modules/metadata/content-model.txt +++ b/archiva-modules/metadata/content-model.txt @@@ -1,208 -1,0 +1,220 @@@ +The following is the intended content model for the metadata content repository: + +. +`-- repositories/ + `-- central/ + |-- config/ + | |-- name= + | |-- storageUrl= + | `-- uri= + |-- content/ + | `-- org/ + | `-- apache/ + | |-- archiva/ + | | `-- platform/ + | | |-- scanner/ + | | | |-- 1.0-SNAPSHOT/ + | | | | |-- scanner-1.0-20091120.012345-1.pom/ + | | | | | |-- asc= + | | | | | |-- created= + | | | | | |-- fileCreated= + | | | | | |-- fileLastModified= + | | | | | |-- maven:buildNumber= + | | | | | |-- maven:classifier + | | | | | |-- maven:timestamp= + | | | | | |-- maven:type= + | | | | | |-- md5= + | | | | | |-- sha1= + | | | | | |-- size= + | | | | | |-- updated= + | | | | | `-- version= + | | | | |-- ciManagement.system= + | | | | |-- ciManagement.url= + | | | | |-- created= + | | | | |-- dependencies.0.artifactId= + | | | | |-- dependencies.0.classifier= + | | | | |-- dependencies.0.groupId= + | | | | |-- dependencies.0.optional= + | | | | |-- dependencies.0.scope= + | | | | |-- dependencies.0.systemPath= + | | | | |-- dependencies.0.type= + | | | | |-- dependencies.0.version= + | | | | |-- description= + | | | | |-- individuals.0.email= + | | | | |-- individuals.0.name= + | | | | |-- individuals.0.properties.scmId= + | | | | |-- individuals.0.roles.0= + | | | | |-- individuals.0.timezone= + | | | | |-- issueManagement.system= + | | | | |-- issueManagement.url= + | | | | |-- licenses.0.name= + | | | | |-- licenses.0.url= + | | | | |-- mailingLists.0.mainArchiveUrl= + | | | | |-- mailingLists.0.name= + | | | | |-- mailingLists.0.otherArchives.0= + | | | | |-- mailingLists.0.postAddress= + | | | | |-- mailingLists.0.subscribeAddress= + | | | | |-- mailingLists.0.unsubscribeAddress= + | | | | |-- maven:buildExtensions.0.artifactId= + | | | | |-- maven:buildExtensions.0.groupId= + | | | | |-- maven:buildExtensions.0.version= + | | | | |-- maven:packaging= + | | | | |-- maven:parent.artifactId= + | | | | |-- maven:parent.groupId= + | | | | |-- maven:parent.version= + | | | | |-- maven:plugins.0.artifactId= + | | | | |-- maven:plugins.0.groupId= + | | | | |-- maven:plugins.0.reporting= + | | | | |-- maven:plugins.0.version= + | | | | |-- maven:properties.mavenVersion= + | | | | |-- maven:repositories.0.id= + | | | | |-- maven:repositories.0.layout= + | | | | |-- maven:repositories.0.name= + | | | | |-- maven:repositories.0.plugins= + | | | | |-- maven:repositories.0.releases= + | | | | |-- maven:repositories.0.snapshots= + | | | | |-- maven:repositories.0.url= + | | | | |-- name= + | | | | |-- organization.favicon= + | | | | |-- organization.logo= + | | | | |-- organization.name= + | | | | |-- organization.url= + | | | | |-- relocatedTo.namespace= + | | | | |-- relocatedTo.project= + | | | | |-- relocatedTo.projectVersion= + | | | | |-- scm.connection= + | | | | |-- scm.developerConnection= + | | | | |-- scm.url= + | | | | |-- updated= + | | | | `-- url= + | | | `-- maven:artifactId= + | | `-- maven:groupId= + | `-- maven/ + | `-- plugins/ + | |-- maven:groupId= + | |-- maven:plugins.compiler.artifactId= + | `-- maven:plugins.compiler.name= + |-- facets/ ++ | |-- org.apache.archiva.audit/ ++ | | `-- 2010/ ++ | | `-- 01/ ++ | | `-- 19/ ++ | | `-- 093600.000/ ++ | | |-- action= ++ | | |-- artifact.id= ++ | | |-- artifact.namespace= ++ | | |-- artifact.projectId= ++ | | |-- artifact.version= ++ | | |-- remoteIP= ++ | | `-- user= + | |-- org.apache.archiva.metadata.repository.stats/ + | | `-- 2009/ + | | `-- 12/ + | | `-- 03/ + | | `-- 090000.000/ + | | |-- scanEndTime= + | | |-- scanStartTime= + | | |-- totalArtifactCount= + | | |-- totalArtifactFileSize= + | | |-- totalFileCount= + | | |-- totalGroupCount= + | | `-- totalProjectCount= + | `-- org.apache.archiva.reports/ + `-- references/ + `-- org/ + `-- apache/ + `-- archiva/ + |-- parent/ + | `-- 1/ + | `-- references/ + | `-- org/ + | `-- apache/ + | `-- archiva/ + | |-- platform/ + | | `-- scanner/ + | | `-- 1.0-SNAPSHOT/ + | | `-- referenceType=parent + | `-- web/ + | `-- webapp/ + | `-- 1.0-SNAPSHOT/ + | `-- referenceType=parent + `-- platform/ + `-- scanner/ + `-- 1.0-SNAPSHOT/ + `-- references/ + `-- org/ + `-- apache/ + `-- archiva/ + `-- web/ + `-- webapp/ + `-- 1.0-SNAPSHOT/ + `-- referenceType=dependency + +(To update - run "tree --dirsfirst -F" on the unpacked content-model.zip from the sandbox) + +Notes: + +*) config should be reflected to an external configuration file and only stored in the content repository for purposes + of accessing through a REST API, for example + +*) In the above example, we have the following coordinates: + - namespace = org.apache.archiva.platform (namespaces are of arbitrary depth, and are project namespaces, not to be + confused with JCR's item/node namespaces) + - project = scanner + - version = 1.0-SNAPSHOT + - artifact = scanner-1.0-20091120.012345-1.pom + +*) filename (scanner-1.0-20091120.012345-1.pom) is a node, and each is distinct except for checksums, etc. + +*) the top level version (1.0-SNAPSHOT) is the version best used to describe the project (the "marketed version"). It + must still be unique for lookup and comparing project versions to each other, but can contain several different + "build" artifacts. + +*) Projects are just a single code project. They do not have subprojects - if such modeling needs to be done, then we + can create a products tree that will map what "Archiva 1.0" contains from the other repositories. + +*) There is not Maven-native information here, other than that in the maven: namespace. pom & other files are not + treated as special - they are each stored and it is up to the reader to interpret + +*) artifact data is not stored in the metadata repository (there is no data= property on the file). The information here + is enough to locate the file in the original storageUrl when it is requested + +*) The API will still use separate namespace and project identifiers (the namespace can be null if there isn't one). + This is chosen to allow splitting the namespace on '.', and also allowing '.' in the project identifier without + splitting + +*) properties with '.' may be nested in other representations such as Java models or XML, if appropriate + +*) we only keep one set of project information for a "version" - this differs from Maven's storage of one POM per + snapshot. The Maven 2 module will take the latest. Those that need Maven's behaviour should retrieve the POM + directly. Implementations are also free to store as much information as desired within the artifact node in addition + to whatever is shared in the project version node. + +*) while some information is stored at the most generic level in the metadata repository (eg maven:groupId, + maven:artifactId), for convenience when loaded by the implementation it may all be pushed into the projectVersion's + information. The metadata repository implementation can decide how best to store and retrieve the information. + +*) created/updated timestamps may be maintained by the metadata repository implementation for the metadata itself. + Timestamps for individual files are stored as additional properties (fileCreated, fileLastModified). It may make + sense to add a "discovered" timestamp if an artifact is known to be created at a different time to which it is added + to the metadata repository. + +*) references are stored outside the main model so that their creation doesn't imply a "stub" model - we know if the + project exists whether a reference is created or not. References need not infer referential integrity. + +*) some of the above needs to be reviewed before going into production. For example: + - the maven specific aspects of dependencies should become a faceted part of the content + - more of the metadata might be faceted in general, keeping the content model basic by default + - determine if any of the stats can be derived by functions of the content repository rather than storing and trying + to keep them up to date. Historical data might be retained by versioning and taking a snapshot at a given point in + time. The current approach of tying them to the scanning process is not optimal + - the storing of metadata as 0-indexed lists would be better in as child nodes. This might require additional levels + in the current repository (.../scanner/versions/1.0-SNAPSHOT/artifacts/scanner-1.0-20091120.012345-1.pom), or + for listed information to be in a separate tree + (/metadata/org/apache/archiva/platform/scanner/1.0-SNAPSHOT/mailingLists/users), or to use some 'reserved names' + for nodes (by using a content repository's namespacing capabilities). The first has the advantage of + keeping information together but a longer path name and less familiarity to Maven users. The second arbitrarily + divides metadata. The third option seems preferable but needs more investigation at this stage. + +*) Future possibilities: + - audit metadata on artifacts (who uploaded, when, and how), or whether it was discovered by scanning diff --cc archiva-modules/metadata/metadata-model/pom.xml index ccd593426,000000000..65226f899 mode 100644,000000..100644 --- a/archiva-modules/metadata/metadata-model/pom.xml +++ b/archiva-modules/metadata/metadata-model/pom.xml @@@ -1,28 -1,0 +1,28 @@@ + + + 4.0.0 + + metadata + org.apache.archiva - 1.3-SNAPSHOT ++ 1.4-SNAPSHOT + + metadata-model + Archiva Metadata Model + diff --cc archiva-modules/metadata/metadata-model/src/main/java/org/apache/archiva/metadata/model/MetadataFacetFactory.java index b9e313a80,000000000..e0804edc1 mode 100644,000000..100644 --- a/archiva-modules/metadata/metadata-model/src/main/java/org/apache/archiva/metadata/model/MetadataFacetFactory.java +++ b/archiva-modules/metadata/metadata-model/src/main/java/org/apache/archiva/metadata/model/MetadataFacetFactory.java @@@ -1,25 -1,0 +1,27 @@@ +package org.apache.archiva.metadata.model; + +/* + * 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. + */ + +public interface MetadataFacetFactory +{ + MetadataFacet createMetadataFacet(); ++ ++ MetadataFacet createMetadataFacet( String repositoryId, String name ); +} diff --cc archiva-modules/metadata/metadata-repository-api/pom.xml index 0f36161fc,000000000..c98dd2a00 mode 100644,000000..100644 --- a/archiva-modules/metadata/metadata-repository-api/pom.xml +++ b/archiva-modules/metadata/metadata-repository-api/pom.xml @@@ -1,38 -1,0 +1,38 @@@ + + + 4.0.0 + + metadata + org.apache.archiva - 1.3-SNAPSHOT ++ 1.4-SNAPSHOT + + metadata-repository-api + Archiva Metadata Repository API + + + org.apache.archiva + metadata-model + + + org.slf4j + slf4j-api + + + diff --cc archiva-modules/metadata/pom.xml index 3e7159b5a,000000000..646612a45 mode 100644,000000..100644 --- a/archiva-modules/metadata/pom.xml +++ b/archiva-modules/metadata/pom.xml @@@ -1,33 -1,0 +1,33 @@@ + + + 4.0.0 + + archiva-modules + org.apache.archiva - 1.3-SNAPSHOT ++ 1.4-SNAPSHOT + + metadata + Archiva Metadata + pom + + metadata-model + metadata-repository-api + + diff --cc archiva-modules/plugins/audit/pom.xml index 000000000,000000000..276905786 new file mode 100644 --- /dev/null +++ b/archiva-modules/plugins/audit/pom.xml @@@ -1,0 -1,0 +1,45 @@@ ++ ++ ++ ++ 4.0.0 ++ ++ plugins ++ org.apache.archiva ++ 1.4-SNAPSHOT ++ ++ audit ++ Audit Logging ++ ++ ++ org.apache.archiva ++ archiva-repository-layer ++ ++ ++ org.apache.archiva ++ metadata-repository-api ++ ++ ++ org.slf4j ++ slf4j-simple ++ test ++ ++ ++ diff --cc archiva-modules/plugins/audit/src/main/java/org/apache/archiva/audit/AuditEventFactory.java index 000000000,000000000..9573d5509 new file mode 100644 --- /dev/null +++ b/archiva-modules/plugins/audit/src/main/java/org/apache/archiva/audit/AuditEventFactory.java @@@ -1,0 -1,0 +1,41 @@@ ++package org.apache.archiva.audit; ++ ++/* ++ * 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 org.apache.archiva.metadata.model.MetadataFacet; ++import org.apache.archiva.metadata.model.MetadataFacetFactory; ++import org.apache.maven.archiva.repository.audit.AuditEvent; ++ ++/** ++ * @plexus.component role="org.apache.archiva.metadata.model.MetadataFacetFactory" role-hint="org.apache.archiva.audit" ++ */ ++public class AuditEventFactory ++ implements MetadataFacetFactory ++{ ++ public MetadataFacet createMetadataFacet() ++ { ++ throw new UnsupportedOperationException( "Must construct an audit event with a name" ); ++ } ++ ++ public MetadataFacet createMetadataFacet( String repositoryId, String name ) ++ { ++ return new AuditEvent( name, repositoryId ); ++ } ++} diff --cc archiva-modules/plugins/audit/src/main/java/org/apache/archiva/audit/AuditManager.java index 000000000,000000000..addb9c0e3 new file mode 100644 --- /dev/null +++ b/archiva-modules/plugins/audit/src/main/java/org/apache/archiva/audit/AuditManager.java @@@ -1,0 -1,0 +1,36 @@@ ++package org.apache.archiva.audit; ++ ++/* ++ * 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 java.util.Date; ++import java.util.List; ++ ++import org.apache.maven.archiva.repository.audit.AuditEvent; ++ ++public interface AuditManager ++{ ++ List getMostRecentAuditEvents(); ++ ++ void addAuditEvent( AuditEvent event ); ++ ++ void deleteAuditEvents( String repositoryId ); ++ ++ List getAuditEventsInRange( String repositoryId, Date startTime, Date endTime ); ++} diff --cc archiva-modules/plugins/audit/src/main/java/org/apache/archiva/audit/DefaultAuditManager.java index 000000000,000000000..e8161f982 new file mode 100644 --- /dev/null +++ b/archiva-modules/plugins/audit/src/main/java/org/apache/archiva/audit/DefaultAuditManager.java @@@ -1,0 -1,0 +1,154 @@@ ++package org.apache.archiva.audit; ++ ++/* ++ * 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 java.text.ParseException; ++import java.text.SimpleDateFormat; ++import java.util.ArrayList; ++import java.util.Collection; ++import java.util.Collections; ++import java.util.Comparator; ++import java.util.Date; ++import java.util.List; ++ ++import org.apache.archiva.metadata.repository.MetadataRepository; ++import org.apache.maven.archiva.repository.audit.AuditEvent; ++import org.slf4j.Logger; ++import org.slf4j.LoggerFactory; ++ ++/** ++ * @plexus.component role="org.apache.archiva.audit.AuditManager" ++ */ ++public class DefaultAuditManager ++ implements AuditManager ++{ ++ /** ++ * @plexus.requirement ++ */ ++ private MetadataRepository metadataRepository; ++ ++ private static final int NUM_RECENT_REVENTS = 10; ++ ++ private static final Logger log = LoggerFactory.getLogger( DefaultAuditManager.class ); ++ ++ public List getMostRecentAuditEvents() ++ { ++ // TODO: consider a more efficient implementation that directly gets the last ten from the content repository ++ List records = new ArrayList(); ++ for ( String repositoryId : metadataRepository.getRepositories() ) ++ { ++ List timestamps = metadataRepository.getMetadataFacets( repositoryId, AuditEvent.FACET_ID ); ++ for ( String timestamp : timestamps ) ++ { ++ records.add( new AuditRecord( repositoryId, timestamp ) ); ++ } ++ } ++ Collections.sort( records ); ++ records = records.subList( 0, records.size() < NUM_RECENT_REVENTS ? records.size() : NUM_RECENT_REVENTS ); ++ ++ List events = new ArrayList( records.size() ); ++ for ( AuditRecord record : records ) ++ { ++ AuditEvent auditEvent = ++ (AuditEvent) metadataRepository.getMetadataFacet( record.repositoryId, AuditEvent.FACET_ID, ++ record.name ); ++ events.add( auditEvent ); ++ } ++ return events; ++ } ++ ++ public void addAuditEvent( AuditEvent event ) ++ { ++ // ignore those with no repository - they will still be logged to the textual audit log ++ if ( event.getRepositoryId() != null ) ++ { ++ metadataRepository.addMetadataFacet( event.getRepositoryId(), event ); ++ } ++ } ++ ++ public void deleteAuditEvents( String repositoryId ) ++ { ++ metadataRepository.removeMetadataFacets( repositoryId, AuditEvent.FACET_ID ); ++ } ++ ++ public List getAuditEventsInRange( String repoId, Date startTime, Date endTime ) ++ { ++ Collection repositoryIds = ++ repoId != null ? Collections.singletonList( repoId ) : metadataRepository.getRepositories(); ++ ++ List results = new ArrayList(); ++ for ( String repositoryId : repositoryIds ) ++ { ++ List list = metadataRepository.getMetadataFacets( repositoryId, AuditEvent.FACET_ID ); ++ for ( String name : list ) ++ { ++ try ++ { ++ Date date = new SimpleDateFormat( AuditEvent.TIMESTAMP_FORMAT ).parse( name ); ++ if ( ( startTime == null || !date.before( startTime ) ) && ++ ( endTime == null || !date.after( endTime ) ) ) ++ { ++ AuditEvent event = ++ (AuditEvent) metadataRepository.getMetadataFacet( repositoryId, AuditEvent.FACET_ID, name ); ++ results.add( event ); ++ } ++ } ++ catch ( ParseException e ) ++ { ++ log.error( "Invalid audit event found in the metadata repository: " + e.getMessage() ); ++ // continue and ignore this one ++ } ++ } ++ } ++ Collections.sort( results, new Comparator() ++ { ++ public int compare( AuditEvent o1, AuditEvent o2 ) ++ { ++ return o2.getTimestamp().compareTo( o1.getTimestamp() ); ++ } ++ } ); ++ return results; ++ } ++ ++ public void setMetadataRepository( MetadataRepository metadataRepository ) ++ { ++ this.metadataRepository = metadataRepository; ++ } ++ ++ private static final class AuditRecord ++ implements Comparable ++ { ++ private String repositoryId; ++ ++ private String name; ++ ++ public AuditRecord( String repositoryId, String name ) ++ { ++ this.repositoryId = repositoryId; ++ this.name = name; ++ } ++ ++ public int compareTo( AuditRecord other ) ++ { ++ // reverse ordering ++ return other.name.compareTo( name ); ++ } ++ } ++} diff --cc archiva-modules/plugins/audit/src/main/java/org/apache/archiva/audit/MetadataAuditListener.java index 000000000,000000000..612eb51af new file mode 100644 --- /dev/null +++ b/archiva-modules/plugins/audit/src/main/java/org/apache/archiva/audit/MetadataAuditListener.java @@@ -1,0 -1,0 +1,44 @@@ ++package org.apache.archiva.audit; ++ ++/* ++ * 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 org.apache.maven.archiva.repository.audit.AuditEvent; ++import org.apache.maven.archiva.repository.audit.AuditListener; ++ ++/** ++ * @plexus.component role="org.apache.maven.archiva.repository.audit.AuditListener" role-hint="metadata" ++ */ ++public class MetadataAuditListener ++ implements AuditListener ++{ ++ /** ++ * @plexus.requirement ++ */ ++ private AuditManager auditManager; ++ ++ 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 ) ) ++ { ++ auditManager.addAuditEvent( event ); ++ } ++ } ++} diff --cc archiva-modules/plugins/audit/src/test/java/org/apache/archiva/audit/AuditManagerTest.java index 000000000,000000000..a384a83dc new file mode 100644 --- /dev/null +++ b/archiva-modules/plugins/audit/src/test/java/org/apache/archiva/audit/AuditManagerTest.java @@@ -1,0 -1,0 +1,507 @@@ ++package org.apache.archiva.audit; ++ ++/* ++ * 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 java.text.DecimalFormat; ++import java.text.ParseException; ++import java.text.SimpleDateFormat; ++import java.util.ArrayList; ++import java.util.Arrays; ++import java.util.Collections; ++import java.util.Date; ++import java.util.LinkedHashMap; ++import java.util.List; ++import java.util.Map; ++ ++import junit.framework.TestCase; ++import org.apache.archiva.metadata.repository.MetadataRepository; ++import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration; ++import org.apache.maven.archiva.repository.RepositoryContentFactory; ++import org.apache.maven.archiva.repository.audit.AuditEvent; ++import org.apache.maven.archiva.repository.content.ManagedDefaultRepositoryContent; ++import org.easymock.MockControl; ++import org.easymock.classextension.MockClassControl; ++ ++public class AuditManagerTest ++ extends TestCase ++{ ++ private DefaultAuditManager auditManager; ++ ++ private MockControl metadataRepositoryControl; ++ ++ private MetadataRepository metadataRepository; ++ ++ private static final String AUDIT_EVENT_BASE = "2010/01/18/123456."; ++ ++ private static final String TEST_REPO_ID = "test-repo"; ++ ++ private static final String TEST_REPO_ID_2 = "repo2"; ++ ++ private static final String TEST_USER = "test_user"; ++ ++ private static final String TEST_RESOURCE_BASE = "test/resource"; ++ ++ private static final String TEST_IP_ADDRESS = "127.0.0.1"; ++ ++ private static final SimpleDateFormat TIMESTAMP_FORMAT = new SimpleDateFormat( AuditEvent.TIMESTAMP_FORMAT ); ++ ++ private static final DecimalFormat MILLIS_FORMAT = new DecimalFormat( "000" ); ++ ++ @Override ++ protected void setUp() ++ throws Exception ++ { ++ super.setUp(); ++ ++ auditManager = new DefaultAuditManager(); ++ ++ metadataRepositoryControl = MockControl.createControl( MetadataRepository.class ); ++ metadataRepository = (MetadataRepository) metadataRepositoryControl.getMock(); ++ auditManager.setMetadataRepository( metadataRepository ); ++ ++ ManagedRepositoryConfiguration repository = new ManagedRepositoryConfiguration(); ++ repository.setId( TEST_REPO_ID ); ++ repository.setLocation( "" ); ++ ManagedDefaultRepositoryContent content = new ManagedDefaultRepositoryContent(); ++ content.setRepository( repository ); ++ MockControl control = MockClassControl.createControl( RepositoryContentFactory.class ); ++ RepositoryContentFactory contentFactory = (RepositoryContentFactory) control.getMock(); ++ contentFactory.getManagedRepositoryContent( TEST_REPO_ID ); ++ control.setDefaultReturnValue( content ); ++ control.replay(); ++ } ++ ++ public void testGetMostRecentEvents() ++ throws ParseException ++ { ++ metadataRepositoryControl.expectAndReturn( metadataRepository.getRepositories(), ++ Collections.singletonList( TEST_REPO_ID ) ); ++ ++ int numEvents = 11; ++ List eventNames = new ArrayList( numEvents ); ++ for ( int i = 0; i < numEvents; i++ ) ++ { ++ eventNames.add( AUDIT_EVENT_BASE + MILLIS_FORMAT.format( i ) ); ++ } ++ ++ metadataRepositoryControl.expectAndReturn( ++ metadataRepository.getMetadataFacets( TEST_REPO_ID, AuditEvent.FACET_ID ), eventNames ); ++ ++ for ( String name : eventNames.subList( 1, eventNames.size() ) ) ++ { ++ AuditEvent event = createTestEvent( name ); ++ ++ metadataRepositoryControl.expectAndReturn( ++ metadataRepository.getMetadataFacet( TEST_REPO_ID, AuditEvent.FACET_ID, name ), event ); ++ } ++ metadataRepositoryControl.replay(); ++ ++ List events = auditManager.getMostRecentAuditEvents(); ++ assertNotNull( events ); ++ assertEquals( numEvents - 1, events.size() ); ++ int expectedTimestampCounter = numEvents - 1; ++ for ( AuditEvent event : events ) ++ { ++ String num = MILLIS_FORMAT.format( expectedTimestampCounter ); ++ assertEvent( event, AUDIT_EVENT_BASE + num, TEST_RESOURCE_BASE + "/" + num ); ++ expectedTimestampCounter--; ++ } ++ ++ metadataRepositoryControl.verify(); ++ } ++ ++ private static AuditEvent createTestEvent( String name ) ++ throws ParseException ++ { ++ return createTestEvent( TEST_REPO_ID, name ); ++ } ++ ++ private static AuditEvent createTestEvent( String repositoryId, String name ) ++ throws ParseException ++ { ++ AuditEvent event = new AuditEvent(); ++ event.setTimestamp( TIMESTAMP_FORMAT.parse( name ) ); ++ event.setAction( AuditEvent.UPLOAD_FILE ); ++ event.setRemoteIP( TEST_IP_ADDRESS ); ++ event.setRepositoryId( repositoryId ); ++ event.setUserId( TEST_USER ); ++ event.setResource( TEST_RESOURCE_BASE + "/" + name.substring( AUDIT_EVENT_BASE.length() ) ); ++ return event; ++ } ++ ++ public void testGetMostRecentEventsLessThan10() ++ throws ParseException ++ { ++ metadataRepositoryControl.expectAndReturn( metadataRepository.getRepositories(), ++ Collections.singletonList( TEST_REPO_ID ) ); ++ int numEvents = 5; ++ List eventNames = new ArrayList( numEvents ); ++ for ( int i = 0; i < numEvents; i++ ) ++ { ++ eventNames.add( AUDIT_EVENT_BASE + MILLIS_FORMAT.format( i ) ); ++ } ++ ++ metadataRepositoryControl.expectAndReturn( ++ metadataRepository.getMetadataFacets( TEST_REPO_ID, AuditEvent.FACET_ID ), eventNames ); ++ ++ for ( String name : eventNames ) ++ { ++ AuditEvent event = createTestEvent( name ); ++ ++ metadataRepositoryControl.expectAndReturn( ++ metadataRepository.getMetadataFacet( TEST_REPO_ID, AuditEvent.FACET_ID, name ), event ); ++ } ++ metadataRepositoryControl.replay(); ++ ++ List events = auditManager.getMostRecentAuditEvents(); ++ assertNotNull( events ); ++ assertEquals( numEvents, events.size() ); ++ int expectedTimestampCounter = numEvents - 1; ++ for ( AuditEvent event : events ) ++ { ++ String num = MILLIS_FORMAT.format( expectedTimestampCounter ); ++ assertEvent( event, AUDIT_EVENT_BASE + num, TEST_RESOURCE_BASE + "/" + num ); ++ expectedTimestampCounter--; ++ } ++ ++ metadataRepositoryControl.verify(); ++ } ++ ++ public void testGetMostRecentEventsInterleavedRepositories() ++ throws ParseException ++ { ++ metadataRepositoryControl.expectAndReturn( metadataRepository.getRepositories(), ++ Arrays.asList( TEST_REPO_ID, TEST_REPO_ID_2 ) ); ++ int numEvents = 11; ++ Map> eventNames = new LinkedHashMap>(); ++ List events = new ArrayList(); ++ eventNames.put( TEST_REPO_ID, new ArrayList() ); ++ eventNames.put( TEST_REPO_ID_2, new ArrayList() ); ++ for ( int i = 0; i < numEvents; i++ ) ++ { ++ String name = AUDIT_EVENT_BASE + MILLIS_FORMAT.format( i ); ++ String repositoryId = i % 2 == 0 ? TEST_REPO_ID : TEST_REPO_ID_2; ++ eventNames.get( repositoryId ).add( name ); ++ events.add( createTestEvent( repositoryId, name ) ); ++ } ++ ++ metadataRepositoryControl.expectAndReturn( ++ metadataRepository.getMetadataFacets( TEST_REPO_ID, AuditEvent.FACET_ID ), eventNames.get( TEST_REPO_ID ) ); ++ metadataRepositoryControl.expectAndReturn( ++ metadataRepository.getMetadataFacets( TEST_REPO_ID_2, AuditEvent.FACET_ID ), ++ eventNames.get( TEST_REPO_ID_2 ) ); ++ ++ for ( AuditEvent event : events.subList( 1, events.size() ) ) ++ { ++ metadataRepositoryControl.expectAndReturn( ++ metadataRepository.getMetadataFacet( event.getRepositoryId(), AuditEvent.FACET_ID, event.getName() ), ++ event ); ++ } ++ metadataRepositoryControl.replay(); ++ ++ events = auditManager.getMostRecentAuditEvents(); ++ assertNotNull( events ); ++ assertEquals( numEvents - 1, events.size() ); ++ int expectedTimestampCounter = numEvents - 1; ++ for ( AuditEvent event : events ) ++ { ++ String num = MILLIS_FORMAT.format( expectedTimestampCounter ); ++ assertEvent( event, AUDIT_EVENT_BASE + num, TEST_RESOURCE_BASE + "/" + num, ++ expectedTimestampCounter % 2 == 0 ? TEST_REPO_ID : TEST_REPO_ID_2 ); ++ expectedTimestampCounter--; ++ } ++ ++ metadataRepositoryControl.verify(); ++ } ++ ++ private static void assertEvent( AuditEvent event, String name, String resource ) ++ { ++ assertEvent( event, name, resource, TEST_REPO_ID ); ++ } ++ ++ private static void assertEvent( AuditEvent event, String name, String resource, String repositoryId ) ++ { ++ assertEquals( name, TIMESTAMP_FORMAT.format( event.getTimestamp() ) ); ++ assertEquals( AuditEvent.UPLOAD_FILE, event.getAction() ); ++ assertEquals( TEST_IP_ADDRESS, event.getRemoteIP() ); ++ assertEquals( repositoryId, event.getRepositoryId() ); ++ assertEquals( TEST_USER, event.getUserId() ); ++ assertEquals( resource, event.getResource() ); ++ } ++ ++ public void testGetMostRecentEventsWhenEmpty() ++ { ++ metadataRepositoryControl.expectAndReturn( metadataRepository.getRepositories(), ++ Collections.singletonList( TEST_REPO_ID ) ); ++ ++ metadataRepositoryControl.expectAndReturn( ++ metadataRepository.getMetadataFacets( TEST_REPO_ID, AuditEvent.FACET_ID ), Collections.emptyList() ); ++ metadataRepositoryControl.replay(); ++ ++ assertTrue( auditManager.getMostRecentAuditEvents().isEmpty() ); ++ ++ metadataRepositoryControl.verify(); ++ } ++ ++ public void testAddAuditEvent() ++ throws ParseException ++ { ++ String name = TIMESTAMP_FORMAT.format( new Date() ); ++ AuditEvent event = createTestEvent( name ); ++ ++ metadataRepository.addMetadataFacet( TEST_REPO_ID, event ); ++ ++ metadataRepositoryControl.replay(); ++ ++ auditManager.addAuditEvent( event ); ++ ++ metadataRepositoryControl.verify(); ++ } ++ ++ public void testAddAuditEventNoRepositoryId() ++ throws ParseException ++ { ++ String name = TIMESTAMP_FORMAT.format( new Date() ); ++ AuditEvent event = createTestEvent( null, name ); ++ ++ // should just be ignored ++ ++ metadataRepositoryControl.replay(); ++ ++ auditManager.addAuditEvent( event ); ++ ++ metadataRepositoryControl.verify(); ++ } ++ ++ public void testDeleteStats() ++ { ++ metadataRepository.removeMetadataFacets( TEST_REPO_ID, AuditEvent.FACET_ID ); ++ ++ metadataRepositoryControl.replay(); ++ ++ auditManager.deleteAuditEvents( TEST_REPO_ID ); ++ ++ metadataRepositoryControl.verify(); ++ } ++ ++ public void testGetEventsRangeInside() ++ throws ParseException ++ { ++ Date current = new Date(); ++ ++ String name1 = TIMESTAMP_FORMAT.format( new Date( current.getTime() - 12345 ) ); ++ Date expectedTimestamp = new Date( current.getTime() - 3000 ); ++ String name2 = TIMESTAMP_FORMAT.format( expectedTimestamp ); ++ AuditEvent expectedEvent = createTestEvent( name2 ); ++ String name3 = TIMESTAMP_FORMAT.format( new Date( current.getTime() - 1000 ) ); ++ ++ metadataRepositoryControl.expectAndReturn( ++ metadataRepository.getMetadataFacets( TEST_REPO_ID, AuditEvent.FACET_ID ), ++ Arrays.asList( name1, name2, name3 ) ); ++ ++ // only match the middle one ++ metadataRepositoryControl.expectAndReturn( ++ metadataRepository.getMetadataFacet( TEST_REPO_ID, AuditEvent.FACET_ID, name2 ), expectedEvent ); ++ ++ metadataRepositoryControl.replay(); ++ ++ List events = ++ auditManager.getAuditEventsInRange( TEST_REPO_ID, new Date( current.getTime() - 4000 ), ++ new Date( current.getTime() - 2000 ) ); ++ ++ assertEquals( 1, events.size() ); ++ assertEvent( events.get( 0 ), name2, expectedEvent.getResource() ); ++ ++ metadataRepositoryControl.verify(); ++ } ++ ++ public void testGetEventsRangeUpperOutside() ++ throws ParseException ++ { ++ Date current = new Date(); ++ ++ String name1 = TIMESTAMP_FORMAT.format( new Date( current.getTime() - 12345 ) ); ++ Date expectedTimestamp = new Date( current.getTime() - 3000 ); ++ String name2 = TIMESTAMP_FORMAT.format( expectedTimestamp ); ++ AuditEvent expectedEvent2 = createTestEvent( name2 ); ++ String name3 = TIMESTAMP_FORMAT.format( new Date( current.getTime() - 1000 ) ); ++ AuditEvent expectedEvent3 = createTestEvent( name3 ); ++ ++ metadataRepositoryControl.expectAndReturn( ++ metadataRepository.getMetadataFacets( TEST_REPO_ID, AuditEvent.FACET_ID ), ++ Arrays.asList( name1, name2, name3 ) ); ++ ++ metadataRepositoryControl.expectAndReturn( ++ metadataRepository.getMetadataFacet( TEST_REPO_ID, AuditEvent.FACET_ID, name2 ), expectedEvent2 ); ++ metadataRepositoryControl.expectAndReturn( ++ metadataRepository.getMetadataFacet( TEST_REPO_ID, AuditEvent.FACET_ID, name3 ), expectedEvent3 ); ++ ++ metadataRepositoryControl.replay(); ++ ++ List events = ++ auditManager.getAuditEventsInRange( TEST_REPO_ID, new Date( current.getTime() - 4000 ), current ); ++ ++ assertEquals( 2, events.size() ); ++ assertEvent( events.get( 0 ), name3, expectedEvent3.getResource() ); ++ assertEvent( events.get( 1 ), name2, expectedEvent2.getResource() ); ++ ++ metadataRepositoryControl.verify(); ++ } ++ ++ public void testGetEventsRangeLowerOutside() ++ throws ParseException ++ { ++ Date current = new Date(); ++ ++ String name1 = TIMESTAMP_FORMAT.format( new Date( current.getTime() - 12345 ) ); ++ AuditEvent expectedEvent1 = createTestEvent( name1 ); ++ Date expectedTimestamp = new Date( current.getTime() - 3000 ); ++ String name2 = TIMESTAMP_FORMAT.format( expectedTimestamp ); ++ AuditEvent expectedEvent2 = createTestEvent( name2 ); ++ String name3 = TIMESTAMP_FORMAT.format( new Date( current.getTime() - 1000 ) ); ++ ++ metadataRepositoryControl.expectAndReturn( ++ metadataRepository.getMetadataFacets( TEST_REPO_ID, AuditEvent.FACET_ID ), ++ Arrays.asList( name1, name2, name3 ) ); ++ ++ metadataRepositoryControl.expectAndReturn( ++ metadataRepository.getMetadataFacet( TEST_REPO_ID, AuditEvent.FACET_ID, name1 ), expectedEvent1 ); ++ metadataRepositoryControl.expectAndReturn( ++ metadataRepository.getMetadataFacet( TEST_REPO_ID, AuditEvent.FACET_ID, name2 ), expectedEvent2 ); ++ ++ metadataRepositoryControl.replay(); ++ ++ List events = ++ auditManager.getAuditEventsInRange( TEST_REPO_ID, new Date( current.getTime() - 20000 ), ++ new Date( current.getTime() - 2000 ) ); ++ ++ assertEquals( 2, events.size() ); ++ assertEvent( events.get( 0 ), name2, expectedEvent2.getResource() ); ++ assertEvent( events.get( 1 ), name1, expectedEvent1.getResource() ); ++ ++ metadataRepositoryControl.verify(); ++ } ++ ++ public void testGetEventsRangeLowerAndUpperOutside() ++ throws ParseException ++ { ++ Date current = new Date(); ++ ++ String name1 = TIMESTAMP_FORMAT.format( new Date( current.getTime() - 12345 ) ); ++ AuditEvent expectedEvent1 = createTestEvent( name1 ); ++ Date expectedTimestamp = new Date( current.getTime() - 3000 ); ++ String name2 = TIMESTAMP_FORMAT.format( expectedTimestamp ); ++ AuditEvent expectedEvent2 = createTestEvent( name2 ); ++ String name3 = TIMESTAMP_FORMAT.format( new Date( current.getTime() - 1000 ) ); ++ AuditEvent expectedEvent3 = createTestEvent( name3 ); ++ ++ metadataRepositoryControl.expectAndReturn( ++ metadataRepository.getMetadataFacets( TEST_REPO_ID, AuditEvent.FACET_ID ), ++ Arrays.asList( name1, name2, name3 ) ); ++ ++ metadataRepositoryControl.expectAndReturn( ++ metadataRepository.getMetadataFacet( TEST_REPO_ID, AuditEvent.FACET_ID, name1 ), expectedEvent1 ); ++ metadataRepositoryControl.expectAndReturn( ++ metadataRepository.getMetadataFacet( TEST_REPO_ID, AuditEvent.FACET_ID, name2 ), expectedEvent2 ); ++ metadataRepositoryControl.expectAndReturn( ++ metadataRepository.getMetadataFacet( TEST_REPO_ID, AuditEvent.FACET_ID, name3 ), expectedEvent3 ); ++ ++ metadataRepositoryControl.replay(); ++ ++ List events = ++ auditManager.getAuditEventsInRange( TEST_REPO_ID, new Date( current.getTime() - 20000 ), current ); ++ ++ assertEquals( 3, events.size() ); ++ assertEvent( events.get( 0 ), name3, expectedEvent3.getResource() ); ++ assertEvent( events.get( 1 ), name2, expectedEvent2.getResource() ); ++ assertEvent( events.get( 2 ), name1, expectedEvent1.getResource() ); ++ ++ metadataRepositoryControl.verify(); ++ } ++ ++ public void testGetEventsRangeMultipleRepositories() ++ throws ParseException ++ { ++ metadataRepositoryControl.expectAndReturn( metadataRepository.getRepositories(), ++ Arrays.asList( TEST_REPO_ID, TEST_REPO_ID_2 ) ); ++ ++ Date current = new Date(); ++ ++ String name1 = TIMESTAMP_FORMAT.format( new Date( current.getTime() - 12345 ) ); ++ AuditEvent expectedEvent1 = createTestEvent( TEST_REPO_ID, name1 ); ++ Date expectedTimestamp = new Date( current.getTime() - 3000 ); ++ String name2 = TIMESTAMP_FORMAT.format( expectedTimestamp ); ++ AuditEvent expectedEvent2 = createTestEvent( TEST_REPO_ID_2, name2 ); ++ String name3 = TIMESTAMP_FORMAT.format( new Date( current.getTime() - 1000 ) ); ++ AuditEvent expectedEvent3 = createTestEvent( TEST_REPO_ID, name3 ); ++ ++ metadataRepositoryControl.expectAndReturn( ++ metadataRepository.getMetadataFacets( TEST_REPO_ID, AuditEvent.FACET_ID ), Arrays.asList( name1, name3 ) ); ++ metadataRepositoryControl.expectAndReturn( ++ metadataRepository.getMetadataFacets( TEST_REPO_ID_2, AuditEvent.FACET_ID ), Arrays.asList( name2 ) ); ++ ++ metadataRepositoryControl.expectAndReturn( ++ metadataRepository.getMetadataFacet( TEST_REPO_ID, AuditEvent.FACET_ID, name1 ), expectedEvent1 ); ++ metadataRepositoryControl.expectAndReturn( ++ metadataRepository.getMetadataFacet( TEST_REPO_ID_2, AuditEvent.FACET_ID, name2 ), expectedEvent2 ); ++ metadataRepositoryControl.expectAndReturn( ++ metadataRepository.getMetadataFacet( TEST_REPO_ID, AuditEvent.FACET_ID, name3 ), expectedEvent3 ); ++ ++ metadataRepositoryControl.replay(); ++ ++ List events = ++ auditManager.getAuditEventsInRange( null, new Date( current.getTime() - 20000 ), current ); ++ ++ assertEquals( 3, events.size() ); ++ assertEvent( events.get( 0 ), name3, expectedEvent3.getResource() ); ++ assertEvent( events.get( 1 ), name2, expectedEvent2.getResource(), TEST_REPO_ID_2 ); ++ assertEvent( events.get( 2 ), name1, expectedEvent1.getResource() ); ++ ++ metadataRepositoryControl.verify(); ++ } ++ ++ public void testGetEventsRangeNotInside() ++ throws ParseException ++ { ++ Date current = new Date(); ++ ++ String name1 = TIMESTAMP_FORMAT.format( new Date( current.getTime() - 12345 ) ); ++ AuditEvent expectedEvent1 = createTestEvent( name1 ); ++ Date expectedTimestamp = new Date( current.getTime() - 3000 ); ++ String name2 = TIMESTAMP_FORMAT.format( expectedTimestamp ); ++ AuditEvent expectedEvent2 = createTestEvent( name2 ); ++ String name3 = TIMESTAMP_FORMAT.format( new Date( current.getTime() - 1000 ) ); ++ AuditEvent expectedEvent3 = createTestEvent( name3 ); ++ ++ metadataRepositoryControl.expectAndReturn( ++ metadataRepository.getMetadataFacets( TEST_REPO_ID, AuditEvent.FACET_ID ), ++ Arrays.asList( name1, name2, name3 ) ); ++ ++ metadataRepositoryControl.replay(); ++ ++ List events = ++ auditManager.getAuditEventsInRange( TEST_REPO_ID, new Date( current.getTime() - 20000 ), ++ new Date( current.getTime() - 16000 ) ); ++ ++ assertEquals( 0, events.size() ); ++ ++ metadataRepositoryControl.verify(); ++ } ++} diff --cc archiva-modules/plugins/maven2-repository/pom.xml index 6a696c690,000000000..14c3d24fb mode 100644,000000..100644 --- a/archiva-modules/plugins/maven2-repository/pom.xml +++ b/archiva-modules/plugins/maven2-repository/pom.xml @@@ -1,97 -1,0 +1,97 @@@ + + + + 4.0.0 + + plugins + org.apache.archiva - 1.3-SNAPSHOT ++ 1.4-SNAPSHOT + + maven2-repository + Maven 2.x Repository Support + + + org.apache.archiva + problem-reports + + + org.apache.archiva + metadata-model + + + org.apache.archiva + metadata-repository-api + + + org.codehaus.plexus + plexus-spring + test + + + commons-logging + commons-logging-api + test + + + org.slf4j + slf4j-simple + test + + + org.apache.maven + maven-model-builder + + + + org.apache.maven + maven-artifact + + + org.apache.maven.shared + maven-dependency-tree + + + org.apache.maven + maven-project + + + + + org.apache.archiva + archiva-configuration + + + + org.apache.archiva + archiva-xml-tools + + + + + + + org.apache.maven + maven-model + 3.0-alpha-4 + + + + diff --cc archiva-modules/plugins/maven2-repository/src/main/java/org/apache/archiva/metadata/repository/storage/maven2/MavenProjectFacetFactory.java index 5de3777af,000000000..e874b3f40 mode 100644,000000..100644 --- a/archiva-modules/plugins/maven2-repository/src/main/java/org/apache/archiva/metadata/repository/storage/maven2/MavenProjectFacetFactory.java +++ b/archiva-modules/plugins/maven2-repository/src/main/java/org/apache/archiva/metadata/repository/storage/maven2/MavenProjectFacetFactory.java @@@ -1,35 -1,0 +1,40 @@@ +package org.apache.archiva.metadata.repository.storage.maven2; + +/* + * 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 org.apache.archiva.metadata.model.MetadataFacet; +import org.apache.archiva.metadata.model.MetadataFacetFactory; + +/** + * @plexus.component role="org.apache.archiva.metadata.model.MetadataFacetFactory" role-hint="org.apache.archiva.metadata.repository.storage.maven2" + */ +public class MavenProjectFacetFactory + implements MetadataFacetFactory +{ + public MetadataFacet createMetadataFacet() + { + return new MavenProjectFacet(); + } ++ ++ public MetadataFacet createMetadataFacet( String repositoryId, String name ) ++ { ++ throw new UnsupportedOperationException( "There is no valid name for project version facets" ); ++ } +} diff --cc archiva-modules/plugins/metadata-repository-file/pom.xml index 09eeeb71b,000000000..f9188f8b3 mode 100644,000000..100644 --- a/archiva-modules/plugins/metadata-repository-file/pom.xml +++ b/archiva-modules/plugins/metadata-repository-file/pom.xml @@@ -1,59 -1,0 +1,59 @@@ + + + + 4.0.0 + + plugins + org.apache.archiva - 1.3-SNAPSHOT ++ 1.4-SNAPSHOT + + metadata-repository-file + File System Backed Metadata Repository + + + org.apache.archiva + metadata-repository-api + + + commons-io + commons-io + + + org.slf4j + slf4j-api + + + org.slf4j + slf4j-simple + test + + + commons-logging + commons-logging-api + test + + + org.codehaus.plexus + plexus-spring + test + + + diff --cc archiva-modules/plugins/metadata-repository-file/src/main/java/org/apache/archiva/metadata/repository/file/FileMetadataRepository.java index 6011dbca8,000000000..195c11b44 mode 100644,000000..100644 --- a/archiva-modules/plugins/metadata-repository-file/src/main/java/org/apache/archiva/metadata/repository/file/FileMetadataRepository.java +++ b/archiva-modules/plugins/metadata-repository-file/src/main/java/org/apache/archiva/metadata/repository/file/FileMetadataRepository.java @@@ -1,948 -1,0 +1,948 @@@ +package org.apache.archiva.metadata.repository.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. + */ + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.StringTokenizer; + +import org.apache.archiva.metadata.model.ArtifactMetadata; +import org.apache.archiva.metadata.model.CiManagement; +import org.apache.archiva.metadata.model.Dependency; +import org.apache.archiva.metadata.model.IssueManagement; +import org.apache.archiva.metadata.model.License; +import org.apache.archiva.metadata.model.MailingList; +import org.apache.archiva.metadata.model.MetadataFacet; +import org.apache.archiva.metadata.model.MetadataFacetFactory; +import org.apache.archiva.metadata.model.Organization; +import org.apache.archiva.metadata.model.ProjectMetadata; +import org.apache.archiva.metadata.model.ProjectVersionMetadata; +import org.apache.archiva.metadata.model.ProjectVersionReference; +import org.apache.archiva.metadata.model.Scm; +import org.apache.archiva.metadata.repository.MetadataRepository; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @plexus.component role="org.apache.archiva.metadata.repository.MetadataRepository" + */ +public class FileMetadataRepository + implements MetadataRepository +{ + /** + * TODO: this isn't suitable for production use + * + * @plexus.configuration + */ + private File directory = new File( System.getProperty( "user.home" ), ".archiva-metadata" ); + + /** + * @plexus.requirement role="org.apache.archiva.metadata.model.MetadataFacetFactory" + */ + private Map metadataFacetFactories; + + private static final Logger log = LoggerFactory.getLogger( FileMetadataRepository.class ); + + private static final String PROJECT_METADATA_KEY = "project-metadata"; + + private static final String PROJECT_VERSION_METADATA_KEY = "version-metadata"; + + private static final String NAMESPACE_METADATA_KEY = "namespace-metadata"; + + private static final String METADATA_KEY = "metadata"; + + public void updateProject( String repoId, ProjectMetadata project ) + { + updateProject( repoId, project.getNamespace(), project.getId() ); + } + + private void updateProject( String repoId, String namespace, String id ) + { + // TODO: this is a more braindead implementation than we would normally expect, for prototyping purposes + updateNamespace( repoId, namespace ); + + try + { + File namespaceDirectory = new File( this.directory, repoId + "/" + namespace ); + Properties properties = new Properties(); + properties.setProperty( "namespace", namespace ); + properties.setProperty( "id", id ); + writeProperties( properties, new File( namespaceDirectory, id ), PROJECT_METADATA_KEY ); + } + catch ( IOException e ) + { + // TODO! + e.printStackTrace(); + } + } + + public void updateProjectVersion( String repoId, String namespace, String projectId, + ProjectVersionMetadata versionMetadata ) + { + updateProject( repoId, namespace, projectId ); + + File directory = + new File( this.directory, repoId + "/" + namespace + "/" + projectId + "/" + versionMetadata.getId() ); + + Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY ); + // remove properties that are not references or artifacts + for ( String name : properties.stringPropertyNames() ) + { + if ( !name.contains( ":" ) && !name.equals( "facetIds" ) ) + { + properties.remove( name ); + } + } + properties.setProperty( "id", versionMetadata.getId() ); + setProperty( properties, "name", versionMetadata.getName() ); + setProperty( properties, "description", versionMetadata.getDescription() ); + setProperty( properties, "url", versionMetadata.getUrl() ); + setProperty( properties, "incomplete", String.valueOf( versionMetadata.isIncomplete() ) ); + if ( versionMetadata.getScm() != null ) + { + setProperty( properties, "scm.connection", versionMetadata.getScm().getConnection() ); + setProperty( properties, "scm.developerConnection", versionMetadata.getScm().getDeveloperConnection() ); + setProperty( properties, "scm.url", versionMetadata.getScm().getUrl() ); + } + if ( versionMetadata.getCiManagement() != null ) + { + setProperty( properties, "ci.system", versionMetadata.getCiManagement().getSystem() ); + setProperty( properties, "ci.url", versionMetadata.getCiManagement().getUrl() ); + } + if ( versionMetadata.getIssueManagement() != null ) + { + setProperty( properties, "issue.system", versionMetadata.getIssueManagement().getSystem() ); + setProperty( properties, "issue.url", versionMetadata.getIssueManagement().getUrl() ); + } + if ( versionMetadata.getOrganization() != null ) + { + setProperty( properties, "org.name", versionMetadata.getOrganization().getName() ); + setProperty( properties, "org.url", versionMetadata.getOrganization().getUrl() ); + } + int i = 0; + for ( License license : versionMetadata.getLicenses() ) + { + setProperty( properties, "license." + i + ".name", license.getName() ); + setProperty( properties, "license." + i + ".url", license.getUrl() ); + i++; + } + i = 0; + for ( MailingList mailingList : versionMetadata.getMailingLists() ) + { + setProperty( properties, "mailingList." + i + ".archive", mailingList.getMainArchiveUrl() ); + setProperty( properties, "mailingList." + i + ".name", mailingList.getName() ); + setProperty( properties, "mailingList." + i + ".post", mailingList.getPostAddress() ); + setProperty( properties, "mailingList." + i + ".unsubscribe", mailingList.getUnsubscribeAddress() ); + setProperty( properties, "mailingList." + i + ".subscribe", mailingList.getSubscribeAddress() ); + setProperty( properties, "mailingList." + i + ".otherArchives", join( mailingList.getOtherArchives() ) ); + i++; + } + i = 0; + for ( Dependency dependency : versionMetadata.getDependencies() ) + { + setProperty( properties, "dependency." + i + ".classifier", dependency.getClassifier() ); + setProperty( properties, "dependency." + i + ".scope", dependency.getScope() ); + setProperty( properties, "dependency." + i + ".systemPath", dependency.getSystemPath() ); + setProperty( properties, "dependency." + i + ".artifactId", dependency.getArtifactId() ); + setProperty( properties, "dependency." + i + ".groupId", dependency.getGroupId() ); + setProperty( properties, "dependency." + i + ".version", dependency.getVersion() ); + setProperty( properties, "dependency." + i + ".type", dependency.getType() ); + i++; + } + Set facetIds = new LinkedHashSet( versionMetadata.getFacetIds() ); + facetIds.addAll( Arrays.asList( properties.getProperty( "facetIds", "" ).split( "," ) ) ); + properties.setProperty( "facetIds", join( facetIds ) ); + + for ( MetadataFacet facet : versionMetadata.getFacetList() ) + { + properties.putAll( facet.toProperties() ); + } + + try + { + writeProperties( properties, directory, PROJECT_VERSION_METADATA_KEY ); + } + catch ( IOException e ) + { + // TODO + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + } + + public void updateProjectReference( String repoId, String namespace, String projectId, String projectVersion, + ProjectVersionReference reference ) + { + File directory = new File( this.directory, repoId + "/" + namespace + "/" + projectId + "/" + projectVersion ); + + Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY ); + int i = Integer.valueOf( properties.getProperty( "ref:lastReferenceNum", "-1" ) ) + 1; + setProperty( properties, "ref:lastReferenceNum", Integer.toString( i ) ); + setProperty( properties, "ref:reference." + i + ".namespace", reference.getNamespace() ); + setProperty( properties, "ref:reference." + i + ".projectId", reference.getProjectId() ); + setProperty( properties, "ref:reference." + i + ".projectVersion", reference.getProjectVersion() ); + setProperty( properties, "ref:reference." + i + ".referenceType", reference.getReferenceType().toString() ); + + try + { + writeProperties( properties, directory, PROJECT_VERSION_METADATA_KEY ); + } + catch ( IOException e ) + { + // TODO + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + } + + public void updateNamespace( String repoId, String namespace ) + { + try + { + File namespaceDirectory = new File( this.directory, repoId + "/" + namespace ); + Properties properties = new Properties(); + properties.setProperty( "namespace", namespace ); + writeProperties( properties, namespaceDirectory, NAMESPACE_METADATA_KEY ); + + } + catch ( IOException e ) + { + // TODO! + e.printStackTrace(); + } + } + + public List getMetadataFacets( String repoId, String facetId ) + { + File directory = getMetadataDirectory( repoId, facetId ); + List facets = new ArrayList(); + recurse( facets, "", directory ); + return facets; + } + + private void recurse( List facets, String prefix, File directory ) + { + File[] list = directory.listFiles(); + if ( list != null ) + { + for ( File dir : list ) + { + if ( dir.isDirectory() ) + { + recurse( facets, prefix + "/" + dir.getName(), dir ); + } + else if ( dir.getName().equals( METADATA_KEY + ".properties" ) ) + { + facets.add( prefix.substring( 1 ) ); + } + } + } + } + + public MetadataFacet getMetadataFacet( String repositoryId, String facetId, String name ) + { + Properties properties; + try + { + properties = + readProperties( new File( getMetadataDirectory( repositoryId, facetId ), name ), METADATA_KEY ); + } + catch ( FileNotFoundException e ) + { + return null; + } + catch ( IOException e ) + { + // TODO + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + return null; + } + MetadataFacet metadataFacet = null; + MetadataFacetFactory metadataFacetFactory = metadataFacetFactories.get( facetId ); + if ( metadataFacetFactory != null ) + { - metadataFacet = metadataFacetFactory.createMetadataFacet(); ++ metadataFacet = metadataFacetFactory.createMetadataFacet( repositoryId, name ); + Map map = new HashMap(); + for ( String key : properties.stringPropertyNames() ) + { + map.put( key, properties.getProperty( key ) ); + } + metadataFacet.fromProperties( map ); + } + return metadataFacet; + } + + public void addMetadataFacet( String repositoryId, MetadataFacet metadataFacet ) + { + Properties properties = new Properties(); + properties.putAll( metadataFacet.toProperties() ); + + try + { + File directory = + new File( getMetadataDirectory( repositoryId, metadataFacet.getFacetId() ), metadataFacet.getName() ); + writeProperties( properties, directory, METADATA_KEY ); + } + catch ( IOException e ) + { + // TODO! + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + } + + public void removeMetadataFacets( String repositoryId, String facetId ) + { + try + { + FileUtils.deleteDirectory( getMetadataDirectory( repositoryId, facetId ) ); + } + catch ( IOException e ) + { + // TODO! + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + } + + public void removeMetadataFacet( String repoId, String facetId, String name ) + { + File dir = new File( getMetadataDirectory( repoId, facetId ), name ); + try + { + FileUtils.deleteDirectory( dir ); + } + catch ( IOException e ) + { + // TODO + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + } + + public List getArtifactsByDateRange( String repoId, Date startTime, Date endTime ) + { + // TODO: this is quite slow - if we are to persist with this repository implementation we should build an index + // of this information (eg. in Lucene, as before) + + List artifacts = new ArrayList(); + for ( String ns : getRootNamespaces( repoId ) ) + { + getArtifactsByDateRange( artifacts, repoId, ns, startTime, endTime ); + } + return artifacts; + } + + private void getArtifactsByDateRange( List artifacts, String repoId, String ns, Date startTime, + Date endTime ) + { + for ( String namespace : getNamespaces( repoId, ns ) ) + { + getArtifactsByDateRange( artifacts, repoId, ns + "." + namespace, startTime, endTime ); + } + + for ( String project : getProjects( repoId, ns ) ) + { + for ( String version : getProjectVersions( repoId, ns, project ) ) + { + for ( ArtifactMetadata artifact : getArtifacts( repoId, ns, project, version ) ) + { + if ( startTime == null || startTime.before( artifact.getWhenGathered() ) ) + { + if ( endTime == null || endTime.after( artifact.getWhenGathered() ) ) + { + artifacts.add( artifact ); + } + } + } + } + } + } + + public Collection getArtifacts( String repoId, String namespace, String projectId, + String projectVersion ) + { + Map artifacts = new HashMap(); + + File directory = new File( this.directory, repoId + "/" + namespace + "/" + projectId + "/" + projectVersion ); + + Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY ); + + for ( Map.Entry entry : properties.entrySet() ) + { + String name = (String) entry.getKey(); + StringTokenizer tok = new StringTokenizer( name, ":" ); + if ( tok.hasMoreTokens() && "artifact".equals( tok.nextToken() ) ) + { + String field = tok.nextToken(); + String id = tok.nextToken(); + + ArtifactMetadata artifact = artifacts.get( id ); + if ( artifact == null ) + { + artifact = new ArtifactMetadata(); + artifact.setRepositoryId( repoId ); + artifact.setNamespace( namespace ); + artifact.setProject( projectId ); + artifact.setVersion( projectVersion ); + artifact.setId( id ); + artifacts.put( id, artifact ); + } + + String value = (String) entry.getValue(); + if ( "updated".equals( field ) ) + { + artifact.setFileLastModified( Long.valueOf( value ) ); + } + else if ( "size".equals( field ) ) + { + artifact.setSize( Long.valueOf( value ) ); + } + else if ( "whenGathered".equals( field ) ) + { + artifact.setWhenGathered( new Date( Long.valueOf( value ) ) ); + } + else if ( "version".equals( field ) ) + { + artifact.setVersion( value ); + } + else if ( "md5".equals( field ) ) + { + artifact.setMd5( value ); + } + else if ( "sha1".equals( field ) ) + { + artifact.setSha1( value ); + } + } + } + return artifacts.values(); + } + + public Collection getRepositories() + { + String[] repoIds = this.directory.list(); + return repoIds != null ? Arrays.asList( repoIds ) : Collections.emptyList(); + } + + public List getArtifactsByChecksum( String repositoryId, String checksum ) + { + // TODO: this is quite slow - if we are to persist with this repository implementation we should build an index + // of this information (eg. in Lucene, as before) + // alternatively, we could build a referential tree in the content repository, however it would need some levels + // of depth to avoid being too broad to be useful (eg. /repository/checksums/a/ab/abcdef1234567) + + List artifacts = new ArrayList(); + for ( String ns : getRootNamespaces( repositoryId ) ) + { + getArtifactsByChecksum( artifacts, repositoryId, ns, checksum ); + } + return artifacts; + } + + public void deleteArtifact( String repositoryId, String namespace, String project, String version, String id ) + { + File directory = new File( this.directory, repositoryId + "/" + namespace + "/" + project + "/" + version ); + + Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY ); + + properties.remove( "artifact:updated:" + id ); + properties.remove( "artifact:whenGathered:" + id ); + properties.remove( "artifact:size:" + id ); + properties.remove( "artifact:md5:" + id ); + properties.remove( "artifact:sha1:" + id ); + properties.remove( "artifact:version:" + id ); + + try + { + writeProperties( properties, directory, PROJECT_VERSION_METADATA_KEY ); + } + catch ( IOException e ) + { + // TODO + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + } + + public void deleteRepository( String repoId ) + { + File directory = new File( this.directory, repoId ); + + try + { + FileUtils.deleteDirectory( directory ); + } + catch ( IOException e ) + { + // TODO + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + } + + private void getArtifactsByChecksum( List artifacts, String repositoryId, String ns, + String checksum ) + { + for ( String namespace : getNamespaces( repositoryId, ns ) ) + { + getArtifactsByChecksum( artifacts, repositoryId, ns + "." + namespace, checksum ); + } + + for ( String project : getProjects( repositoryId, ns ) ) + { + for ( String version : getProjectVersions( repositoryId, ns, project ) ) + { + for ( ArtifactMetadata artifact : getArtifacts( repositoryId, ns, project, version ) ) + { + if ( checksum.equals( artifact.getMd5() ) || checksum.equals( artifact.getSha1() ) ) + { + artifacts.add( artifact ); + } + } + } + } + } + + private File getMetadataDirectory( String repositoryId, String facetId ) + { + return new File( this.directory, repositoryId + "/.meta/" + facetId ); + } + + private String join( Collection ids ) + { + if ( !ids.isEmpty() ) + { + StringBuilder s = new StringBuilder(); + for ( String id : ids ) + { + s.append( id ); + s.append( "," ); + } + return s.substring( 0, s.length() - 1 ); + } + return ""; + } + + private void setProperty( Properties properties, String name, String value ) + { + if ( value != null ) + { + properties.setProperty( name, value ); + } + } + + public void updateArtifact( String repoId, String namespace, String projectId, String projectVersion, + ArtifactMetadata artifact ) + { + File directory = new File( this.directory, repoId + "/" + namespace + "/" + projectId + "/" + projectVersion ); + + Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY ); + + properties.setProperty( "artifact:updated:" + artifact.getId(), + Long.toString( artifact.getFileLastModified().getTime() ) ); + properties.setProperty( "artifact:whenGathered:" + artifact.getId(), + Long.toString( artifact.getWhenGathered().getTime() ) ); + properties.setProperty( "artifact:size:" + artifact.getId(), Long.toString( artifact.getSize() ) ); + if ( artifact.getMd5() != null ) + { + properties.setProperty( "artifact:md5:" + artifact.getId(), artifact.getMd5() ); + } + if ( artifact.getSha1() != null ) + { + properties.setProperty( "artifact:sha1:" + artifact.getId(), artifact.getSha1() ); + } + properties.setProperty( "artifact:version:" + artifact.getId(), artifact.getVersion() ); + + try + { + writeProperties( properties, directory, PROJECT_VERSION_METADATA_KEY ); + } + catch ( IOException e ) + { + // TODO + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + } + + private Properties readOrCreateProperties( File directory, String propertiesKey ) + { + try + { + return readProperties( directory, propertiesKey ); + } + catch ( FileNotFoundException e ) + { + // ignore and return new properties + } + catch ( IOException e ) + { + // TODO + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + return new Properties(); + } + + private Properties readProperties( File directory, String propertiesKey ) + throws IOException + { + Properties properties = new Properties(); + FileInputStream in = null; + try + { + in = new FileInputStream( new File( directory, propertiesKey + ".properties" ) ); + properties.load( in ); + } + finally + { + IOUtils.closeQuietly( in ); + } + return properties; + } + + public ProjectMetadata getProject( String repoId, String namespace, String projectId ) + { + File directory = new File( this.directory, repoId + "/" + namespace + "/" + projectId ); + + Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY ); + + ProjectMetadata project = new ProjectMetadata(); + project.setNamespace( properties.getProperty( "namespace" ) ); + project.setId( properties.getProperty( "id" ) ); + return project; + } + + public ProjectVersionMetadata getProjectVersion( String repoId, String namespace, String projectId, + String projectVersion ) + { + File directory = new File( this.directory, repoId + "/" + namespace + "/" + projectId + "/" + projectVersion ); + + Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY ); + String id = properties.getProperty( "id" ); + ProjectVersionMetadata versionMetadata = null; + if ( id != null ) + { + versionMetadata = new ProjectVersionMetadata(); + versionMetadata.setId( id ); + versionMetadata.setName( properties.getProperty( "name" ) ); + versionMetadata.setDescription( properties.getProperty( "description" ) ); + versionMetadata.setUrl( properties.getProperty( "url" ) ); + versionMetadata.setIncomplete( Boolean.valueOf( properties.getProperty( "incomplete", "false" ) ) ); + + String scmConnection = properties.getProperty( "scm.connection" ); + String scmDeveloperConnection = properties.getProperty( "scm.developerConnection" ); + String scmUrl = properties.getProperty( "scm.url" ); + if ( scmConnection != null || scmDeveloperConnection != null || scmUrl != null ) + { + Scm scm = new Scm(); + scm.setConnection( scmConnection ); + scm.setDeveloperConnection( scmDeveloperConnection ); + scm.setUrl( scmUrl ); + versionMetadata.setScm( scm ); + } + + String ciSystem = properties.getProperty( "ci.system" ); + String ciUrl = properties.getProperty( "ci.url" ); + if ( ciSystem != null || ciUrl != null ) + { + CiManagement ci = new CiManagement(); + ci.setSystem( ciSystem ); + ci.setUrl( ciUrl ); + versionMetadata.setCiManagement( ci ); + } + + String issueSystem = properties.getProperty( "issue.system" ); + String issueUrl = properties.getProperty( "issue.url" ); + if ( issueSystem != null || issueUrl != null ) + { + IssueManagement issueManagement = new IssueManagement(); + issueManagement.setSystem( issueSystem ); + issueManagement.setUrl( issueUrl ); + versionMetadata.setIssueManagement( issueManagement ); + } + + String orgName = properties.getProperty( "org.name" ); + String orgUrl = properties.getProperty( "org.url" ); + if ( orgName != null || orgUrl != null ) + { + Organization org = new Organization(); + org.setName( orgName ); + org.setUrl( orgUrl ); + versionMetadata.setOrganization( org ); + } + + boolean done = false; + int i = 0; + while ( !done ) + { + String licenseName = properties.getProperty( "license." + i + ".name" ); + String licenseUrl = properties.getProperty( "license." + i + ".url" ); + if ( licenseName != null || licenseUrl != null ) + { + License license = new License(); + license.setName( licenseName ); + license.setUrl( licenseUrl ); + versionMetadata.addLicense( license ); + } + else + { + done = true; + } + i++; + } + + done = false; + i = 0; + while ( !done ) + { + String mailingListName = properties.getProperty( "mailingList." + i + ".name" ); + if ( mailingListName != null ) + { + MailingList mailingList = new MailingList(); + mailingList.setName( mailingListName ); + mailingList.setMainArchiveUrl( properties.getProperty( "mailingList." + i + ".archive" ) ); + mailingList.setOtherArchives( + Arrays.asList( properties.getProperty( "mailingList." + i + ".otherArchives" ).split( "," ) ) ); + mailingList.setPostAddress( properties.getProperty( "mailingList." + i + ".post" ) ); + mailingList.setSubscribeAddress( properties.getProperty( "mailingList." + i + ".subscribe" ) ); + mailingList.setUnsubscribeAddress( properties.getProperty( "mailingList." + i + ".unsubscribe" ) ); + versionMetadata.addMailingList( mailingList ); + } + else + { + done = true; + } + i++; + } + + done = false; + i = 0; + while ( !done ) + { + String dependencyArtifactId = properties.getProperty( "dependency." + i + ".artifactId" ); + if ( dependencyArtifactId != null ) + { + Dependency dependency = new Dependency(); + dependency.setArtifactId( dependencyArtifactId ); + dependency.setGroupId( properties.getProperty( "dependency." + i + ".groupId" ) ); + dependency.setClassifier( properties.getProperty( "dependency." + i + ".classifier" ) ); + dependency.setOptional( + Boolean.valueOf( properties.getProperty( "dependency." + i + ".optional" ) ) ); + dependency.setScope( properties.getProperty( "dependency." + i + ".scope" ) ); + dependency.setSystemPath( properties.getProperty( "dependency." + i + ".systemPath" ) ); + dependency.setType( properties.getProperty( "dependency." + i + ".type" ) ); + dependency.setVersion( properties.getProperty( "dependency." + i + ".version" ) ); + versionMetadata.addDependency( dependency ); + } + else + { + done = true; + } + i++; + } + + String facetIds = properties.getProperty( "facetIds", "" ); + if ( facetIds.length() > 0 ) + { + for ( String facetId : facetIds.split( "," ) ) + { + MetadataFacetFactory factory = metadataFacetFactories.get( facetId ); + if ( factory == null ) + { + log.error( "Attempted to load unknown metadata facet: " + facetId ); + } + else + { + MetadataFacet facet = factory.createMetadataFacet(); + Map map = new HashMap(); + for ( String key : properties.stringPropertyNames() ) + { + if ( key.startsWith( facet.getFacetId() ) ) + { + map.put( key, properties.getProperty( key ) ); + } + } + facet.fromProperties( map ); + versionMetadata.addFacet( facet ); + } + } + } + + for ( MetadataFacet facet : versionMetadata.getFacetList() ) + { + properties.putAll( facet.toProperties() ); + } + } + return versionMetadata; + } + + public Collection getArtifactVersions( String repoId, String namespace, String projectId, + String projectVersion ) + { + File directory = new File( this.directory, repoId + "/" + namespace + "/" + projectId + "/" + projectVersion ); + + Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY ); + + Set versions = new HashSet(); + for ( Map.Entry entry : properties.entrySet() ) + { + String name = (String) entry.getKey(); + if ( name.startsWith( "artifact:version:" ) ) + { + versions.add( (String) entry.getValue() ); + } + } + return versions; + } + + public Collection getProjectReferences( String repoId, String namespace, String projectId, + String projectVersion ) + { + File directory = new File( this.directory, repoId + "/" + namespace + "/" + projectId + "/" + projectVersion ); + + Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY ); + int numberOfRefs = Integer.valueOf( properties.getProperty( "ref:lastReferenceNum", "-1" ) ) + 1; + + List references = new ArrayList(); + for ( int i = 0; i < numberOfRefs; i++ ) + { + ProjectVersionReference reference = new ProjectVersionReference(); + reference.setProjectId( properties.getProperty( "ref:reference." + i + ".projectId" ) ); + reference.setNamespace( properties.getProperty( "ref:reference." + i + ".namespace" ) ); + reference.setProjectVersion( properties.getProperty( "ref:reference." + i + ".projectVersion" ) ); + reference.setReferenceType( ProjectVersionReference.ReferenceType.valueOf( + properties.getProperty( "ref:reference." + i + ".referenceType" ) ) ); + references.add( reference ); + } + return references; + } + + public Collection getRootNamespaces( String repoId ) + { + return getNamespaces( repoId, null ); + } + + public Collection getNamespaces( String repoId, String baseNamespace ) + { + List allNamespaces = new ArrayList(); + File directory = new File( this.directory, repoId ); + File[] files = directory.listFiles(); + if ( files != null ) + { + for ( File namespace : files ) + { + if ( new File( namespace, NAMESPACE_METADATA_KEY + ".properties" ).exists() ) + { + allNamespaces.add( namespace.getName() ); + } + } + } + + Set namespaces = new LinkedHashSet(); + int fromIndex = baseNamespace != null ? baseNamespace.length() + 1 : 0; + for ( String namespace : allNamespaces ) + { + if ( baseNamespace == null || namespace.startsWith( baseNamespace + "." ) ) + { + int i = namespace.indexOf( '.', fromIndex ); + if ( i >= 0 ) + { + namespaces.add( namespace.substring( fromIndex, i ) ); + } + else + { + namespaces.add( namespace.substring( fromIndex ) ); + } + } + } + return new ArrayList( namespaces ); + } + + public Collection getProjects( String repoId, String namespace ) + { + List projects = new ArrayList(); + File directory = new File( this.directory, repoId + "/" + namespace ); + File[] files = directory.listFiles(); + if ( files != null ) + { + for ( File project : files ) + { + if ( new File( project, PROJECT_METADATA_KEY + ".properties" ).exists() ) + { + projects.add( project.getName() ); + } + } + } + return projects; + } + + public Collection getProjectVersions( String repoId, String namespace, String projectId ) + { + List projectVersions = new ArrayList(); + File directory = new File( this.directory, repoId + "/" + namespace + "/" + projectId ); + File[] files = directory.listFiles(); + if ( files != null ) + { + for ( File projectVersion : files ) + { + if ( new File( projectVersion, PROJECT_VERSION_METADATA_KEY + ".properties" ).exists() ) + { + projectVersions.add( projectVersion.getName() ); + } + } + } + return projectVersions; + } + + private void writeProperties( Properties properties, File directory, String propertiesKey ) + throws IOException + { + directory.mkdirs(); + FileOutputStream os = new FileOutputStream( new File( directory, propertiesKey + ".properties" ) ); + try + { + properties.store( os, null ); + } + finally + { + IOUtils.closeQuietly( os ); + } + } + + public void setDirectory( File directory ) + { + this.directory = directory; + } + + public void setMetadataFacetFactories( Map metadataFacetFactories ) + { + this.metadataFacetFactories = metadataFacetFactories; + } +} diff --cc archiva-modules/plugins/metadata-repository-file/src/test/java/org/apache/archiva/metadata/repository/file/FileMetadataRepositoryTest.java index 915b0190f,000000000..ed71d570b mode 100644,000000..100644 --- a/archiva-modules/plugins/metadata-repository-file/src/test/java/org/apache/archiva/metadata/repository/file/FileMetadataRepositoryTest.java +++ b/archiva-modules/plugins/metadata-repository-file/src/test/java/org/apache/archiva/metadata/repository/file/FileMetadataRepositoryTest.java @@@ -1,645 -1,0 +1,657 @@@ +package org.apache.archiva.metadata.repository.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. + */ + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +import org.apache.archiva.metadata.model.ArtifactMetadata; +import org.apache.archiva.metadata.model.MailingList; +import org.apache.archiva.metadata.model.MetadataFacet; +import org.apache.archiva.metadata.model.MetadataFacetFactory; +import org.apache.archiva.metadata.model.ProjectMetadata; +import org.apache.archiva.metadata.model.ProjectVersionMetadata; +import org.apache.commons.io.FileUtils; +import org.codehaus.plexus.spring.PlexusInSpringTestCase; + +/** + * @todo should this be a generic MetadataRepository implementation test? + */ +public class FileMetadataRepositoryTest + extends PlexusInSpringTestCase +{ + private FileMetadataRepository repository; + + private static final String TEST_REPO_ID = "test"; + + private static final String TEST_PROJECT = "projectId"; + + private static final String TEST_NAMESPACE = "namespace"; + + private static final String TEST_PROJECT_VERSION = "1.0"; + + private static final String TEST_FACET_ID = "test-facet-id"; + + private static final String TEST_NAME = "test/name"; + + private static final String TEST_VALUE = "test-value"; + + private static final String UNKNOWN = "unknown"; + + private static final String OTHER_REPO = "other-repo"; + + private static final String TEST_MD5 = "bd4a9b642562547754086de2dab26b7d"; + + private static final String TEST_SHA1 = "2e5daf0201ddeb068a62d5e08da18657ab2c6be9"; + ++ private static final String TEST_METADATA_VALUE = "test-metadata"; ++ + public void setUp() + throws Exception + { + super.setUp(); + + repository = new FileMetadataRepository(); + File directory = getTestFile( "target/test-repository" ); + FileUtils.deleteDirectory( directory ); + repository.setDirectory( directory ); + + Map factories = new HashMap(); + factories.put( TEST_FACET_ID, new MetadataFacetFactory() + { + public MetadataFacet createMetadataFacet() + { - return new TestMetadataFacet( "test-metadata" ); ++ return new TestMetadataFacet( TEST_METADATA_VALUE ); ++ } ++ ++ public MetadataFacet createMetadataFacet( String repositoryId, String name ) ++ { ++ return new TestMetadataFacet( TEST_METADATA_VALUE ); + } + } ); + + // add to ensure we don't accidentally create an empty facet ID. + factories.put( "", new MetadataFacetFactory() + { + public MetadataFacet createMetadataFacet() + { - return new TestMetadataFacet( "", "test-value" ); ++ return new TestMetadataFacet( "", TEST_VALUE ); ++ } ++ ++ public MetadataFacet createMetadataFacet( String repositoryId, String name ) ++ { ++ return new TestMetadataFacet( "", TEST_VALUE ); + } + } ); + repository.setMetadataFacetFactories( factories ); + } + + public void testRootNamespaceWithNoMetadataRepository() + { + Collection namespaces = repository.getRootNamespaces( TEST_REPO_ID ); + assertEquals( Collections.emptyList(), namespaces ); + } + + public void testUpdateProjectVersionMetadataWithNoOtherArchives() + { + ProjectVersionMetadata metadata = new ProjectVersionMetadata(); + metadata.setId( TEST_PROJECT_VERSION ); + MailingList mailingList = new MailingList(); + mailingList.setName( "Foo List" ); + mailingList.setOtherArchives( Collections.emptyList() ); + metadata.setMailingLists( Collections.singletonList( mailingList ) ); + repository.updateProjectVersion( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, metadata ); + } + + public void testUpdateProjectVersionMetadataIncomplete() + { + ProjectVersionMetadata metadata = new ProjectVersionMetadata(); + metadata.setId( TEST_PROJECT_VERSION ); + metadata.setIncomplete( true ); + repository.updateProjectVersion( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, metadata ); + + metadata = repository.getProjectVersion( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION ); + assertEquals( true, metadata.isIncomplete() ); + } + + public void testUpdateProjectVersionMetadataWithExistingFacets() + { + ProjectVersionMetadata metadata = new ProjectVersionMetadata(); + metadata.setId( TEST_PROJECT_VERSION ); + MetadataFacet facet = new TestMetadataFacet( "baz" ); + metadata.addFacet( facet ); + repository.updateProjectVersion( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, metadata ); + + metadata = repository.getProjectVersion( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION ); + assertEquals( Collections.singleton( TEST_FACET_ID ), metadata.getFacetIds() ); + + metadata = new ProjectVersionMetadata(); + metadata.setId( TEST_PROJECT_VERSION ); + repository.updateProjectVersion( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, metadata ); + + metadata = repository.getProjectVersion( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION ); + assertEquals( Collections.singleton( TEST_FACET_ID ), metadata.getFacetIds() ); + TestMetadataFacet testFacet = (TestMetadataFacet) metadata.getFacet( TEST_FACET_ID ); + assertEquals( "baz", testFacet.getValue() ); + } + + public void testUpdateProjectVersionMetadataWithNoExistingFacets() + { + ProjectVersionMetadata metadata = new ProjectVersionMetadata(); + metadata.setId( TEST_PROJECT_VERSION ); + repository.updateProjectVersion( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, metadata ); + + metadata = repository.getProjectVersion( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION ); + assertEquals( Collections.emptyList(), new ArrayList( metadata.getFacetIds() ) ); + + metadata = new ProjectVersionMetadata(); + metadata.setId( TEST_PROJECT_VERSION ); + repository.updateProjectVersion( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, metadata ); + + metadata = repository.getProjectVersion( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION ); + assertEquals( Collections.emptyList(), new ArrayList( metadata.getFacetIds() ) ); + } + + public void testGetMetadataFacet() + { + repository.addMetadataFacet( TEST_REPO_ID, new TestMetadataFacet( TEST_VALUE ) ); + + assertEquals( new TestMetadataFacet( TEST_VALUE ), + repository.getMetadataFacet( TEST_REPO_ID, TEST_FACET_ID, TEST_NAME ) ); + } + + public void testGetMetadataFacetWhenEmpty() + { + assertNull( repository.getMetadataFacet( TEST_REPO_ID, TEST_FACET_ID, TEST_NAME ) ); + } + + public void testGetMetadataFacetWhenUnknownName() + { + repository.addMetadataFacet( TEST_REPO_ID, new TestMetadataFacet( TEST_VALUE ) ); + + assertNull( repository.getMetadataFacet( TEST_REPO_ID, TEST_FACET_ID, UNKNOWN ) ); + } + + public void testGetMetadataFacetWhenDefaultValue() + { + repository.addMetadataFacet( TEST_REPO_ID, new TestMetadataFacet( null ) ); + - assertEquals( new TestMetadataFacet( "test-metadata" ), ++ assertEquals( new TestMetadataFacet( TEST_METADATA_VALUE ), + repository.getMetadataFacet( TEST_REPO_ID, TEST_FACET_ID, TEST_NAME ) ); + } + + public void testGetMetadataFacetWhenUnknownFacetId() + { + assertNull( repository.getMetadataFacet( TEST_REPO_ID, UNKNOWN, TEST_NAME ) ); + } + + public void testGetMetadataFacets() + { + repository.addMetadataFacet( TEST_REPO_ID, new TestMetadataFacet( TEST_VALUE ) ); + + assertEquals( Collections.singletonList( TEST_NAME ), + repository.getMetadataFacets( TEST_REPO_ID, TEST_FACET_ID ) ); + } + + public void testGetMetadataFacetsWhenEmpty() + { + List facets = repository.getMetadataFacets( TEST_REPO_ID, TEST_FACET_ID ); + assertTrue( facets.isEmpty() ); + } + + public void testRemoveFacets() + { + repository.addMetadataFacet( TEST_REPO_ID, new TestMetadataFacet( TEST_VALUE ) ); + + List facets = repository.getMetadataFacets( TEST_REPO_ID, TEST_FACET_ID ); + assertFalse( facets.isEmpty() ); + + repository.removeMetadataFacets( TEST_REPO_ID, TEST_FACET_ID ); + + facets = repository.getMetadataFacets( TEST_REPO_ID, TEST_FACET_ID ); + assertTrue( facets.isEmpty() ); + } + + public void testRemoveFacetsWhenEmpty() + { + List facets = repository.getMetadataFacets( TEST_REPO_ID, TEST_FACET_ID ); + assertTrue( facets.isEmpty() ); + + repository.removeMetadataFacets( TEST_REPO_ID, TEST_FACET_ID ); + + facets = repository.getMetadataFacets( TEST_REPO_ID, TEST_FACET_ID ); + assertTrue( facets.isEmpty() ); + } + + public void testRemoveFacetsWhenUnknown() + { + repository.removeMetadataFacets( TEST_REPO_ID, UNKNOWN ); + } + + public void testRemoveFacet() + { + TestMetadataFacet metadataFacet = new TestMetadataFacet( TEST_VALUE ); + repository.addMetadataFacet( TEST_REPO_ID, metadataFacet ); + + assertEquals( metadataFacet, repository.getMetadataFacet( TEST_REPO_ID, TEST_FACET_ID, TEST_NAME ) ); + List facets = repository.getMetadataFacets( TEST_REPO_ID, TEST_FACET_ID ); + assertFalse( facets.isEmpty() ); + + repository.removeMetadataFacet( TEST_REPO_ID, TEST_FACET_ID, TEST_NAME ); + + assertNull( repository.getMetadataFacet( TEST_REPO_ID, TEST_FACET_ID, TEST_NAME ) ); + facets = repository.getMetadataFacets( TEST_REPO_ID, TEST_FACET_ID ); + assertTrue( facets.isEmpty() ); + } + + public void testRemoveFacetWhenEmpty() + { + List facets = repository.getMetadataFacets( TEST_REPO_ID, TEST_FACET_ID ); + assertTrue( facets.isEmpty() ); + assertNull( repository.getMetadataFacet( TEST_REPO_ID, TEST_FACET_ID, TEST_NAME ) ); + + repository.removeMetadataFacet( TEST_REPO_ID, TEST_FACET_ID, TEST_NAME ); + + facets = repository.getMetadataFacets( TEST_REPO_ID, TEST_FACET_ID ); + assertTrue( facets.isEmpty() ); + assertNull( repository.getMetadataFacet( TEST_REPO_ID, TEST_FACET_ID, TEST_NAME ) ); + } + + public void testRemoveFacetWhenUnknown() + { + repository.removeMetadataFacet( TEST_REPO_ID, UNKNOWN, TEST_NAME ); + } + + public void testGetArtifacts() + { + ArtifactMetadata artifact1 = createArtifact(); + ArtifactMetadata artifact2 = createArtifact( "pom" ); + repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, artifact1 ); + repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, artifact2 ); + + assertEquals( Arrays.asList( artifact2, artifact1 ), new ArrayList( + repository.getArtifacts( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION ) ) ); + } + + public void testGetArtifactVersions() + { + ArtifactMetadata artifact1 = createArtifact(); + String version1 = "1.0-20091212.012345-1"; + artifact1.setId( artifact1.getProject() + "-" + version1 + ".jar" ); + artifact1.setVersion( version1 ); + ArtifactMetadata artifact2 = createArtifact(); + String version2 = "1.0-20091212.123456-2"; + artifact2.setId( artifact2.getProject() + "-" + version2 + ".jar" ); + artifact2.setVersion( version2 ); + repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, artifact1 ); + repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, artifact2 ); + + assertEquals( new HashSet( Arrays.asList( version2, version1 ) ), + repository.getArtifactVersions( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, + TEST_PROJECT_VERSION ) ); + } + + public void testGetArtifactVersionsMultipleArtifactsSingleVersion() + { + ArtifactMetadata artifact1 = createArtifact(); + artifact1.setId( TEST_PROJECT + "-" + TEST_PROJECT_VERSION + ".jar" ); + ArtifactMetadata artifact2 = createArtifact(); + artifact2.setId( TEST_PROJECT + "-" + TEST_PROJECT_VERSION + "-sources.jar" ); + repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, artifact1 ); + repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, artifact2 ); + + assertEquals( Collections.singleton( TEST_PROJECT_VERSION ), + repository.getArtifactVersions( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, + TEST_PROJECT_VERSION ) ); + } + + public void testRepositories() + { + repository.addMetadataFacet( TEST_REPO_ID, new TestMetadataFacet( TEST_VALUE ) ); + repository.addMetadataFacet( OTHER_REPO, new TestMetadataFacet( TEST_VALUE ) ); + + assertEquals( Arrays.asList( OTHER_REPO, TEST_REPO_ID ), repository.getRepositories() ); + } + + public void testRepositoriesWhenEmpty() + { + assertTrue( repository.getRepositories().isEmpty() ); + } + + public void testGetArtifactsByDateRangeOpen() + { + repository.updateNamespace( TEST_REPO_ID, TEST_NAMESPACE ); + repository.updateProject( TEST_REPO_ID, createProject() ); + ArtifactMetadata artifact = createArtifact(); + repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, artifact ); + + assertEquals( Collections.singletonList( artifact ), + repository.getArtifactsByDateRange( TEST_REPO_ID, null, null ) ); + } + + public void testGetArtifactsByDateRangeSparseNamespace() + { + String namespace = "org.apache.archiva"; + repository.updateNamespace( TEST_REPO_ID, namespace ); + repository.updateProject( TEST_REPO_ID, createProject( namespace ) ); + ArtifactMetadata artifact = createArtifact(); + artifact.setNamespace( namespace ); + repository.updateArtifact( TEST_REPO_ID, namespace, TEST_PROJECT, TEST_PROJECT_VERSION, artifact ); + + assertEquals( Collections.singletonList( artifact ), + repository.getArtifactsByDateRange( TEST_REPO_ID, null, null ) ); + } + + public void testGetArtifactsByDateRangeLowerBound() + { + repository.updateNamespace( TEST_REPO_ID, TEST_NAMESPACE ); + repository.updateProject( TEST_REPO_ID, createProject() ); + ArtifactMetadata artifact = createArtifact(); + repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, artifact ); + + Date date = new Date( artifact.getWhenGathered().getTime() - 10000 ); + assertEquals( Collections.singletonList( artifact ), + repository.getArtifactsByDateRange( TEST_REPO_ID, date, null ) ); + } + + public void testGetArtifactsByDateRangeLowerBoundOutOfRange() + { + repository.updateNamespace( TEST_REPO_ID, TEST_NAMESPACE ); + repository.updateProject( TEST_REPO_ID, createProject() ); + ArtifactMetadata artifact = createArtifact(); + repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, artifact ); + + Date date = new Date( artifact.getWhenGathered().getTime() + 10000 ); + assertTrue( repository.getArtifactsByDateRange( TEST_REPO_ID, date, null ).isEmpty() ); + } + + public void testGetArtifactsByDateRangeLowerAndUpperBound() + { + repository.updateNamespace( TEST_REPO_ID, TEST_NAMESPACE ); + repository.updateProject( TEST_REPO_ID, createProject() ); + ArtifactMetadata artifact = createArtifact(); + repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, artifact ); + + Date lower = new Date( artifact.getWhenGathered().getTime() - 10000 ); + Date upper = new Date( artifact.getWhenGathered().getTime() + 10000 ); + assertEquals( Collections.singletonList( artifact ), + repository.getArtifactsByDateRange( TEST_REPO_ID, lower, upper ) ); + } + + public void testGetArtifactsByDateRangeUpperBound() + { + repository.updateNamespace( TEST_REPO_ID, TEST_NAMESPACE ); + repository.updateProject( TEST_REPO_ID, createProject() ); + ArtifactMetadata artifact = createArtifact(); + repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, artifact ); + + Date upper = new Date( artifact.getWhenGathered().getTime() + 10000 ); + assertEquals( Collections.singletonList( artifact ), + repository.getArtifactsByDateRange( TEST_REPO_ID, null, upper ) ); + } + + public void testGetArtifactsByDateRangeUpperBoundOutOfRange() + { + repository.updateNamespace( TEST_REPO_ID, TEST_NAMESPACE ); + repository.updateProject( TEST_REPO_ID, createProject() ); + ArtifactMetadata artifact = createArtifact(); + repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, artifact ); + + Date upper = new Date( artifact.getWhenGathered().getTime() - 10000 ); + assertTrue( repository.getArtifactsByDateRange( TEST_REPO_ID, null, upper ).isEmpty() ); + } + + public void testGetNamespacesWithSparseDepth() + { + repository.updateNamespace( TEST_REPO_ID, "org.apache.maven.shared" ); + + assertEquals( Arrays.asList( "org" ), repository.getRootNamespaces( TEST_REPO_ID ) ); + assertEquals( Arrays.asList( "apache" ), repository.getNamespaces( TEST_REPO_ID, "org" ) ); + assertEquals( Arrays.asList( "maven" ), repository.getNamespaces( TEST_REPO_ID, "org.apache" ) ); + assertEquals( Arrays.asList( "shared" ), repository.getNamespaces( TEST_REPO_ID, "org.apache.maven" ) ); + } + + public void testGetArtifactsByChecksumSingleResultMd5() + { + repository.updateNamespace( TEST_REPO_ID, TEST_NAMESPACE ); + repository.updateProject( TEST_REPO_ID, createProject() ); + ArtifactMetadata artifact = createArtifact(); + repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, artifact ); + + assertEquals( Collections.singletonList( artifact ), + repository.getArtifactsByChecksum( TEST_REPO_ID, TEST_MD5 ) ); + } + + public void testGetArtifactsByChecksumSingleResultSha1() + { + repository.updateNamespace( TEST_REPO_ID, TEST_NAMESPACE ); + repository.updateProject( TEST_REPO_ID, createProject() ); + ArtifactMetadata artifact = createArtifact(); + repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, artifact ); + + assertEquals( Collections.singletonList( artifact ), + repository.getArtifactsByChecksum( TEST_REPO_ID, TEST_SHA1 ) ); + } + + public void testGetArtifactsByChecksumMultipleResult() + { + repository.updateNamespace( TEST_REPO_ID, TEST_NAMESPACE ); + + ProjectMetadata projectMetadata = createProject(); + repository.updateProject( TEST_REPO_ID, projectMetadata ); + ArtifactMetadata artifact1 = createArtifact(); + repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, artifact1 ); + + projectMetadata = createProject(); + String newProjectId = "another-project"; + projectMetadata.setId( newProjectId ); + repository.updateProject( TEST_REPO_ID, projectMetadata ); + ArtifactMetadata artifact2 = createArtifact(); + artifact2.setProject( newProjectId ); + repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, newProjectId, TEST_PROJECT_VERSION, artifact2 ); + + assertEquals( Arrays.asList( artifact2, artifact1 ), + repository.getArtifactsByChecksum( TEST_REPO_ID, TEST_SHA1 ) ); + } + + public void testGetArtifactsByChecksumNoResult() + { + repository.updateNamespace( TEST_REPO_ID, TEST_NAMESPACE ); + repository.updateProject( TEST_REPO_ID, createProject() ); + ArtifactMetadata artifact = createArtifact(); + repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, artifact ); + + assertEquals( Collections.emptyList(), + repository.getArtifactsByChecksum( TEST_REPO_ID, "not a checksum" ) ); + } + + public void testDeleteArtifact() + { + ArtifactMetadata artifact = createArtifact(); + repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, artifact ); + + assertEquals( Collections.singletonList( artifact ), new ArrayList( + repository.getArtifacts( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION ) ) ); + + repository.deleteArtifact( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, artifact.getId() ); + + assertTrue( + repository.getArtifacts( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION ).isEmpty() ); + } + + public void testDeleteRepository() + { + repository.updateNamespace( TEST_REPO_ID, TEST_NAMESPACE ); + + ProjectMetadata project1 = new ProjectMetadata(); + project1.setNamespace( TEST_NAMESPACE ); + project1.setId( "project1" ); + repository.updateProject( TEST_REPO_ID, project1 ); + ProjectMetadata project2 = new ProjectMetadata(); + project2.setNamespace( TEST_NAMESPACE ); + project2.setId( "project2" ); + repository.updateProject( TEST_REPO_ID, project2 ); + + ArtifactMetadata artifact1 = createArtifact(); + artifact1.setProject( "project1" ); + repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, "project1", TEST_PROJECT_VERSION, artifact1 ); + ArtifactMetadata artifact2 = createArtifact(); + artifact2.setProject( "project2" ); + repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, "project2", TEST_PROJECT_VERSION, artifact2 ); + + assertEquals( Arrays.asList( artifact1, artifact2 ), new ArrayList( + repository.getArtifactsByDateRange( TEST_REPO_ID, null, null ) ) ); + + repository.deleteRepository( TEST_REPO_ID ); + + assertTrue( repository.getArtifactsByDateRange( TEST_REPO_ID, null, null ).isEmpty() ); + } + + private ProjectMetadata createProject() + { + return createProject( TEST_NAMESPACE ); + } + + private ProjectMetadata createProject( String ns ) + { + ProjectMetadata project = new ProjectMetadata(); + project.setId( TEST_PROJECT ); + project.setNamespace( ns ); + return project; + } + + private ArtifactMetadata createArtifact() + { + return createArtifact( "jar" ); + } + + private ArtifactMetadata createArtifact( String type ) + { + ArtifactMetadata artifact = new ArtifactMetadata(); + artifact.setId( TEST_PROJECT + "-" + TEST_PROJECT_VERSION + "." + type ); + artifact.setWhenGathered( new Date() ); + artifact.setNamespace( TEST_NAMESPACE ); + artifact.setProject( TEST_PROJECT ); + artifact.setRepositoryId( TEST_REPO_ID ); + artifact.setFileLastModified( System.currentTimeMillis() ); + artifact.setVersion( TEST_PROJECT_VERSION ); + artifact.setMd5( TEST_MD5 ); + artifact.setSha1( TEST_SHA1 ); + return artifact; + } + + private static class TestMetadataFacet + implements MetadataFacet + { + private String testFacetId; + + private TestMetadataFacet( String value ) + { + this.value = value; + testFacetId = TEST_FACET_ID; + } + + private TestMetadataFacet( String facetId, String value ) + { + this.value = value; + testFacetId = facetId; + } + + private String value; + + public String getFacetId() + { + return testFacetId; + } + + public String getName() + { + return TEST_NAME; + } + + public Map toProperties() + { + if ( value != null ) + { + return Collections.singletonMap( testFacetId + ":foo", value ); + } + else + { + return Collections.emptyMap(); + } + } + + public void fromProperties( Map properties ) + { + String value = properties.get( testFacetId + ":foo" ); + if ( value != null ) + { + this.value = value; + } + } + + public String getValue() + { + return value; + } + + @Override + public String toString() + { + return "TestMetadataFacet{" + "value='" + value + '\'' + '}'; + } + + @Override + public boolean equals( Object o ) + { + if ( this == o ) + { + return true; + } + if ( o == null || getClass() != o.getClass() ) + { + return false; + } + + TestMetadataFacet that = (TestMetadataFacet) o; + + if ( value != null ? !value.equals( that.value ) : that.value != null ) + { + return false; + } + + return true; + } + + @Override + public int hashCode() + { + return value != null ? value.hashCode() : 0; + } + } +} diff --cc archiva-modules/plugins/pom.xml index 19878499a,000000000..a564ac416 mode 100644,000000..100644 --- a/archiva-modules/plugins/pom.xml +++ b/archiva-modules/plugins/pom.xml @@@ -1,35 -1,0 +1,36 @@@ + + + 4.0.0 + + archiva-modules + org.apache.archiva - 1.3-SNAPSHOT ++ 1.4-SNAPSHOT + + plugins + Archiva Core Plugins + pom + + metadata-repository-file + maven2-repository + repository-statistics + problem-reports ++ audit + + diff --cc archiva-modules/plugins/problem-reports/pom.xml index 955cfdf62,000000000..5d98090a5 mode 100644,000000..100644 --- a/archiva-modules/plugins/problem-reports/pom.xml +++ b/archiva-modules/plugins/problem-reports/pom.xml @@@ -1,45 -1,0 +1,45 @@@ + + + + 4.0.0 + + plugins + org.apache.archiva - 1.3-SNAPSHOT ++ 1.4-SNAPSHOT + + problem-reports + Archiva Problem Reporting Plugin + + + org.apache.archiva + metadata-repository-api + + + + org.apache.archiva + archiva-repository-layer + + + org.apache.archiva + archiva-checksum + + + diff --cc archiva-modules/plugins/problem-reports/src/main/java/org/apache/archiva/reports/RepositoryProblemFacetFactory.java index aeb602b59,000000000..5f95f1496 mode 100644,000000..100644 --- a/archiva-modules/plugins/problem-reports/src/main/java/org/apache/archiva/reports/RepositoryProblemFacetFactory.java +++ b/archiva-modules/plugins/problem-reports/src/main/java/org/apache/archiva/reports/RepositoryProblemFacetFactory.java @@@ -1,35 -1,0 +1,40 @@@ +package org.apache.archiva.reports; + +/* + * 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 org.apache.archiva.metadata.model.MetadataFacet; +import org.apache.archiva.metadata.model.MetadataFacetFactory; + +/** + * @plexus.component role="org.apache.archiva.metadata.model.MetadataFacetFactory" role-hint="org.apache.archiva.reports" + */ +public class RepositoryProblemFacetFactory + implements MetadataFacetFactory +{ + public MetadataFacet createMetadataFacet() + { + return new RepositoryProblemFacet(); + } ++ ++ public MetadataFacet createMetadataFacet( String repositoryId, String name ) ++ { ++ return new RepositoryProblemFacet(); ++ } +} diff --cc archiva-modules/plugins/repository-statistics/pom.xml index 83a2df5d7,000000000..247e1d884 mode 100644,000000..100644 --- a/archiva-modules/plugins/repository-statistics/pom.xml +++ b/archiva-modules/plugins/repository-statistics/pom.xml @@@ -1,59 -1,0 +1,59 @@@ + + + + 4.0.0 + + plugins + org.apache.archiva - 1.3-SNAPSHOT ++ 1.4-SNAPSHOT + + repository-statistics + Repository Statistics + + + org.apache.archiva + metadata-repository-api + + + org.codehaus.plexus + plexus-spring + test + + + org.slf4j + slf4j-api + + + org.slf4j + slf4j-simple + test + + + org.slf4j + jcl-over-slf4j + test + + + org.apache.archiva + archiva-repository-layer + + + diff --cc archiva-modules/plugins/repository-statistics/src/main/java/org/apache/archiva/metadata/repository/stats/DefaultRepositoryStatisticsManager.java index 80d50359e,000000000..60ce58a15 mode 100644,000000..100644 --- a/archiva-modules/plugins/repository-statistics/src/main/java/org/apache/archiva/metadata/repository/stats/DefaultRepositoryStatisticsManager.java +++ b/archiva-modules/plugins/repository-statistics/src/main/java/org/apache/archiva/metadata/repository/stats/DefaultRepositoryStatisticsManager.java @@@ -1,196 -1,0 +1,197 @@@ +package org.apache.archiva.metadata.repository.stats; + +/* + * 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 java.text.ParseException; ++import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +import org.apache.archiva.metadata.model.ArtifactMetadata; +import org.apache.archiva.metadata.repository.MetadataRepository; +import org.apache.maven.archiva.repository.ManagedRepositoryContent; +import org.apache.maven.archiva.repository.RepositoryContentFactory; +import org.apache.maven.archiva.repository.RepositoryException; +import org.apache.maven.archiva.repository.layout.LayoutException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @plexus.component role="org.apache.archiva.metadata.repository.stats.RepositoryStatisticsManager" role-hint="default" + */ +public class DefaultRepositoryStatisticsManager + implements RepositoryStatisticsManager +{ + private static final Logger log = LoggerFactory.getLogger( DefaultRepositoryStatisticsManager.class ); + + /** + * @plexus.requirement + */ + private MetadataRepository metadataRepository; + + /** + * @plexus.requirement + */ + private RepositoryContentFactory repositoryContentFactory; + + public RepositoryStatistics getLastStatistics( String repositoryId ) + { + // TODO: consider a more efficient implementation that directly gets the last one from the content repository + List scans = metadataRepository.getMetadataFacets( repositoryId, RepositoryStatistics.FACET_ID ); + Collections.sort( scans ); + if ( !scans.isEmpty() ) + { + String name = scans.get( scans.size() - 1 ); + return (RepositoryStatistics) metadataRepository.getMetadataFacet( repositoryId, + RepositoryStatistics.FACET_ID, name ); + } + else + { + return null; + } + } + + private void walkRepository( RepositoryStatistics stats, String repositoryId, String ns, + ManagedRepositoryContent repositoryContent ) + { + for ( String namespace : metadataRepository.getNamespaces( repositoryId, ns ) ) + { + walkRepository( stats, repositoryId, ns + "." + namespace, repositoryContent ); + } + + Collection projects = metadataRepository.getProjects( repositoryId, ns ); + if ( !projects.isEmpty() ) + { + stats.setTotalGroupCount( stats.getTotalGroupCount() + 1 ); + stats.setTotalProjectCount( stats.getTotalProjectCount() + projects.size() ); + + for ( String project : projects ) + { + for ( String version : metadataRepository.getProjectVersions( repositoryId, ns, project ) ) + { + for ( ArtifactMetadata artifact : metadataRepository.getArtifacts( repositoryId, ns, project, + version ) ) + { + stats.setTotalArtifactCount( stats.getTotalArtifactCount() + 1 ); + stats.setTotalArtifactFileSize( stats.getTotalArtifactFileSize() + artifact.getSize() ); + + // TODO: need a maven2 metadata repository API equivalent + try + { + String type = repositoryContent.toArtifactReference( + ns.replace( '.', '/' ) + "/" + project + "/" + version + "/" + + artifact.getId() ).getType(); + stats.setTotalCountForType( type, stats.getTotalCountForType( type ) + 1 ); + } + catch ( LayoutException e ) + { + // ignore + } + } + } + } + } + } + + + public void addStatisticsAfterScan( String repositoryId, Date startTime, Date endTime, long totalFiles, + long newFiles ) + { + RepositoryStatistics repositoryStatistics = new RepositoryStatistics(); + repositoryStatistics.setScanStartTime( startTime ); + repositoryStatistics.setScanEndTime( endTime ); + repositoryStatistics.setTotalFileCount( totalFiles ); + repositoryStatistics.setNewFileCount( newFiles ); + + // In the future, instead of being tied to a scan we might want to record information in the fly based on + // events that are occurring. Even without these totals we could query much of the information on demand based + // on information from the metadata content repository. In the mean time, we lock information in at scan time. + // Note that if new types are later discoverable due to a code change or new plugin, historical stats will not + // be updated and the repository will need to be rescanned. + + long startWalk = System.currentTimeMillis(); + // TODO: we can probably get a more efficient implementation directly from the metadata repository, but for now + // we just walk it. Alternatively, we could build an index, or store the aggregate information and update + // it on the fly + for ( String ns : metadataRepository.getRootNamespaces( repositoryId ) ) + { + ManagedRepositoryContent content; + try + { + content = repositoryContentFactory.getManagedRepositoryContent( repositoryId ); + } + catch ( RepositoryException e ) + { + throw new RuntimeException( e ); + } + walkRepository( repositoryStatistics, repositoryId, ns, content ); + } + log.info( "Repository walk for statistics executed in " + ( System.currentTimeMillis() - startWalk ) + "ms" ); + + metadataRepository.addMetadataFacet( repositoryId, repositoryStatistics ); + } + + public void deleteStatistics( String repositoryId ) + { + metadataRepository.removeMetadataFacets( repositoryId, RepositoryStatistics.FACET_ID ); + } + + public List getStatisticsInRange( String repositoryId, Date startTime, Date endTime ) + { + List results = new ArrayList(); + List list = metadataRepository.getMetadataFacets( repositoryId, RepositoryStatistics.FACET_ID ); + Collections.sort( list, Collections.reverseOrder() ); + for ( String name : list ) + { + try + { - Date date = RepositoryStatistics.SCAN_TIMESTAMP.parse( name ); ++ Date date = new SimpleDateFormat( RepositoryStatistics.SCAN_TIMESTAMP_FORMAT ).parse( name ); + if ( ( startTime == null || !date.before( startTime ) ) && + ( endTime == null || !date.after( endTime ) ) ) + { + RepositoryStatistics stats = + (RepositoryStatistics) metadataRepository.getMetadataFacet( repositoryId, + RepositoryStatistics.FACET_ID, + name ); + results.add( stats ); + } + } + catch ( ParseException e ) + { + log.error( "Invalid scan result found in the metadata repository: " + e.getMessage() ); + // continue and ignore this one + } + } + return results; + } + + public void setMetadataRepository( MetadataRepository metadataRepository ) + { + this.metadataRepository = metadataRepository; + } + + public void setRepositoryContentFactory( RepositoryContentFactory repositoryContentFactory ) + { + this.repositoryContentFactory = repositoryContentFactory; + } +} diff --cc archiva-modules/plugins/repository-statistics/src/main/java/org/apache/archiva/metadata/repository/stats/RepositoryStatistics.java index 79678ac3d,000000000..68407cb54 mode 100644,000000..100644 --- a/archiva-modules/plugins/repository-statistics/src/main/java/org/apache/archiva/metadata/repository/stats/RepositoryStatistics.java +++ b/archiva-modules/plugins/repository-statistics/src/main/java/org/apache/archiva/metadata/repository/stats/RepositoryStatistics.java @@@ -1,281 -1,0 +1,280 @@@ +package org.apache.archiva.metadata.repository.stats; + +/* + * 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 java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import org.apache.archiva.metadata.model.MetadataFacet; + +public class RepositoryStatistics + implements MetadataFacet +{ + private Date scanEndTime; + + private Date scanStartTime; + + private long totalArtifactCount; + + private long totalArtifactFileSize; + + private long totalFileCount; + + private long totalGroupCount; + + private long totalProjectCount; + + private long newFileCount; + + public static String FACET_ID = "org.apache.archiva.metadata.repository.stats"; + - static final DateFormat SCAN_TIMESTAMP = new SimpleDateFormat( "yyyy/MM/dd/HHmmss.SSS" ); ++ static final String SCAN_TIMESTAMP_FORMAT = "yyyy/MM/dd/HHmmss.SSS"; + + private Map totalCountForType = new HashMap(); + + public Date getScanEndTime() + { + return scanEndTime; + } + + public void setScanEndTime( Date scanEndTime ) + { + this.scanEndTime = scanEndTime; + } + + public Date getScanStartTime() + { + return scanStartTime; + } + + public void setScanStartTime( Date scanStartTime ) + { + this.scanStartTime = scanStartTime; + } + + public long getTotalArtifactCount() + { + return totalArtifactCount; + } + + public void setTotalArtifactCount( long totalArtifactCount ) + { + this.totalArtifactCount = totalArtifactCount; + } + + public long getTotalArtifactFileSize() + { + return totalArtifactFileSize; + } + + public void setTotalArtifactFileSize( long totalArtifactFileSize ) + { + this.totalArtifactFileSize = totalArtifactFileSize; + } + + public long getTotalFileCount() + { + return totalFileCount; + } + + public void setTotalFileCount( long totalFileCount ) + { + this.totalFileCount = totalFileCount; + } + + public long getTotalGroupCount() + { + return totalGroupCount; + } + + public void setTotalGroupCount( long totalGroupCount ) + { + this.totalGroupCount = totalGroupCount; + } + + public long getTotalProjectCount() + { + return totalProjectCount; + } + + public void setTotalProjectCount( long totalProjectCount ) + { + this.totalProjectCount = totalProjectCount; + } + + public void setNewFileCount( long newFileCount ) + { + this.newFileCount = newFileCount; + } + + public long getNewFileCount() + { + return newFileCount; + } + + public long getDuration() + { + return scanEndTime.getTime() - scanStartTime.getTime(); + } + + public String getFacetId() + { + return FACET_ID; + } + + public String getName() + { - return SCAN_TIMESTAMP.format( scanStartTime ); ++ return new SimpleDateFormat( SCAN_TIMESTAMP_FORMAT ).format( scanStartTime ); + } + + public Map toProperties() + { + Map properties = new HashMap(); + properties.put( "scanEndTime", String.valueOf( scanEndTime.getTime() ) ); + properties.put( "scanStartTime", String.valueOf( scanStartTime.getTime() ) ); + properties.put( "totalArtifactCount", String.valueOf( totalArtifactCount ) ); + properties.put( "totalArtifactFileSize", String.valueOf( totalArtifactFileSize ) ); + properties.put( "totalFileCount", String.valueOf( totalFileCount ) ); + properties.put( "totalGroupCount", String.valueOf( totalGroupCount ) ); + properties.put( "totalProjectCount", String.valueOf( totalProjectCount ) ); + properties.put( "newFileCount", String.valueOf( newFileCount ) ); + for ( Map.Entry entry : totalCountForType.entrySet() ) + { + properties.put( "count-" + entry.getKey(), String.valueOf( entry.getValue() ) ); + } + return properties; + } + + public void fromProperties( Map properties ) + { + scanEndTime = new Date( Long.valueOf( properties.get( "scanEndTime" ) ) ); + scanStartTime = new Date( Long.valueOf( properties.get( "scanStartTime" ) ) ); + totalArtifactCount = Long.valueOf( properties.get( "totalArtifactCount" ) ); + totalArtifactFileSize = Long.valueOf( properties.get( "totalArtifactFileSize" ) ); + totalFileCount = Long.valueOf( properties.get( "totalFileCount" ) ); + totalGroupCount = Long.valueOf( properties.get( "totalGroupCount" ) ); + totalProjectCount = Long.valueOf( properties.get( "totalProjectCount" ) ); + newFileCount = Long.valueOf( properties.get( "newFileCount" ) ); + totalCountForType.clear(); + for ( Map.Entry entry : properties.entrySet() ) + { + if ( entry.getKey().startsWith( "count-" ) ) + { + totalCountForType.put( entry.getKey().substring( 6 ), Long.valueOf( entry.getValue() ) ); + } + } + } + + @Override + public boolean equals( Object o ) + { + if ( this == o ) + { + return true; + } + if ( o == null || getClass() != o.getClass() ) + { + return false; + } + + RepositoryStatistics that = (RepositoryStatistics) o; + + if ( newFileCount != that.newFileCount ) + { + return false; + } + if ( totalArtifactCount != that.totalArtifactCount ) + { + return false; + } + if ( totalArtifactFileSize != that.totalArtifactFileSize ) + { + return false; + } + if ( totalFileCount != that.totalFileCount ) + { + return false; + } + if ( totalGroupCount != that.totalGroupCount ) + { + return false; + } + if ( totalProjectCount != that.totalProjectCount ) + { + return false; + } + if ( !scanEndTime.equals( that.scanEndTime ) ) + { + return false; + } + if ( !scanStartTime.equals( that.scanStartTime ) ) + { + return false; + } + if ( !totalCountForType.equals( that.totalCountForType ) ) + { + return false; + } + + return true; + } + + @Override + public int hashCode() + { + int result = scanEndTime.hashCode(); + result = 31 * result + scanStartTime.hashCode(); + result = 31 * result + (int) ( totalArtifactCount ^ ( totalArtifactCount >>> 32 ) ); + result = 31 * result + (int) ( totalArtifactFileSize ^ ( totalArtifactFileSize >>> 32 ) ); + result = 31 * result + (int) ( totalFileCount ^ ( totalFileCount >>> 32 ) ); + result = 31 * result + (int) ( totalGroupCount ^ ( totalGroupCount >>> 32 ) ); + result = 31 * result + (int) ( totalProjectCount ^ ( totalProjectCount >>> 32 ) ); + result = 31 * result + (int) ( newFileCount ^ ( newFileCount >>> 32 ) ); + result = 31 * result + totalCountForType.hashCode(); + return result; + } + + @Override + public String toString() + { + return "RepositoryStatistics{" + "scanEndTime=" + scanEndTime + ", scanStartTime=" + scanStartTime + + ", totalArtifactCount=" + totalArtifactCount + ", totalArtifactFileSize=" + totalArtifactFileSize + + ", totalFileCount=" + totalFileCount + ", totalGroupCount=" + totalGroupCount + ", totalProjectCount=" + + totalProjectCount + ", newFileCount=" + newFileCount + ", totalCountForType=" + totalCountForType + '}'; + } + + public Map getTotalCountForType() + { + return totalCountForType; + } + + public long getTotalCountForType( String type ) + { + Long value = totalCountForType.get( type ); + return value != null ? value : 0; + } + + public void setTotalCountForType( String type, long count ) + { + totalCountForType.put( type, count ); + } +} diff --cc archiva-modules/plugins/repository-statistics/src/main/java/org/apache/archiva/metadata/repository/stats/RepositoryStatisticsFactory.java index d819195f8,000000000..49716a99c mode 100644,000000..100644 --- a/archiva-modules/plugins/repository-statistics/src/main/java/org/apache/archiva/metadata/repository/stats/RepositoryStatisticsFactory.java +++ b/archiva-modules/plugins/repository-statistics/src/main/java/org/apache/archiva/metadata/repository/stats/RepositoryStatisticsFactory.java @@@ -1,35 -1,0 +1,40 @@@ +package org.apache.archiva.metadata.repository.stats; + +/* + * 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 org.apache.archiva.metadata.model.MetadataFacet; +import org.apache.archiva.metadata.model.MetadataFacetFactory; + +/** + * @plexus.component role="org.apache.archiva.metadata.model.MetadataFacetFactory" role-hint="org.apache.archiva.metadata.repository.stats" + */ +public class RepositoryStatisticsFactory + implements MetadataFacetFactory +{ + public MetadataFacet createMetadataFacet() + { + return new RepositoryStatistics(); + } ++ ++ public MetadataFacet createMetadataFacet( String repositoryId, String name ) ++ { ++ return new RepositoryStatistics(); ++ } +} diff --cc archiva-modules/plugins/repository-statistics/src/test/java/org/apache/archiva/metadata/repository/stats/RepositoryStatisticsManagerTest.java index 09ec07907,000000000..453e3a4d8 mode 100644,000000..100644 --- a/archiva-modules/plugins/repository-statistics/src/test/java/org/apache/archiva/metadata/repository/stats/RepositoryStatisticsManagerTest.java +++ b/archiva-modules/plugins/repository-statistics/src/test/java/org/apache/archiva/metadata/repository/stats/RepositoryStatisticsManagerTest.java @@@ -1,564 -1,0 +1,576 @@@ +package org.apache.archiva.metadata.repository.stats; + +/* + * 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 java.text.ParseException; ++import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import junit.framework.TestCase; +import org.apache.archiva.metadata.model.ArtifactMetadata; +import org.apache.archiva.metadata.repository.MetadataRepository; +import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration; +import org.apache.maven.archiva.repository.RepositoryContentFactory; +import org.apache.maven.archiva.repository.content.ManagedDefaultRepositoryContent; +import org.easymock.MockControl; +import org.easymock.classextension.MockClassControl; + +public class RepositoryStatisticsManagerTest + extends TestCase +{ + private DefaultRepositoryStatisticsManager repositoryStatisticsManager; + + private static final String TEST_REPO_ID = "test-repo"; + + private MockControl metadataRepositoryControl; + + private MetadataRepository metadataRepository; + + private static final String FIRST_TEST_SCAN = "2009/12/01/123456.789"; + + private static final String SECOND_TEST_SCAN = "2009/12/02/012345.678"; + + private Map statsCreated = new LinkedHashMap(); + + @Override + protected void setUp() + throws Exception + { + super.setUp(); + + repositoryStatisticsManager = new DefaultRepositoryStatisticsManager(); + + metadataRepositoryControl = MockControl.createControl( MetadataRepository.class ); + metadataRepository = (MetadataRepository) metadataRepositoryControl.getMock(); + repositoryStatisticsManager.setMetadataRepository( metadataRepository ); + + ManagedRepositoryConfiguration repository = new ManagedRepositoryConfiguration(); + repository.setId( TEST_REPO_ID ); + repository.setLocation( "" ); + ManagedDefaultRepositoryContent content = new ManagedDefaultRepositoryContent(); + content.setRepository( repository ); + MockControl control = MockClassControl.createControl( RepositoryContentFactory.class ); + RepositoryContentFactory contentFactory = (RepositoryContentFactory) control.getMock(); + contentFactory.getManagedRepositoryContent( TEST_REPO_ID ); + control.setDefaultReturnValue( content ); + control.replay(); + repositoryStatisticsManager.setRepositoryContentFactory( contentFactory ); + } + + public void testGetLatestStats() + throws ParseException + { - Date endTime = new Date( RepositoryStatistics.SCAN_TIMESTAMP.parse( SECOND_TEST_SCAN ).getTime() + 60000 ); ++ Date startTime = parseTimestamp( SECOND_TEST_SCAN ); ++ Date endTime = new Date( startTime.getTime() + 60000 ); + + RepositoryStatistics stats = new RepositoryStatistics(); - stats.setScanStartTime( RepositoryStatistics.SCAN_TIMESTAMP.parse( SECOND_TEST_SCAN ) ); ++ stats.setScanStartTime( startTime ); + stats.setScanEndTime( endTime ); + stats.setTotalArtifactFileSize( 1314527915L ); + stats.setNewFileCount( 123 ); + stats.setTotalArtifactCount( 10386 ); + stats.setTotalProjectCount( 2031 ); + stats.setTotalGroupCount( 529 ); + stats.setTotalFileCount( 56229 ); + + metadataRepositoryControl.expectAndReturn( + metadataRepository.getMetadataFacets( TEST_REPO_ID, RepositoryStatistics.FACET_ID ), + Arrays.asList( FIRST_TEST_SCAN, SECOND_TEST_SCAN ) ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getMetadataFacet( TEST_REPO_ID, RepositoryStatistics.FACET_ID, SECOND_TEST_SCAN ), + stats ); + metadataRepositoryControl.replay(); + + stats = repositoryStatisticsManager.getLastStatistics( TEST_REPO_ID ); + assertNotNull( stats ); + assertEquals( 1314527915L, stats.getTotalArtifactFileSize() ); + assertEquals( 123, stats.getNewFileCount() ); + assertEquals( 10386, stats.getTotalArtifactCount() ); + assertEquals( 2031, stats.getTotalProjectCount() ); + assertEquals( 529, stats.getTotalGroupCount() ); + assertEquals( 56229, stats.getTotalFileCount() ); - assertEquals( SECOND_TEST_SCAN, RepositoryStatistics.SCAN_TIMESTAMP.format( stats.getScanStartTime() ) ); ++ assertEquals( SECOND_TEST_SCAN, formatTimestamp( stats.getScanStartTime() ) ); + assertEquals( SECOND_TEST_SCAN, stats.getName() ); + assertEquals( endTime, stats.getScanEndTime() ); + + metadataRepositoryControl.verify(); + } + ++ private static String formatTimestamp( Date value ) ++ { ++ return new SimpleDateFormat( RepositoryStatistics.SCAN_TIMESTAMP_FORMAT ).format( value ); ++ } ++ ++ private static Date parseTimestamp( String value ) ++ throws ParseException ++ { ++ return new SimpleDateFormat( RepositoryStatistics.SCAN_TIMESTAMP_FORMAT ).parse( value ); ++ } ++ + public void testGetLatestStatsWhenEmpty() + { + metadataRepositoryControl.expectAndReturn( + metadataRepository.getMetadataFacets( TEST_REPO_ID, RepositoryStatistics.FACET_ID ), + Collections.emptyList() ); + metadataRepositoryControl.replay(); + + RepositoryStatistics stats = repositoryStatisticsManager.getLastStatistics( TEST_REPO_ID ); + assertNull( stats ); + + metadataRepositoryControl.verify(); + } + + public void testAddNewStats() + { + Date current = new Date(); + Date startTime = new Date( current.getTime() - 12345 ); + + RepositoryStatistics stats = createTestStats( startTime, current ); + + walkRepository( 1 ); + + metadataRepository.addMetadataFacet( TEST_REPO_ID, stats ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getMetadataFacets( TEST_REPO_ID, RepositoryStatistics.FACET_ID ), + Arrays.asList( stats.getName() ) ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getMetadataFacet( TEST_REPO_ID, RepositoryStatistics.FACET_ID, stats.getName() ), + stats ); + + metadataRepositoryControl.replay(); + + repositoryStatisticsManager.addStatisticsAfterScan( TEST_REPO_ID, startTime, current, 56345, 45 ); + + stats = repositoryStatisticsManager.getLastStatistics( TEST_REPO_ID ); + assertNotNull( stats ); + assertEquals( 246900, stats.getTotalArtifactFileSize() ); + assertEquals( 45, stats.getNewFileCount() ); + assertEquals( 20, stats.getTotalArtifactCount() ); + assertEquals( 5, stats.getTotalProjectCount() ); + assertEquals( 4, stats.getTotalGroupCount() ); + assertEquals( 56345, stats.getTotalFileCount() ); + assertEquals( current.getTime() - 12345, stats.getScanStartTime().getTime() ); + assertEquals( current, stats.getScanEndTime() ); + + metadataRepositoryControl.verify(); + } + + public void testDeleteStats() + { + walkRepository( 2 ); + + Date current = new Date(); + + Date startTime1 = new Date( current.getTime() - 12345 ); + RepositoryStatistics stats1 = createTestStats( startTime1, new Date( current.getTime() - 6000 ) ); + metadataRepository.addMetadataFacet( TEST_REPO_ID, stats1 ); + + Date startTime2 = new Date( current.getTime() - 3000 ); + RepositoryStatistics stats2 = createTestStats( startTime2, current ); + metadataRepository.addMetadataFacet( TEST_REPO_ID, stats2 ); + + metadataRepositoryControl.expectAndReturn( + metadataRepository.getMetadataFacets( TEST_REPO_ID, RepositoryStatistics.FACET_ID ), + Arrays.asList( stats1.getName(), stats2.getName() ) ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getMetadataFacet( TEST_REPO_ID, RepositoryStatistics.FACET_ID, stats2.getName() ), + stats2 ); + + metadataRepository.removeMetadataFacets( TEST_REPO_ID, RepositoryStatistics.FACET_ID ); + + metadataRepositoryControl.expectAndReturn( + metadataRepository.getMetadataFacets( TEST_REPO_ID, RepositoryStatistics.FACET_ID ), + Collections.emptyList() ); + + metadataRepositoryControl.replay(); + + repositoryStatisticsManager.addStatisticsAfterScan( TEST_REPO_ID, startTime1, stats1.getScanEndTime(), 56345, + 45 ); + repositoryStatisticsManager.addStatisticsAfterScan( TEST_REPO_ID, startTime2, stats2.getScanEndTime(), 56345, + 45 ); + + assertNotNull( repositoryStatisticsManager.getLastStatistics( TEST_REPO_ID ) ); + + repositoryStatisticsManager.deleteStatistics( TEST_REPO_ID ); + + assertNull( repositoryStatisticsManager.getLastStatistics( TEST_REPO_ID ) ); + + metadataRepositoryControl.verify(); + } + + public void testDeleteStatsWhenEmpty() + { + metadataRepositoryControl.expectAndReturn( + metadataRepository.getMetadataFacets( TEST_REPO_ID, RepositoryStatistics.FACET_ID ), + Collections.emptyList(), 2 ); + metadataRepository.removeMetadataFacets( TEST_REPO_ID, RepositoryStatistics.FACET_ID ); + + metadataRepositoryControl.replay(); + + assertNull( repositoryStatisticsManager.getLastStatistics( TEST_REPO_ID ) ); + + repositoryStatisticsManager.deleteStatistics( TEST_REPO_ID ); + + assertNull( repositoryStatisticsManager.getLastStatistics( TEST_REPO_ID ) ); + + metadataRepositoryControl.verify(); + } + + public void testGetStatsRangeInside() + { + walkRepository( 3 ); + + Date current = new Date(); + + addStats( new Date( current.getTime() - 12345 ), new Date( current.getTime() - 6000 ) ); + addStats( new Date( current.getTime() - 3000 ), new Date( current.getTime() - 2000 ) ); + addStats( new Date( current.getTime() - 1000 ), current ); + + ArrayList keys = new ArrayList( statsCreated.keySet() ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getMetadataFacets( TEST_REPO_ID, RepositoryStatistics.FACET_ID ), keys ); + + // only match the middle one + String key = keys.get( 1 ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getMetadataFacet( TEST_REPO_ID, RepositoryStatistics.FACET_ID, key ), + statsCreated.get( key ) ); + + metadataRepositoryControl.replay(); + + for ( RepositoryStatistics stats : statsCreated.values() ) + { + repositoryStatisticsManager.addStatisticsAfterScan( TEST_REPO_ID, stats.getScanStartTime(), + stats.getScanEndTime(), 56345, 45 ); + } + + List list = + repositoryStatisticsManager.getStatisticsInRange( TEST_REPO_ID, new Date( current.getTime() - 4000 ), + new Date( current.getTime() - 2000 ) ); + + assertEquals( 1, list.size() ); + assertEquals( new Date( current.getTime() - 3000 ), list.get( 0 ).getScanStartTime() ); + + metadataRepositoryControl.verify(); + } + + public void testGetStatsRangeUpperOutside() + { + walkRepository( 3 ); + + Date current = new Date(); + + addStats( new Date( current.getTime() - 12345 ), new Date( current.getTime() - 6000 ) ); + addStats( new Date( current.getTime() - 3000 ), new Date( current.getTime() - 2000 ) ); + addStats( new Date( current.getTime() - 1000 ), current ); + + ArrayList keys = new ArrayList( statsCreated.keySet() ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getMetadataFacets( TEST_REPO_ID, RepositoryStatistics.FACET_ID ), keys ); + + String key = keys.get( 1 ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getMetadataFacet( TEST_REPO_ID, RepositoryStatistics.FACET_ID, key ), + statsCreated.get( key ) ); + key = keys.get( 2 ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getMetadataFacet( TEST_REPO_ID, RepositoryStatistics.FACET_ID, key ), + statsCreated.get( key ) ); + + metadataRepositoryControl.replay(); + + for ( RepositoryStatistics stats : statsCreated.values() ) + { + repositoryStatisticsManager.addStatisticsAfterScan( TEST_REPO_ID, stats.getScanStartTime(), + stats.getScanEndTime(), 56345, 45 ); + } + + List list = + repositoryStatisticsManager.getStatisticsInRange( TEST_REPO_ID, new Date( current.getTime() - 4000 ), + current ); + + assertEquals( 2, list.size() ); + assertEquals( new Date( current.getTime() - 3000 ), list.get( 1 ).getScanStartTime() ); + assertEquals( new Date( current.getTime() - 1000 ), list.get( 0 ).getScanStartTime() ); + + metadataRepositoryControl.verify(); + } + + public void testGetStatsRangeLowerOutside() + { + walkRepository( 3 ); + + Date current = new Date(); + + addStats( new Date( current.getTime() - 12345 ), new Date( current.getTime() - 6000 ) ); + addStats( new Date( current.getTime() - 3000 ), new Date( current.getTime() - 2000 ) ); + addStats( new Date( current.getTime() - 1000 ), current ); + + ArrayList keys = new ArrayList( statsCreated.keySet() ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getMetadataFacets( TEST_REPO_ID, RepositoryStatistics.FACET_ID ), keys ); + + String key = keys.get( 0 ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getMetadataFacet( TEST_REPO_ID, RepositoryStatistics.FACET_ID, key ), + statsCreated.get( key ) ); + key = keys.get( 1 ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getMetadataFacet( TEST_REPO_ID, RepositoryStatistics.FACET_ID, key ), + statsCreated.get( key ) ); + + metadataRepositoryControl.replay(); + + for ( RepositoryStatistics stats : statsCreated.values() ) + { + repositoryStatisticsManager.addStatisticsAfterScan( TEST_REPO_ID, stats.getScanStartTime(), + stats.getScanEndTime(), 56345, 45 ); + } + + List list = + repositoryStatisticsManager.getStatisticsInRange( TEST_REPO_ID, new Date( current.getTime() - 20000 ), + new Date( current.getTime() - 2000 ) ); + + assertEquals( 2, list.size() ); + assertEquals( new Date( current.getTime() - 12345 ), list.get( 1 ).getScanStartTime() ); + assertEquals( new Date( current.getTime() - 3000 ), list.get( 0 ).getScanStartTime() ); + + metadataRepositoryControl.verify(); + } + + public void testGetStatsRangeLowerAndUpperOutside() + { + walkRepository( 3 ); + + Date current = new Date(); + + addStats( new Date( current.getTime() - 12345 ), new Date( current.getTime() - 6000 ) ); + addStats( new Date( current.getTime() - 3000 ), new Date( current.getTime() - 2000 ) ); + addStats( new Date( current.getTime() - 1000 ), current ); + + ArrayList keys = new ArrayList( statsCreated.keySet() ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getMetadataFacets( TEST_REPO_ID, RepositoryStatistics.FACET_ID ), keys ); + + String key = keys.get( 0 ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getMetadataFacet( TEST_REPO_ID, RepositoryStatistics.FACET_ID, key ), + statsCreated.get( key ) ); + key = keys.get( 1 ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getMetadataFacet( TEST_REPO_ID, RepositoryStatistics.FACET_ID, key ), + statsCreated.get( key ) ); + key = keys.get( 2 ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getMetadataFacet( TEST_REPO_ID, RepositoryStatistics.FACET_ID, key ), + statsCreated.get( key ) ); + + metadataRepositoryControl.replay(); + + for ( RepositoryStatistics stats : statsCreated.values() ) + { + repositoryStatisticsManager.addStatisticsAfterScan( TEST_REPO_ID, stats.getScanStartTime(), + stats.getScanEndTime(), 56345, 45 ); + } + + List list = + repositoryStatisticsManager.getStatisticsInRange( TEST_REPO_ID, new Date( current.getTime() - 20000 ), + current ); + + assertEquals( 3, list.size() ); + assertEquals( new Date( current.getTime() - 12345 ), list.get( 2 ).getScanStartTime() ); + assertEquals( new Date( current.getTime() - 3000 ), list.get( 1 ).getScanStartTime() ); + assertEquals( new Date( current.getTime() - 1000 ), list.get( 0 ).getScanStartTime() ); + + metadataRepositoryControl.verify(); + } + + public void testGetStatsRangeNotInside() + { + walkRepository( 3 ); + + Date current = new Date(); + + addStats( new Date( current.getTime() - 12345 ), new Date( current.getTime() - 6000 ) ); + addStats( new Date( current.getTime() - 3000 ), new Date( current.getTime() - 2000 ) ); + addStats( new Date( current.getTime() - 1000 ), current ); + + ArrayList keys = new ArrayList( statsCreated.keySet() ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getMetadataFacets( TEST_REPO_ID, RepositoryStatistics.FACET_ID ), keys ); + + metadataRepositoryControl.replay(); + + for ( RepositoryStatistics stats : statsCreated.values() ) + { + repositoryStatisticsManager.addStatisticsAfterScan( TEST_REPO_ID, stats.getScanStartTime(), + stats.getScanEndTime(), 56345, 45 ); + } + + List list = + repositoryStatisticsManager.getStatisticsInRange( TEST_REPO_ID, new Date( current.getTime() - 20000 ), + new Date( current.getTime() - 16000 ) ); + + assertEquals( 0, list.size() ); + + metadataRepositoryControl.verify(); + } + + private void addStats( Date startTime, Date endTime ) + { + RepositoryStatistics stats = createTestStats( startTime, endTime ); + metadataRepository.addMetadataFacet( TEST_REPO_ID, stats ); + statsCreated.put( stats.getName(), stats ); + } + + private ArtifactMetadata createArtifact( String namespace, String projectId, String projectVersion, String type ) + { + ArtifactMetadata metadata = new ArtifactMetadata(); + metadata.setRepositoryId( TEST_REPO_ID ); + metadata.setId( projectId + "-" + projectVersion + "." + type ); + metadata.setProject( projectId ); + metadata.setSize( 12345L ); + metadata.setVersion( projectVersion ); + metadata.setNamespace( namespace ); + return metadata; + } + + private RepositoryStatistics createTestStats( Date startTime, Date endTime ) + { + RepositoryStatistics stats = new RepositoryStatistics(); + stats.setScanStartTime( startTime ); + stats.setScanEndTime( endTime ); + stats.setTotalArtifactFileSize( 20 * 12345L ); + stats.setNewFileCount( 45 ); + stats.setTotalArtifactCount( 20 ); + stats.setTotalProjectCount( 5 ); + stats.setTotalGroupCount( 4 ); + stats.setTotalFileCount( 56345 ); + stats.setTotalCountForType( "jar", 10 ); + stats.setTotalCountForType( "pom", 10 ); + return stats; + } + + private void walkRepository( int count ) + { + for ( int i = 0; i < count; i++ ) + { + metadataRepositoryControl.expectAndReturn( metadataRepository.getRootNamespaces( TEST_REPO_ID ), + Arrays.asList( "com", "org" ) ); + metadataRepositoryControl.expectAndReturn( metadataRepository.getProjects( TEST_REPO_ID, "com" ), + Arrays.asList() ); + metadataRepositoryControl.expectAndReturn( metadataRepository.getNamespaces( TEST_REPO_ID, "com" ), + Arrays.asList( "example" ) ); + metadataRepositoryControl.expectAndReturn( metadataRepository.getNamespaces( TEST_REPO_ID, "com.example" ), + Arrays.asList() ); + metadataRepositoryControl.expectAndReturn( metadataRepository.getProjects( TEST_REPO_ID, "com.example" ), + Arrays.asList( "example-project" ) ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getProjectVersions( TEST_REPO_ID, "com.example", "example-project" ), + Arrays.asList( "1.0", "1.1" ) ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getArtifacts( TEST_REPO_ID, "com.example", "example-project", "1.0" ), + Arrays.asList( createArtifact( "com.example", "example-project", "1.0", "jar" ), + createArtifact( "com.example", "example-project", "1.0", "pom" ) ) ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getArtifacts( TEST_REPO_ID, "com.example", "example-project", "1.1" ), + Arrays.asList( createArtifact( "com.example", "example-project", "1.1", "jar" ), + createArtifact( "com.example", "example-project", "1.1", "pom" ) ) ); + metadataRepositoryControl.expectAndReturn( metadataRepository.getNamespaces( TEST_REPO_ID, "org" ), + Arrays.asList( "apache", "codehaus" ) ); + metadataRepositoryControl.expectAndReturn( metadataRepository.getNamespaces( TEST_REPO_ID, "org.apache" ), + Arrays.asList( "archiva", "maven" ) ); + metadataRepositoryControl.expectAndReturn( metadataRepository.getProjects( TEST_REPO_ID, "org.apache" ), + Arrays.asList() ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getNamespaces( TEST_REPO_ID, "org.apache.archiva" ), Arrays.asList() ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getProjects( TEST_REPO_ID, "org.apache.archiva" ), + Arrays.asList( "metadata-repository-api", "metadata-model" ) ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getProjectVersions( TEST_REPO_ID, "org.apache.archiva", "metadata-repository-api" ), + Arrays.asList( "1.3-SNAPSHOT", "1.3" ) ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getArtifacts( TEST_REPO_ID, "org.apache.archiva", "metadata-repository-api", + "1.3-SNAPSHOT" ), + Arrays.asList( createArtifact( "org.apache.archiva", "metadata-repository-api", "1.3-SNAPSHOT", "jar" ), + createArtifact( "org.apache.archiva", "metadata-repository-api", "1.3-SNAPSHOT", + "pom" ) ) ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getArtifacts( TEST_REPO_ID, "org.apache.archiva", "metadata-repository-api", "1.3" ), + Arrays.asList( createArtifact( "org.apache.archiva", "metadata-repository-api", "1.3", "jar" ), + createArtifact( "org.apache.archiva", "metadata-repository-api", "1.3", "pom" ) ) ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getProjectVersions( TEST_REPO_ID, "org.apache.archiva", "metadata-model" ), + Arrays.asList( "1.3-SNAPSHOT", "1.3" ) ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getArtifacts( TEST_REPO_ID, "org.apache.archiva", "metadata-model", "1.3-SNAPSHOT" ), + Arrays.asList( createArtifact( "org.apache.archiva", "metadata-model", "1.3-SNAPSHOT", "jar" ), - createArtifact( "org.apache.archiva", "metadata-model", "1.3-SNAPSHOT", - "pom" ) ) ); ++ createArtifact( "org.apache.archiva", "metadata-model", "1.3-SNAPSHOT", "pom" ) ) ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getArtifacts( TEST_REPO_ID, "org.apache.archiva", "metadata-model", "1.3" ), + Arrays.asList( createArtifact( "org.apache.archiva", "metadata-model", "1.3", "jar" ), + createArtifact( "org.apache.archiva", "metadata-model", "1.3", "pom" ) ) ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getNamespaces( TEST_REPO_ID, "org.apache.maven" ), Arrays.asList() ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getProjects( TEST_REPO_ID, "org.apache.maven" ), Arrays.asList( "maven-model" ) ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getProjectVersions( TEST_REPO_ID, "org.apache.maven", "maven-model" ), + Arrays.asList( "2.2.1" ) ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getArtifacts( TEST_REPO_ID, "org.apache.maven", "maven-model", "2.2.1" ), + Arrays.asList( createArtifact( "org.apache.archiva", "maven-model", "2.2.1", "jar" ), + createArtifact( "org.apache.archiva", "maven-model", "2.2.1", "pom" ) ) ); + metadataRepositoryControl.expectAndReturn( metadataRepository.getNamespaces( TEST_REPO_ID, "org.codehaus" ), + Arrays.asList( "plexus" ) ); + metadataRepositoryControl.expectAndReturn( metadataRepository.getProjects( TEST_REPO_ID, "org" ), + Arrays.asList() ); + metadataRepositoryControl.expectAndReturn( metadataRepository.getProjects( TEST_REPO_ID, "org.codehaus" ), + Arrays.asList() ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getNamespaces( TEST_REPO_ID, "org.codehaus.plexus" ), Arrays.asList() ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getProjects( TEST_REPO_ID, "org.codehaus.plexus" ), + Arrays.asList( "plexus-spring" ) ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getProjectVersions( TEST_REPO_ID, "org.codehaus.plexus", "plexus-spring" ), + Arrays.asList( "1.0", "1.1", "1.2" ) ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getArtifacts( TEST_REPO_ID, "org.codehaus.plexus", "plexus-spring", "1.0" ), + Arrays.asList( createArtifact( "org.codehaus.plexus", "plexus-spring", "1.0", "jar" ), + createArtifact( "org.codehaus.plexus", "plexus-spring", "1.0", "pom" ) ) ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getArtifacts( TEST_REPO_ID, "org.codehaus.plexus", "plexus-spring", "1.1" ), + Arrays.asList( createArtifact( "org.codehaus.plexus", "plexus-spring", "1.1", "jar" ), + createArtifact( "org.codehaus.plexus", "plexus-spring", "1.1", "pom" ) ) ); + metadataRepositoryControl.expectAndReturn( + metadataRepository.getArtifacts( TEST_REPO_ID, "org.codehaus.plexus", "plexus-spring", "1.2" ), + Arrays.asList( createArtifact( "org.codehaus.plexus", "plexus-spring", "1.2", "jar" ), + createArtifact( "org.codehaus.plexus", "plexus-spring", "1.2", "pom" ) ) ); + } + } +} diff --cc pom.xml index 1946c7382,1b8bba034..c7e880d6e --- a/pom.xml +++ b/pom.xml @@@ -284,36 -290,6 +286,41 @@@ activation 1.1 + + org.apache.archiva + metadata-model - 1.3-SNAPSHOT ++ 1.4-SNAPSHOT + + + org.apache.archiva + metadata-repository-api - 1.3-SNAPSHOT ++ 1.4-SNAPSHOT + + + org.apache.archiva + metadata-repository-file - 1.3-SNAPSHOT ++ 1.4-SNAPSHOT + + + org.apache.archiva + repository-statistics - 1.3-SNAPSHOT ++ 1.4-SNAPSHOT + + + org.apache.archiva + problem-reports - 1.3-SNAPSHOT ++ 1.4-SNAPSHOT ++ ++ ++ org.apache.archiva ++ audit ++ 1.4-SNAPSHOT + + + org.apache.archiva + maven2-repository - 1.3-SNAPSHOT ++ 1.4-SNAPSHOT + org.apache.archiva archiva-applet @@@ -352,7 -338,17 +369,22 @@@ org.apache.archiva archiva-core-consumers - 1.3-SNAPSHOT + 1.4-SNAPSHOT + ++ ++ org.apache.archiva ++ archiva-metadata-consumer ++ 1.4-SNAPSHOT ++ + + org.apache.archiva + archiva-database + 1.4-SNAPSHOT + + + org.apache.archiva + archiva-database-consumers + 1.4-SNAPSHOT org.apache.archiva @@@ -381,28 -377,18 +413,28 @@@ org.apache.archiva - archiva-report-manager + archiva-repository-layer - 1.3-SNAPSHOT + 1.4-SNAPSHOT org.apache.archiva - archiva-repository-layer + archiva-repository-scanner - 1.3-SNAPSHOT ++ 1.4-SNAPSHOT + + + org.apache.archiva + archiva-scheduler-api - 1.3-SNAPSHOT + 1.4-SNAPSHOT org.apache.archiva - archiva-scheduled + archiva-scheduler-repository - 1.3-SNAPSHOT ++ 1.4-SNAPSHOT + + + org.apache.archiva + archiva-scheduler-indexing - 1.3-SNAPSHOT + 1.4-SNAPSHOT org.apache.archiva @@@ -1103,9 -1100,8 +1135,9 @@@ 2.0.8 + 3.0-alpha-4 1.0-beta-5 - 1.2.2 + 1.2.3 6.1.19 1.5.8 0.9