Package org.sonar.batch.index

Source Code of org.sonar.batch.index.DefaultResourcePersister

/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* SonarQube is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
package org.sonar.batch.index;

import com.google.common.collect.Maps;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.database.DatabaseSession;
import org.sonar.api.database.model.ResourceModel;
import org.sonar.api.database.model.Snapshot;
import org.sonar.api.resources.File;
import org.sonar.api.resources.Language;
import org.sonar.api.resources.Library;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.resources.Resource;
import org.sonar.api.resources.ResourceUtils;
import org.sonar.api.resources.Scopes;
import org.sonar.api.security.ResourcePermissions;
import org.sonar.api.utils.SonarException;

import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import javax.persistence.NonUniqueResultException;
import javax.persistence.Query;

import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;

public final class DefaultResourcePersister implements ResourcePersister {

  private static final String RESOURCE_ID = "resourceId";
  private static final String LAST = "last";
  private static final String VERSION = "version";
  private static final String SCOPE = "scope";
  private static final String QUALIFIER = "qualifier";

  private final DatabaseSession session;
  private final Map<Resource, Snapshot> snapshotsByResource = Maps.newHashMap();
  private final ResourcePermissions permissions;
  private final SnapshotCache snapshotCache;
  private final ResourceCache resourceCache;

  public DefaultResourcePersister(DatabaseSession session, ResourcePermissions permissions, SnapshotCache snapshotCache, ResourceCache resourceCache) {
    this.session = session;
    this.permissions = permissions;
    this.snapshotCache = snapshotCache;
    this.resourceCache = resourceCache;
  }

  @Override
  public Snapshot saveProject(Project project, @Nullable Project parent) {
    Snapshot snapshot = snapshotsByResource.get(project);
    if (snapshot == null) {
      snapshot = persistProject(project, parent);
      addToCache(project, snapshot);
    }
    return snapshot;
  }

  private void addToCache(Resource resource, Snapshot snapshot) {
    if (snapshot != null) {
      snapshotsByResource.put(resource, snapshot);
      resourceCache.add(resource);
      if (!(resource instanceof Library)) {
        // Maven libraries can have the same effective key than a project so we can't cache by effectiveKey
        snapshotCache.put(resource.getEffectiveKey(), snapshot);
      }
    }
  }

  private Snapshot persistProject(Project project, @Nullable Project parent) {
    // temporary hack
    project.setEffectiveKey(project.getKey());

    ResourceModel model = findOrCreateModel(project, parent);
    // Used by ResourceKeyMigration in order to know that a project has already being migrated
    model.setDeprecatedKey(project.getKey());
    // language is null for project since multi-language support
    model.setLanguageKey(null);

    // For views
    if (project instanceof ResourceCopy) {
      model.setCopyResourceId(((ResourceCopy) project).getCopyResourceId());
    }

    Snapshot parentSnapshot = null;
    if (parent != null) {
      // assume that the parent project has already been saved
      parentSnapshot = snapshotsByResource.get(project.getParent());
      model.setRootId((Integer) ObjectUtils.defaultIfNull(parentSnapshot.getRootProjectId(), parentSnapshot.getResourceId()));
    } else {
      model.setRootId(null);
    }
    model = session.save(model);
    project.setId(model.getId());
    project.setUuid(model.getUuid());

    Snapshot snapshot = new Snapshot(model, parentSnapshot);
    snapshot.setVersion(project.getAnalysisVersion());
    snapshot.setCreatedAt(project.getAnalysisDate());
    snapshot.setBuildDate(new Date());
    snapshot = session.save(snapshot);
    session.commit();

    if (!permissions.hasRoles(project)) {
      permissions.grantDefaultRoles(project);
    }

    return snapshot;
  }

  @Override
  @CheckForNull
  public Snapshot getSnapshot(@Nullable Resource reference) {
    return snapshotsByResource.get(reference);
  }

  @Override
  public Snapshot getSnapshotOrFail(Resource resource) {
    Snapshot snapshot = getSnapshot(resource);
    if (snapshot == null) {
      throw new ResourceNotPersistedException(resource);
    }
    return snapshot;
  }

  @Override
  public Snapshot getSnapshotOrFail(InputFile inputFile) {
    return getSnapshotOrFail(fromInputFile(inputFile));
  }

  private Resource fromInputFile(InputFile inputFile) {
    return File.create(inputFile.relativePath());
  }

  /**
   * just for unit tests
   */
  Map<Resource, Snapshot> getSnapshotsByResource() {
    return snapshotsByResource;
  }

  @Override
  public Snapshot saveResource(Project project, Resource resource) {
    return saveResource(project, resource, null);
  }

  @Override
  public Snapshot saveResource(Project project, Resource resource, @Nullable Resource parent) {
    Snapshot snapshot = snapshotsByResource.get(resource);
    if (snapshot == null) {
      snapshot = persist(project, resource, parent);
      addToCache(resource, snapshot);
    }
    return snapshot;
  }

  private Snapshot persist(Project project, Resource resource, @Nullable Resource parent) {
    Snapshot snapshot;
    if (resource instanceof Project) {
      // should not occur, please use the method saveProject()
      snapshot = persistProject((Project) resource, (Project) parent);

    } else if (resource instanceof Library) {
      snapshot = persistLibrary(project, (Library) resource);

    } else {
      snapshot = persistFileOrDirectory(project, resource, parent);
    }

    return snapshot;
  }

  private Snapshot persistLibrary(Project project, Library library) {
    ResourceModel model = findOrCreateModel(library, null);
    model = session.save(model);
    // TODO to be removed
    library.setId(model.getId());
    library.setUuid(model.getUuid());
    library.setEffectiveKey(library.getKey());

    Snapshot snapshot = findLibrarySnapshot(model.getId(), library.getVersion());
    if (snapshot == null) {
      snapshot = new Snapshot(model, null);
      snapshot.setCreatedAt(project.getAnalysisDate());
      snapshot.setBuildDate(new Date());
      snapshot.setVersion(library.getVersion());
      snapshot.setStatus(Snapshot.STATUS_PROCESSED);

      // see http://jira.codehaus.org/browse/SONAR-1850
      // The qualifier must be LIB, even if the resource is TRK, because this snapshot has no measures.
      snapshot.setQualifier(Qualifiers.LIBRARY);
      snapshot = session.save(snapshot);
    }
    session.commit();
    return snapshot;
  }

  private Snapshot findLibrarySnapshot(Integer resourceId, String version) {
    Query query = session.createQuery("from " + Snapshot.class.getSimpleName() +
      " s WHERE s.resourceId=:resourceId AND s.version=:version AND s.scope=:scope AND s.qualifier<>:qualifier AND s.last=:last");
    query.setParameter(RESOURCE_ID, resourceId);
    query.setParameter(VERSION, version);
    query.setParameter(SCOPE, Scopes.PROJECT);
    query.setParameter(QUALIFIER, Qualifiers.LIBRARY);
    query.setParameter(LAST, Boolean.TRUE);
    List<Snapshot> snapshots = query.getResultList();
    if (snapshots.isEmpty()) {
      snapshots = session.getResults(Snapshot.class, RESOURCE_ID, resourceId, VERSION, version, SCOPE, Scopes.PROJECT, QUALIFIER, Qualifiers.LIBRARY);
    }
    return snapshots.isEmpty() ? null : snapshots.get(0);
  }

  /**
   * Everything except project and library
   */
  private Snapshot persistFileOrDirectory(Project project, Resource resource, @Nullable Resource parentReference) {
    Snapshot moduleSnapshot = snapshotsByResource.get(project);
    Integer moduleId = moduleSnapshot.getResourceId();
    ResourceModel model = findOrCreateModel(resource, parentReference != null ? parentReference : project);
    model.setRootId(moduleId);
    model = session.save(model);
    resource.setId(model.getId());
    resource.setUuid(model.getUuid());

    Snapshot parentSnapshot = (Snapshot) ObjectUtils.defaultIfNull(getSnapshot(parentReference), moduleSnapshot);
    Snapshot snapshot = new Snapshot(model, parentSnapshot);
    snapshot.setBuildDate(new Date());
    snapshot = session.save(snapshot);
    session.commit();
    return snapshot;
  }

  @Override
  @CheckForNull
  public Snapshot getLastSnapshot(Snapshot snapshot, boolean onlyOlder) {
    String hql = "SELECT s FROM " + Snapshot.class.getSimpleName() + " s WHERE s.last=:last AND s.resourceId=:resourceId";
    if (onlyOlder) {
      hql += " AND s.createdAt<:date";
    }
    Query query = session.createQuery(hql);
    query.setParameter(LAST, true);
    query.setParameter(RESOURCE_ID, snapshot.getResourceId());
    if (onlyOlder) {
      query.setParameter("date", snapshot.getCreatedAt());
    }
    return session.getSingleResult(query, null);
  }

  @Override
  public void clear() {
    // we keep cache of projects
    for (Iterator<Map.Entry<Resource, Snapshot>> it = snapshotsByResource.entrySet().iterator(); it.hasNext();) {
      Map.Entry<Resource, Snapshot> entry = it.next();
      if (!ResourceUtils.isSet(entry.getKey())) {
        it.remove();
      }
    }
  }

  private ResourceModel findOrCreateModel(Resource resource, @Nullable Resource parentResource) {
    ResourceModel model;
    try {
      model = session.getSingleResult(ResourceModel.class, "key", resource.getEffectiveKey());
      if (model == null) {
        if (StringUtils.isBlank(resource.getEffectiveKey())) {
          throw new SonarException("Unable to persist resource " + resource.toString() + ". Resource effective key is blank. This may be caused by an outdated plugin.");
        }
        model = createModel(resource, parentResource);

      } else {
        mergeModel(model, resource);
      }
      updateUuids(resource, parentResource, model);
      return model;

    } catch (NonUniqueResultException e) {
      throw new SonarException("The resource '" + resource.getEffectiveKey() + "' is duplicated in database.", e);
    }
  }

  ResourceModel createModel(Resource resource, @Nullable Resource parentResource) {
    ResourceModel model = new ResourceModel();
    model.setEnabled(Boolean.TRUE);
    model.setDescription(resource.getDescription());
    model.setKey(resource.getEffectiveKey());
    model.setUuid(UUID.randomUUID().toString());
    model.setPath(resource.getPath());
    Language language = resource.getLanguage();
    if (language != null) {
      model.setLanguageKey(language.getKey());
    }
    if (StringUtils.isNotBlank(resource.getName())) {
      model.setName(resource.getName());
    } else {
      model.setName(resource.getKey());
    }
    model.setLongName(resource.getLongName());
    model.setScope(resource.getScope());
    model.setQualifier(resource.getQualifier());
    return model;
  }

  private void updateUuids(Resource resource, Resource parentResource, ResourceModel model) {
    // Don't override uuids when persisting a library and a project already exists
    if (ResourceUtils.isLibrary(resource) && !Qualifiers.LIBRARY.equals(model.getQualifier())) {
      return;
    }
    if (parentResource != null) {
      ResourceModel parentModel = session.getSingleResult(ResourceModel.class, "id", parentResource.getId());
      if (parentModel.getProjectUuid() != null) {
        model.setProjectUuid(parentModel.getProjectUuid());
      } else {
        model.setProjectUuid(parentModel.getUuid());
      }
      if (Scopes.isProject(parentResource)) {
        model.setModuleUuid(parentResource.getUuid());
        String parentModuleUuidPath = parentModel.getModuleUuidPath();
        model.setModuleUuidPath(parentModuleUuidPath != null ? parentModuleUuidPath + "." + parentModel.getUuid() : parentModel.getUuid());
      } else {
        model.setModuleUuid(parentModel.getModuleUuid());
        String parentModuleUuidPath = parentModel.getModuleUuidPath();
        model.setModuleUuidPath(parentModuleUuidPath != null ? parentModuleUuidPath : parentModel.getUuid());
      }
    } else {
      // Root module && libraries
      model.setProjectUuid(model.getUuid());
    }
  }

  static void mergeModel(ResourceModel model, Resource resource) {
    model.setEnabled(true);
    model.setKey(resource.getEffectiveKey());
    if (StringUtils.isNotBlank(resource.getName())) {
      model.setName(resource.getName());
    }
    if (StringUtils.isNotBlank(resource.getLongName())) {
      model.setLongName(resource.getLongName());
    }
    if (StringUtils.isNotBlank(resource.getDescription())) {
      model.setDescription(resource.getDescription());
    }
    if (StringUtils.isNotBlank(resource.getPath())) {
      model.setPath(resource.getPath());
    }
    if (!ResourceUtils.isLibrary(resource)) {
      model.setScope(resource.getScope());
      model.setQualifier(resource.getQualifier());
    }
    Language language = resource.getLanguage();
    if (language != null) {
      model.setLanguageKey(language.getKey());
    }
  }
}
TOP

Related Classes of org.sonar.batch.index.DefaultResourcePersister

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.