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    }