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 }