001    /*
002     * SonarQube, open source software quality management tool.
003     * Copyright (C) 2008-2014 SonarSource
004     * mailto:contact AT sonarsource DOT com
005     *
006     * SonarQube 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     * SonarQube 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 License
017     * along with this program; if not, write to the Free Software Foundation,
018     * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
019     */
020    package org.sonar.batch.bootstrap;
021    
022    import com.google.common.base.Throwables;
023    import org.apache.commons.io.FileUtils;
024    import org.apache.commons.lang.StringUtils;
025    import org.slf4j.Logger;
026    import org.slf4j.LoggerFactory;
027    import org.sonar.api.BatchComponent;
028    import org.sonar.api.CoreProperties;
029    import org.sonar.api.config.Settings;
030    import org.sonar.api.database.DatabaseProperties;
031    import org.sonar.api.utils.HttpDownloader.HttpException;
032    import org.sonar.api.utils.SonarException;
033    import org.sonar.api.utils.TempFolder;
034    
035    import java.io.File;
036    import java.net.SocketTimeoutException;
037    
038    /**
039     * @since 3.4
040     */
041    public class PreviewDatabase implements BatchComponent {
042      private static final Logger LOG = LoggerFactory.getLogger(PreviewDatabase.class);
043    
044      private static final String DIALECT = "h2";
045      private static final String DRIVER = "org.h2.Driver";
046      private static final String URL = "jdbc:h2:";
047      private static final String USER = "sonar";
048      private static final String PASSWORD = USER;
049    
050      private final Settings settings;
051      private final ServerClient server;
052      private final TempFolder tempUtils;
053      private final AnalysisMode mode;
054    
055      public PreviewDatabase(Settings settings, ServerClient server, TempFolder tempUtils, AnalysisMode mode) {
056        this.settings = settings;
057        this.server = server;
058        this.tempUtils = tempUtils;
059        this.mode = mode;
060      }
061    
062      public void start() {
063        if (mode.isPreview()) {
064          File databaseFile = tempUtils.newFile("preview", ".h2.db");
065    
066          int readTimeoutSec = mode.getPreviewReadTimeoutSec();
067          downloadDatabase(databaseFile, readTimeoutSec * 1000);
068    
069          String databasePath = StringUtils.removeEnd(databaseFile.getAbsolutePath(), ".h2.db");
070          replaceSettings(databasePath);
071        }
072      }
073    
074      private void downloadDatabase(File toFile, int readTimeoutMillis) {
075        String projectKey = null;
076        try {
077          projectKey = settings.getString(CoreProperties.PROJECT_KEY_PROPERTY);
078          String branch = settings.getString(CoreProperties.PROJECT_BRANCH_PROPERTY);
079          if (StringUtils.isNotBlank(branch)) {
080            projectKey = String.format("%s:%s", projectKey, branch);
081          }
082          if (StringUtils.isBlank(projectKey)) {
083            server.download("/batch_bootstrap/db", toFile, readTimeoutMillis);
084          } else {
085            server.download("/batch_bootstrap/db?project=" + projectKey, toFile, readTimeoutMillis);
086          }
087          LOG.debug("Dry Run database size: {}", FileUtils.byteCountToDisplaySize(FileUtils.sizeOf(toFile)));
088        } catch (SonarException e) {
089          handleException(readTimeoutMillis, projectKey, e);
090          throw e;
091        }
092      }
093    
094      private void handleException(int readTimeout, String projectKey, SonarException e) {
095        Throwable rootCause = Throwables.getRootCause(e);
096        if (rootCause instanceof SocketTimeoutException) {
097          // Pico will unwrap the first runtime exception
098          throw new SonarException(new SonarException(String.format("Preview database read timed out after %s ms. You can try to increase read timeout with property -D"
099            + CoreProperties.PREVIEW_READ_TIMEOUT_SEC + " (in seconds)",
100            readTimeout), e));
101        }
102        if (projectKey != null && (rootCause instanceof HttpException) && (((HttpException) rootCause).getResponseCode() == 401)) {
103          // Pico will unwrap the first runtime exception
104          throw new SonarException(new SonarException(String.format("You don't have access rights to project [%s]", projectKey), e));
105        }
106      }
107    
108      private void replaceSettings(String databasePath) {
109        settings
110          .setProperty(DatabaseProperties.PROP_DIALECT, DIALECT)
111          .setProperty(DatabaseProperties.PROP_DRIVER, DRIVER)
112          .setProperty(DatabaseProperties.PROP_USER, USER)
113          .setProperty(DatabaseProperties.PROP_PASSWORD, PASSWORD)
114          .setProperty(DatabaseProperties.PROP_URL, URL + databasePath);
115      }
116    }