001 /* 002 * Sonar, open source software quality management tool. 003 * Copyright (C) 2008-2011 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 034 import org.sonar.api.resources.Qualifiers; 035 036 import org.sonar.api.resources.Scopes; 037 038 import java.util.Iterator; 039 import java.util.List; 040 import java.util.Map; 041 042 public final class DefaultResourcePersister implements ResourcePersister { 043 044 private DatabaseSession session; 045 046 private Map<Resource, Snapshot> snapshotsByResource = Maps.newHashMap(); 047 048 public DefaultResourcePersister(DatabaseSession session) { 049 this.session = session; 050 } 051 052 public Snapshot saveProject(Project project, Project parent) { 053 Snapshot snapshot = snapshotsByResource.get(project); 054 if (snapshot == null) { 055 snapshot = persistProject(project, parent); 056 addToCache(project, snapshot); 057 } 058 return snapshot; 059 } 060 061 private void addToCache(Resource resource, Snapshot snapshot) { 062 if (snapshot != null) { 063 snapshotsByResource.put(resource, snapshot); 064 } 065 } 066 067 private Snapshot persistProject(Project project, Project parent) { 068 // temporary hack 069 project.setEffectiveKey(project.getKey()); 070 071 ResourceModel model = findOrCreateModel(project); 072 model.setLanguageKey(project.getLanguageKey());// ugly, only for projects 073 074 Snapshot parentSnapshot = null; 075 if (parent != null) { 076 // assume that the parent project has already been saved 077 parentSnapshot = snapshotsByResource.get(project.getParent()); 078 model.setRootId((Integer) ObjectUtils.defaultIfNull(parentSnapshot.getRootProjectId(), parentSnapshot.getResourceId())); 079 } 080 model = session.save(model); 081 project.setId(model.getId()); // TODO to be removed 082 083 Snapshot snapshot = new Snapshot(model, parentSnapshot); 084 snapshot.setVersion(project.getAnalysisVersion()); 085 snapshot.setCreatedAt(project.getAnalysisDate()); 086 snapshot = session.save(snapshot); 087 session.commit(); 088 return snapshot; 089 } 090 091 public Snapshot getSnapshot(Resource reference) { 092 return snapshotsByResource.get(reference); 093 } 094 095 public Snapshot getSnapshotOrFail(Resource resource) throws ResourceNotPersistedException { 096 Snapshot snapshot = getSnapshot(resource); 097 if (snapshot == null) { 098 throw new ResourceNotPersistedException(resource); 099 } 100 return snapshot; 101 } 102 103 /** 104 * just for unit tests 105 */ 106 Map<Resource, Snapshot> getSnapshotsByResource() { 107 return snapshotsByResource; 108 } 109 110 111 public Snapshot saveResource(Project project, Resource resource) { 112 return saveResource(project, resource, null); 113 } 114 115 public Snapshot saveResource(Project project, Resource resource, Resource parent) { 116 Snapshot snapshot = snapshotsByResource.get(resource); 117 if (snapshot == null) { 118 snapshot = persist(project, resource, parent); 119 addToCache(resource, snapshot); 120 } 121 return snapshot; 122 } 123 124 125 private Snapshot persist(Project project, Resource resource, Resource parent) { 126 Snapshot snapshot; 127 if (resource instanceof Project) { 128 // should not occur, please use the method saveProject() 129 snapshot = persistProject((Project) resource, project); 130 131 } else if (resource instanceof Library) { 132 snapshot = persistLibrary(project, (Library) resource); 133 134 } else { 135 snapshot = persistFileOrDirectory(project, resource, parent); 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.setVersion(library.getVersion()); 152 snapshot.setStatus(Snapshot.STATUS_PROCESSED); 153 154 // see http://jira.codehaus.org/browse/SONAR-1850 155 // The qualifier must be LIB, even if the resource is TRK, because this snapshot has no measures. 156 snapshot.setQualifier(Qualifiers.LIBRARY); 157 snapshot = session.save(snapshot); 158 } 159 session.commit(); 160 return snapshot; 161 } 162 163 private Snapshot findLibrarySnapshot(Integer resourceId, String version) { 164 Query query = session.createQuery("from " + Snapshot.class.getSimpleName() + 165 " s WHERE s.resourceId=:resourceId AND s.version=:version AND s.scope=:scope AND s.qualifier<>:qualifier AND s.last=:last"); 166 query.setParameter("resourceId", resourceId); 167 query.setParameter("version", version); 168 query.setParameter("scope", Scopes.PROJECT); 169 query.setParameter("qualifier", Qualifiers.LIBRARY); 170 query.setParameter("last", Boolean.TRUE); 171 List<Snapshot> snapshots = query.getResultList(); 172 if (snapshots.isEmpty()) { 173 snapshots = session.getResults(Snapshot.class, "resourceId", resourceId, "version", version, "scope", Scopes.PROJECT, "qualifier", Qualifiers.LIBRARY); 174 } 175 return (snapshots.isEmpty() ? null : snapshots.get(0)); 176 } 177 178 /** 179 * Everything except project and library 180 */ 181 private Snapshot persistFileOrDirectory(Project project, Resource resource, Resource parentReference) { 182 ResourceModel model = findOrCreateModel(resource); 183 Snapshot projectSnapshot = snapshotsByResource.get(project); 184 model.setRootId(projectSnapshot.getResourceId()); 185 model = session.save(model); 186 resource.setId(model.getId()); // TODO to be removed 187 188 Snapshot parentSnapshot = (Snapshot) ObjectUtils.defaultIfNull(getSnapshot(parentReference), projectSnapshot); 189 Snapshot snapshot = new Snapshot(model, parentSnapshot); 190 snapshot = session.save(snapshot); 191 session.commit(); 192 return snapshot; 193 } 194 195 public Snapshot getLastSnapshot(Snapshot snapshot, boolean onlyOlder) { 196 String hql = "SELECT s FROM " + Snapshot.class.getSimpleName() + " s WHERE s.last=:last AND s.resourceId=:resourceId"; 197 if (onlyOlder) { 198 hql += " AND s.createdAt<:date"; 199 } 200 Query query = session.createQuery(hql); 201 query.setParameter("last", true); 202 query.setParameter("resourceId", snapshot.getResourceId()); 203 if (onlyOlder) { 204 query.setParameter("date", snapshot.getCreatedAt()); 205 } 206 return session.getSingleResult(query, null); 207 } 208 209 public void clear() { 210 // we keep cache of projects 211 for (Iterator<Map.Entry<Resource, Snapshot>> it = snapshotsByResource.entrySet().iterator(); it.hasNext();) { 212 Map.Entry<Resource, Snapshot> entry = it.next(); 213 if (!ResourceUtils.isSet(entry.getKey())) { 214 it.remove(); 215 } 216 } 217 } 218 219 private ResourceModel findOrCreateModel(Resource resource) { 220 ResourceModel model; 221 try { 222 model = session.getSingleResult(ResourceModel.class, "key", resource.getEffectiveKey()); 223 if (model == null) { 224 model = createModel(resource); 225 226 } else { 227 mergeModel(model, resource); 228 } 229 return model; 230 231 } catch (NonUniqueResultException e) { 232 throw new SonarException("The resource '" + resource.getEffectiveKey() + "' is duplicated in database.", e); 233 } 234 } 235 236 static ResourceModel createModel(Resource resource) { 237 ResourceModel model = new ResourceModel(); 238 model.setEnabled(Boolean.TRUE); 239 model.setDescription(resource.getDescription()); 240 model.setKey(resource.getEffectiveKey()); 241 if (resource.getLanguage() != null) { 242 model.setLanguageKey(resource.getLanguage().getKey()); 243 } 244 if (StringUtils.isNotBlank(resource.getName())) { 245 model.setName(resource.getName()); 246 } else { 247 model.setName(resource.getKey()); 248 } 249 model.setLongName(resource.getLongName()); 250 model.setScope(resource.getScope()); 251 model.setQualifier(resource.getQualifier()); 252 return model; 253 } 254 255 static void mergeModel(ResourceModel model, Resource resource) { 256 model.setEnabled(true); 257 if (StringUtils.isNotBlank(resource.getName())) { 258 model.setName(resource.getName()); 259 } 260 if (StringUtils.isNotBlank(resource.getLongName())) { 261 model.setLongName(resource.getLongName()); 262 } 263 if (StringUtils.isNotBlank(resource.getDescription())) { 264 model.setDescription(resource.getDescription()); 265 } 266 if (!ResourceUtils.isLibrary(resource)) { 267 model.setScope(resource.getScope()); 268 model.setQualifier(resource.getQualifier()); 269 } 270 if (resource.getLanguage() != null) { 271 model.setLanguageKey(resource.getLanguage().getKey()); 272 } 273 } 274 }