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 }