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 }