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