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    }