001package org.apache.archiva.metadata.repository.stats; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import org.apache.archiva.metadata.repository.MetadataRepository; 023import org.apache.archiva.metadata.repository.MetadataRepositoryException; 024import org.apache.archiva.metadata.repository.RepositorySession; 025import org.apache.archiva.metadata.repository.RepositorySessionFactory; 026import org.apache.archiva.metadata.repository.stats.model.DefaultRepositoryStatistics; 027import org.apache.archiva.metadata.repository.stats.model.RepositoryStatistics; 028import org.apache.archiva.metadata.repository.stats.model.RepositoryStatisticsManager; 029import org.apache.archiva.metadata.repository.stats.model.RepositoryStatisticsProvider; 030import org.apache.archiva.metadata.repository.stats.model.RepositoryWalkingStatisticsProvider; 031import org.apache.commons.lang3.time.StopWatch; 032import org.slf4j.Logger; 033import org.slf4j.LoggerFactory; 034import org.springframework.stereotype.Service; 035 036import javax.inject.Inject; 037import java.text.ParseException; 038import java.text.SimpleDateFormat; 039import java.util.ArrayList; 040import java.util.Collections; 041import java.util.Date; 042import java.util.List; 043import java.util.TimeZone; 044 045/** 046 * 047 */ 048@Service("repositoryStatisticsManager#default") 049public class DefaultRepositoryStatisticsManager 050 implements RepositoryStatisticsManager 051{ 052 private static final Logger log = LoggerFactory.getLogger( DefaultRepositoryStatisticsManager.class ); 053 054 private static final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone( "UTC" ); 055 056 private RepositoryWalkingStatisticsProvider walkingProvider = new RepositoryWalkingStatisticsProvider(); 057 058 059 060 @Inject 061 RepositorySessionFactory repositorySessionFactory; 062 063 @Override 064 public boolean hasStatistics( String repositoryId ) 065 throws MetadataRepositoryException 066 { 067 try(RepositorySession session = repositorySessionFactory.createSession()) { 068 final MetadataRepository metadataRepository = session.getRepository( ); 069 return metadataRepository.hasMetadataFacet(session, repositoryId, DefaultRepositoryStatistics.FACET_ID); 070 } 071 } 072 073 @Override 074 public RepositoryStatistics getLastStatistics( String repositoryId ) 075 throws MetadataRepositoryException 076 { 077 StopWatch stopWatch = new StopWatch(); 078 stopWatch.start(); 079 try(RepositorySession session = repositorySessionFactory.createSession()) { 080 final MetadataRepository metadataRepository = session.getRepository( ); 081 082 // TODO: consider a more efficient implementation that directly gets the last one from the content repository 083 List<String> scans = metadataRepository.getMetadataFacets(session, repositoryId, DefaultRepositoryStatistics.FACET_ID); 084 if (scans == null) { 085 return null; 086 } 087 Collections.sort(scans); 088 if (!scans.isEmpty()) { 089 String name = scans.get(scans.size() - 1); 090 RepositoryStatistics repositoryStatistics = 091 RepositoryStatistics.class.cast(metadataRepository.getMetadataFacet(session, repositoryId, 092 RepositoryStatistics.FACET_ID, name)); 093 stopWatch.stop(); 094 log.debug("time to find last RepositoryStatistics: {} ms", stopWatch.getTime()); 095 return repositoryStatistics; 096 } else { 097 return null; 098 } 099 } 100 } 101 102 @Override 103 public void addStatisticsAfterScan( String repositoryId, Date startTime, 104 Date endTime, long totalFiles, long newFiles ) 105 throws MetadataRepositoryException 106 { 107 try(RepositorySession session = repositorySessionFactory.createSession()) { 108 final MetadataRepository metadataRepository = session.getRepository( ); 109 110 DefaultRepositoryStatistics repositoryStatistics = new DefaultRepositoryStatistics(); 111 repositoryStatistics.setRepositoryId(repositoryId); 112 repositoryStatistics.setScanStartTime(startTime); 113 repositoryStatistics.setScanEndTime(endTime); 114 repositoryStatistics.setTotalFileCount(totalFiles); 115 repositoryStatistics.setNewFileCount(newFiles); 116 117 // TODO 118 // In the future, instead of being tied to a scan we might want to record information in the fly based on 119 // events that are occurring. Even without these totals we could query much of the information on demand based 120 // on information from the metadata content repository. In the mean time, we lock information in at scan time. 121 // Note that if new types are later discoverable due to a code change or new plugin, historical stats will not 122 // be updated and the repository will need to be rescanned. 123 124 long startGather = System.currentTimeMillis(); 125 126 if (metadataRepository instanceof RepositoryStatisticsProvider) { 127 ((RepositoryStatisticsProvider) metadataRepository).populateStatistics(session, 128 metadataRepository, repositoryId, repositoryStatistics); 129 } else { 130 walkingProvider.populateStatistics(session, metadataRepository, repositoryId, repositoryStatistics); 131 } 132 133 log.info("Gathering statistics executed in {} ms", (System.currentTimeMillis() - startGather)); 134 135 metadataRepository.addMetadataFacet(session, repositoryId, repositoryStatistics); 136 } 137 } 138 139 @Override 140 public void deleteStatistics( String repositoryId ) 141 throws MetadataRepositoryException 142 { 143 try(RepositorySession session = repositorySessionFactory.createSession()) { 144 final MetadataRepository metadataRepository = session.getRepository( ); 145 metadataRepository.removeMetadataFacets(session, repositoryId, DefaultRepositoryStatistics.FACET_ID); 146 } 147 } 148 149 @Override 150 public List<RepositoryStatistics> getStatisticsInRange( String repositoryId, 151 Date startTime, Date endTime ) 152 throws MetadataRepositoryException 153 { 154 try(RepositorySession session = repositorySessionFactory.createSession()) { 155 final MetadataRepository metadataRepository = session.getRepository( ); 156 List<RepositoryStatistics> results = new ArrayList<>(); 157 List<String> list = metadataRepository.getMetadataFacets(session, repositoryId, DefaultRepositoryStatistics.FACET_ID); 158 Collections.sort(list, Collections.reverseOrder()); 159 for (String name : list) { 160 try { 161 Date date = createNameFormat().parse(name); 162 if ((startTime == null || !date.before(startTime)) && (endTime == null || !date.after( 163 endTime))) { 164 RepositoryStatistics stats = 165 (RepositoryStatistics) metadataRepository.getMetadataFacet(session, 166 repositoryId, 167 DefaultRepositoryStatistics.FACET_ID, name); 168 results.add(stats); 169 } 170 } catch (ParseException e) { 171 log.error("Invalid scan result found in the metadata repository: {}", e.getMessage()); 172 // continue and ignore this one 173 } 174 } 175 return results; 176 } 177 } 178 179 private static SimpleDateFormat createNameFormat() 180 { 181 SimpleDateFormat fmt = new SimpleDateFormat( DefaultRepositoryStatistics.SCAN_TIMESTAMP_FORMAT ); 182 fmt.setTimeZone( UTC_TIME_ZONE ); 183 return fmt; 184 } 185 186 public RepositorySessionFactory getRepositorySessionFactory( ) 187 { 188 return repositorySessionFactory; 189 } 190 191 public void setRepositorySessionFactory( RepositorySessionFactory repositorySessionFactory ) 192 { 193 this.repositorySessionFactory = repositorySessionFactory; 194 } 195}