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    }