001package org.apache.archiva.metadata.model; 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.checksum.ChecksumAlgorithm; 023import org.apache.commons.collections4.bidimap.DualHashBidiMap; 024 025import javax.xml.bind.annotation.XmlRootElement; 026import java.time.Instant; 027import java.time.ZonedDateTime; 028import java.time.temporal.ChronoUnit; 029import java.time.temporal.TemporalUnit; 030import java.util.Map; 031import java.util.Set; 032import java.util.stream.Collectors; 033 034/** 035 * Metadata stored in the content repository for a particular artifact. Information that is shared between different 036 * artifacts of a given project version can be found in the 037 * {@link org.apache.archiva.metadata.model.ProjectVersionMetadata} class. The metadata is faceted to store information 038 * about particular types of artifacts, for example Maven 2.x artifact specific information. 039 * For more information, see the 040 * <a href="{@docRoot}/../metadata-content-model.html" target="_top">Metadata Content Model</a>. 041 */ 042@XmlRootElement(name = "artifactMetadata") 043public class ArtifactMetadata 044 extends FacetedMetadata { 045 046 047 /** 048 * The artifact ID uniquely identifies an artifact within a given namespace, project and project version. For 049 * example, <tt>archiva-1.4-20100201.345612-2.jar</tt> 050 */ 051 private String id; 052 053 /** 054 * The repository that the artifact is stored in within the content repository. 055 */ 056 private String repositoryId; 057 058 /** 059 * The namespace of the project within the repository. 060 * 061 * @see ProjectMetadata#getNamespace() 062 */ 063 private String namespace; 064 065 /** 066 * The identifier of the project within the repository and namespace. 067 * 068 * @see ProjectMetadata#getId() 069 */ 070 private String project; 071 072 /** 073 * The version of the project. This may be more generalised than @{link #version}. 074 * 075 * @see ProjectVersionMetadata#getId() 076 */ 077 private String projectVersion; 078 079 /** 080 * The artifact version, if different from the project version. Note that the metadata does not do any calculation 081 * of this based on the project version - the calling code must be sure to set and check it appropriately if 082 * <tt>null</tt>. 083 */ 084 private String version; 085 086 /** 087 * The last modified date of the artifact file, if known. 088 */ 089 private ZonedDateTime fileLastModified; 090 091 /** 092 * The file size of the artifact, if known. 093 */ 094 private long size; 095 096 /** 097 * The list of checksums. 098 */ 099 private Map<ChecksumAlgorithm, String> checksums = new DualHashBidiMap<>( ); 100 101 private String toStringValue = ""; 102 private int lastHash = 0; 103 104 /** 105 * When the artifact was found in the repository storage and added to the metadata content repository. 106 */ 107 private ZonedDateTime whenGathered; 108 109 public String getId() { 110 return id; 111 } 112 113 public void setId(String id) { 114 this.id = id; 115 } 116 117 public long getSize() { 118 return size; 119 } 120 121 public void setSize(long size) { 122 this.size = size; 123 } 124 125 public String getVersion() { 126 return version; 127 } 128 129 public void setVersion(String version) { 130 this.version = version; 131 } 132 133 public String getProjectVersion() { 134 return projectVersion; 135 } 136 137 public void setProjectVersion(String projectVersion) { 138 this.projectVersion = projectVersion; 139 } 140 141 public void setFileLastModified(long fileLastModified) { 142 this.fileLastModified = ZonedDateTime.ofInstant(Instant.ofEpochMilli(fileLastModified), ModelInfo.STORAGE_TZ); 143 } 144 145 public void setWhenGathered(ZonedDateTime whenGathered) { 146 // We set the resolution to milliseconds, because it's the resolution that all current backends support 147 this.whenGathered = whenGathered.withZoneSameInstant(ModelInfo.STORAGE_TZ).truncatedTo(ChronoUnit.MILLIS); 148 } 149 150 public void setMd5(String md5) { 151 this.checksums.put(ChecksumAlgorithm.MD5, md5); 152 } 153 154 public void setSha1(String sha1) { 155 this.checksums.put(ChecksumAlgorithm.SHA1, sha1); 156 } 157 158 public ZonedDateTime getWhenGathered() { 159 return whenGathered; 160 } 161 162 public String getChecksum(ChecksumAlgorithm checksumAlgorithm) { 163 return checksums.get(checksumAlgorithm); 164 } 165 166 public void setChecksum(ChecksumAlgorithm algorithm, String checksumValue) { 167 this.checksums.put(algorithm, checksumValue); 168 } 169 170 public Set<ChecksumAlgorithm> getChecksumTypes() { 171 return checksums.keySet(); 172 } 173 174 public Map<ChecksumAlgorithm,String> getChecksums() { 175 return this.checksums; 176 } 177 178 public boolean hasChecksum(String checksum) { 179 return this.checksums.containsValue( checksum ); 180 } 181 182 public void setChecksums(Map<ChecksumAlgorithm,String> checksums) { 183 this.checksums = checksums; 184 } 185 186 public String getMd5() { 187 return checksums.get(ChecksumAlgorithm.MD5); 188 } 189 190 public String getSha1() { 191 return checksums.get(ChecksumAlgorithm.SHA1); 192 } 193 194 public ZonedDateTime getFileLastModified() { 195 196 return fileLastModified; 197 } 198 199 public String getNamespace() { 200 return namespace; 201 } 202 203 public void setNamespace(String namespace) { 204 this.namespace = namespace; 205 } 206 207 public void setProject(String project) { 208 this.project = project; 209 } 210 211 public String getProject() { 212 return project; 213 } 214 215 public String getRepositoryId() { 216 return repositoryId; 217 } 218 219 public void setRepositoryId(String repositoryId) { 220 this.repositoryId = repositoryId; 221 } 222 223 @Override 224 public boolean equals(Object o) { 225 if (this == o) { 226 return true; 227 } 228 if (o == null || getClass() != o.getClass()) { 229 return false; 230 } 231 232 ArtifactMetadata that = (ArtifactMetadata) o; 233 234 if (size != that.size) { 235 return false; 236 } 237 // Time equality by instant that means the point in time must match, but not the time zone 238 if (fileLastModified != null 239 ? !fileLastModified.toInstant().equals(that.fileLastModified.toInstant()) 240 : that.fileLastModified != null) { 241 return false; 242 } 243 if (!id.equals(that.id)) { 244 return false; 245 } 246 for ( Map.Entry<ChecksumAlgorithm, String> entry : this.checksums.entrySet()) { 247 String thatChecksum = that.checksums.get(entry.getKey()); 248 if (entry.getValue()!=null ? !entry.getValue().equals(thatChecksum) : thatChecksum!=null) { 249 return false; 250 } 251 } 252 if (namespace != null ? !namespace.equals(that.namespace) : that.namespace != null) { 253 return false; 254 } 255 if (project != null ? !project.equals(that.project) : that.project != null) { 256 return false; 257 } 258 if (projectVersion != null ? !projectVersion.equals(that.projectVersion) : that.projectVersion != null) { 259 return false; 260 } 261 /** 262 * We cannot compare in different repositories, if this is in here 263 if ( !repositoryId.equals( that.repositoryId ) ) 264 { 265 return false; 266 } 267 **/ 268 if (version != null ? !version.equals(that.version) : that.version != null) { 269 return false; 270 } 271 if (whenGathered != null ? !whenGathered.toInstant().equals(that.whenGathered.toInstant()) : that.whenGathered != null) { 272 return false; 273 } 274 275 return true; 276 } 277 278 @Override 279 public int hashCode() { 280 int result = id != null ? id.hashCode() : 0; 281 result = 31 * result + (repositoryId != null ? repositoryId.hashCode() : 0); 282 result = 31 * result + (namespace != null ? namespace.hashCode() : 0); 283 result = 31 * result + (project != null ? project.hashCode() : 0); 284 result = 31 * result + (projectVersion != null ? projectVersion.hashCode() : 0); 285 result = 31 * result + (version != null ? version.hashCode() : 0); 286 result = 31 * result + (fileLastModified != null ? fileLastModified.hashCode() : 0); 287 result = 31 * result + (int) (size ^ (size >>> 32)); 288 for (String checksum : checksums.values()) { 289 result = 31 * result + (checksum != null ? checksum.hashCode() : 0); 290 } 291 result = 31 * result + (whenGathered != null ? whenGathered.hashCode() : 0); 292 return result; 293 } 294 295 /** 296 * Doing some hashing to avoid the expensive string concatenation. 297 */ 298 @Override 299 public String toString() { 300 final int hashCode=hashCode(); 301 if (hashCode!=lastHash) { 302 toStringValue = "ArtifactMetadata{" + "id='" + id + '\'' + ", size=" + size + ", version='" + version + '\'' + 303 ", fileLastModified=" + fileLastModified + ", whenGathered=" + whenGathered + 304 ", namespace='" + namespace + '\'' + ", project='" + project + '\'' + 305 ", projectVersion='" + projectVersion + '\'' + ", repositoryId='" + repositoryId + '\'' + 306 ", checksums=" + checksums.entrySet().stream().map(e -> e.getKey() + ":" + e.getValue()).collect(Collectors.joining(",")) + 307 '}'; 308 lastHash=hashCode; 309 } 310 return toStringValue; 311 } 312}