001 /* 002 * Sonar, open source software quality management tool. 003 * Copyright (C) 2008-2012 SonarSource 004 * mailto:contact AT sonarsource DOT com 005 * 006 * Sonar is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * Sonar is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with Sonar; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 019 */ 020 package org.sonar.batch.index; 021 022 import com.google.common.collect.Maps; 023 import org.apache.commons.lang.ObjectUtils; 024 import org.apache.commons.lang.StringUtils; 025 import org.sonar.api.database.DatabaseSession; 026 import org.sonar.api.database.model.ResourceModel; 027 import org.sonar.api.database.model.Snapshot; 028 import org.sonar.api.resources.*; 029 import org.sonar.api.utils.SonarException; 030 031 import javax.persistence.NonUniqueResultException; 032 import javax.persistence.Query; 033 import java.util.Date; 034 import java.util.Iterator; 035 import java.util.List; 036 import java.util.Map; 037 038 public final class DefaultResourcePersister implements ResourcePersister { 039 040 private DatabaseSession session; 041 042 private Map<Resource, Snapshot> snapshotsByResource = Maps.newHashMap(); 043 044 public DefaultResourcePersister(DatabaseSession session) { 045 this.session = session; 046 } 047 048 public Snapshot saveProject(Project project, Project parent) { 049 Snapshot snapshot = snapshotsByResource.get(project); 050 if (snapshot == null) { 051 snapshot = persistProject(project, parent); 052 addToCache(project, snapshot); 053 } 054 return snapshot; 055 } 056 057 private void addToCache(Resource resource, Snapshot snapshot) { 058 if (snapshot != null) { 059 snapshotsByResource.put(resource, snapshot); 060 } 061 } 062 063 private Snapshot persistProject(Project project, Project parent) { 064 // temporary hack 065 project.setEffectiveKey(project.getKey()); 066 067 ResourceModel model = findOrCreateModel(project); 068 model.setLanguageKey(project.getLanguageKey());// ugly, only for projects 069 070 Snapshot parentSnapshot = null; 071 if (parent != null) { 072 // assume that the parent project has already been saved 073 parentSnapshot = snapshotsByResource.get(project.getParent()); 074 model.setRootId((Integer) ObjectUtils.defaultIfNull(parentSnapshot.getRootProjectId(), parentSnapshot.getResourceId())); 075 } else { 076 model.setRootId(null); 077 } 078 model = session.save(model); 079 project.setId(model.getId()); 080 081 Snapshot snapshot = new Snapshot(model, parentSnapshot); 082 snapshot.setVersion(project.getAnalysisVersion()); 083 snapshot.setCreatedAt(project.getAnalysisDate()); 084 snapshot.setBuildDate(new Date()); 085 snapshot = session.save(snapshot); 086 session.commit(); 087 return snapshot; 088 } 089 090 public Snapshot getSnapshot(Resource reference) { 091 return snapshotsByResource.get(reference); 092 } 093 094 public Snapshot getSnapshotOrFail(Resource resource) { 095 Snapshot snapshot = getSnapshot(resource); 096 if (snapshot == null) { 097 throw new ResourceNotPersistedException(resource); 098 } 099 return snapshot; 100 } 101 102 /** 103 * just for unit tests 104 */ 105 Map<Resource, Snapshot> getSnapshotsByResource() { 106 return snapshotsByResource; 107 } 108 109 110 public Snapshot saveResource(Project project, Resource resource) { 111 return saveResource(project, resource, null); 112 } 113 114 public Snapshot saveResource(Project project, Resource resource, Resource parent) { 115 Snapshot snapshot = snapshotsByResource.get(resource); 116 if (snapshot == null) { 117 snapshot = persist(project, resource, parent); 118 addToCache(resource, snapshot); 119 } 120 return snapshot; 121 } 122 123 124 private Snapshot persist(Project project, Resource resource, Resource parent) { 125 Snapshot snapshot; 126 if (resource instanceof Project) { 127 // should not occur, please use the method saveProject() 128 snapshot = persistProject((Project) resource, project); 129 130 } else if (resource instanceof Library) { 131 snapshot = persistLibrary(project, (Library) resource); 132 133 } else { 134 snapshot = persistFileOrDirectory(project, resource, parent); 135 } 136 137 return snapshot; 138 } 139 140 141 private Snapshot persistLibrary(Project project, Library library) { 142 ResourceModel model = findOrCreateModel(library); 143 model = session.save(model); 144 library.setId(model.getId()); // TODO to be removed 145 library.setEffectiveKey(library.getKey()); 146 147 Snapshot snapshot = findLibrarySnapshot(model.getId(), library.getVersion()); 148 if (snapshot == null) { 149 snapshot = new Snapshot(model, null); 150 snapshot.setCreatedAt(project.getAnalysisDate()); 151 snapshot.setBuildDate(new Date()); 152 snapshot.setVersion(library.getVersion()); 153 snapshot.setStatus(Snapshot.STATUS_PROCESSED); 154 155 // see http://jira.codehaus.org/browse/SONAR-1850 156 // The qualifier must be LIB, even if the resource is TRK, because this snapshot has no measures. 157 snapshot.setQualifier(Qualifiers.LIBRARY); 158 snapshot = session.save(snapshot); 159 } 160 session.commit(); 161 return snapshot; 162 } 163 164 private Snapshot findLibrarySnapshot(Integer resourceId, String version) { 165 Query query = session.createQuery("from " + Snapshot.class.getSimpleName() + 166 " s WHERE s.resourceId=:resourceId AND s.version=:version AND s.scope=:scope AND s.qualifier<>:qualifier AND s.last=:last"); 167 query.setParameter("resourceId", resourceId); 168 query.setParameter("version", version); 169 query.setParameter("scope", Scopes.PROJECT); 170 query.setParameter("qualifier", Qualifiers.LIBRARY); 171 query.setParameter("last", Boolean.TRUE); 172 List<Snapshot> snapshots = query.getResultList(); 173 if (snapshots.isEmpty()) { 174 snapshots = session.getResults(Snapshot.class, "resourceId", resourceId, "version", version, "scope", Scopes.PROJECT, "qualifier", Qualifiers.LIBRARY); 175 } 176 return (snapshots.isEmpty() ? null : snapshots.get(0)); 177 } 178 179 /** 180 * Everything except project and library 181 */ 182 private Snapshot persistFileOrDirectory(Project project, Resource resource, Resource parentReference) { 183 ResourceModel model = findOrCreateModel(resource); 184 Snapshot projectSnapshot = snapshotsByResource.get(project); 185 model.setRootId(projectSnapshot.getResourceId()); 186 model = session.save(model); 187 resource.setId(model.getId()); 188 189 Snapshot parentSnapshot = (Snapshot) ObjectUtils.defaultIfNull(getSnapshot(parentReference), projectSnapshot); 190 Snapshot snapshot = new Snapshot(model, parentSnapshot); 191 snapshot.setBuildDate(new Date()); 192 snapshot = session.save(snapshot); 193 session.commit(); 194 return snapshot; 195 } 196 197 public Snapshot getLastSnapshot(Snapshot snapshot, boolean onlyOlder) { 198 String hql = "SELECT s FROM " + Snapshot.class.getSimpleName() + " s WHERE s.last=:last AND s.resourceId=:resourceId"; 199 if (onlyOlder) { 200 hql += " AND s.createdAt<:date"; 201 } 202 Query query = session.createQuery(hql); 203 query.setParameter("last", true); 204 query.setParameter("resourceId", snapshot.getResourceId()); 205 if (onlyOlder) { 206 query.setParameter("date", snapshot.getCreatedAt()); 207 } 208 return session.getSingleResult(query, null); 209 } 210 211 public void clear() { 212 // we keep cache of projects 213 for (Iterator<Map.Entry<Resource, Snapshot>> it = snapshotsByResource.entrySet().iterator(); it.hasNext(); ) { 214 Map.Entry<Resource, Snapshot> entry = it.next(); 215 if (!ResourceUtils.isSet(entry.getKey())) { 216 it.remove(); 217 } 218 } 219 } 220 221 private ResourceModel findOrCreateModel(Resource resource) { 222 ResourceModel model; 223 try { 224 model = session.getSingleResult(ResourceModel.class, "key", resource.getEffectiveKey()); 225 if (model == null) { 226 model = createModel(resource); 227 228 } else { 229 mergeModel(model, resource); 230 } 231 return model; 232 233 } catch (NonUniqueResultException e) { 234 throw new SonarException("The resource '" + resource.getEffectiveKey() + "' is duplicated in database.", e); 235 } 236 } 237 238 static ResourceModel createModel(Resource resource) { 239 ResourceModel model = new ResourceModel(); 240 model.setEnabled(Boolean.TRUE); 241 model.setDescription(resource.getDescription()); 242 model.setKey(resource.getEffectiveKey()); 243 if (resource.getLanguage() != null) { 244 model.setLanguageKey(resource.getLanguage().getKey()); 245 } 246 if (StringUtils.isNotBlank(resource.getName())) { 247 model.setName(resource.getName()); 248 } else { 249 model.setName(resource.getKey()); 250 } 251 model.setLongName(resource.getLongName()); 252 model.setScope(resource.getScope()); 253 model.setQualifier(resource.getQualifier()); 254 return model; 255 } 256 257 static void mergeModel(ResourceModel model, Resource resource) { 258 model.setEnabled(true); 259 if (StringUtils.isNotBlank(resource.getName())) { 260 model.setName(resource.getName()); 261 } 262 if (StringUtils.isNotBlank(resource.getLongName())) { 263 model.setLongName(resource.getLongName()); 264 } 265 if (StringUtils.isNotBlank(resource.getDescription())) { 266 model.setDescription(resource.getDescription()); 267 } 268 if (!ResourceUtils.isLibrary(resource)) { 269 model.setScope(resource.getScope()); 270 model.setQualifier(resource.getQualifier()); 271 } 272 if (resource.getLanguage() != null) { 273 model.setLanguageKey(resource.getLanguage().getKey()); 274 } 275 } 276 }