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 */
020package org.sonar.batch.index;
021
022import com.google.common.collect.Maps;
023import org.apache.commons.lang.ObjectUtils;
024import org.apache.commons.lang.StringUtils;
025import org.sonar.api.database.DatabaseSession;
026import org.sonar.api.database.model.ResourceModel;
027import org.sonar.api.database.model.Snapshot;
028import org.sonar.api.resources.*;
029import org.sonar.api.utils.SonarException;
030
031import javax.persistence.NonUniqueResultException;
032import javax.persistence.Query;
033import java.util.Date;
034import java.util.Iterator;
035import java.util.List;
036import java.util.Map;
037
038public 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}