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.server.plugins;
021    
022    import com.google.common.annotations.VisibleForTesting;
023    import com.google.common.base.Function;
024    import org.apache.commons.io.FileUtils;
025    import org.apache.commons.lang.StringUtils;
026    import org.slf4j.Logger;
027    import org.slf4j.LoggerFactory;
028    import org.sonar.api.platform.PluginMetadata;
029    import org.sonar.api.platform.PluginRepository;
030    import org.sonar.api.platform.ServerFileSystem;
031    import org.sonar.server.plugins.ClassLoaderUtils;
032    
033    import javax.annotation.Nullable;
034    import java.io.File;
035    import java.io.IOException;
036    
037    /**
038     * Ruby on Rails requires the files to be on filesystem but not in Java classpath (JAR). This component extracts
039     * all the needed files from plugins and copy them to $SONAR_HOME/temp
040     *
041     * @since 3.0
042     */
043    public class ApplicationDeployer {
044      private static final Logger LOG = LoggerFactory.getLogger(ApplicationDeployer.class);
045      private static final String ROR_PATH = "org/sonar/ror/";
046    
047      private ServerFileSystem fileSystem;
048      private PluginRepository pluginRepository;
049    
050      public ApplicationDeployer(ServerFileSystem fileSystem, PluginRepository pluginRepository) {
051        this.fileSystem = fileSystem;
052        this.pluginRepository = pluginRepository;
053      }
054    
055      public void start() throws IOException {
056        deployRubyRailsApps();
057      }
058    
059      private void deployRubyRailsApps() {
060        LOG.info("Deploy Ruby on Rails applications");
061        File appsDir = prepareRubyRailsRootDirectory();
062    
063        for (PluginMetadata pluginMetadata : pluginRepository.getMetadata()) {
064          String pluginKey = pluginMetadata.getKey();
065          try {
066            deployRubyRailsApp(appsDir, pluginKey, pluginRepository.getPlugin(pluginKey).getClass().getClassLoader());
067          } catch (Exception e) {
068            throw new IllegalStateException("Fail to deploy Ruby on Rails application: " + pluginKey, e);
069          }
070        }
071      }
072    
073      @VisibleForTesting
074      File prepareRubyRailsRootDirectory() {
075        File appsDir = new File(fileSystem.getTempDir(), "ror");
076        prepareDir(appsDir);
077        return appsDir;
078      }
079    
080      @VisibleForTesting
081      static void deployRubyRailsApp(File appsDir, final String pluginKey, ClassLoader appClassLoader) {
082        if (hasRubyRailsApp(pluginKey, appClassLoader)) {
083          LOG.info("Deploy app: " + pluginKey);
084          File appDir = new File(appsDir, pluginKey);
085          ClassLoaderUtils.copyResources(appClassLoader, pathToRubyInitFile(pluginKey), appDir, new Function<String, String>() {
086            public String apply(@Nullable String relativePath) {
087              // Relocate the deployed files :
088              // relativePath format is: org/sonar/ror/sqale/app/controllers/foo_controller.rb
089              // app path is: org/sonar/ror/sqale
090              // -> deployed file is app/controllers/foo_controller.rb
091              return StringUtils.substringAfter(relativePath, pluginKey + "/");
092            }
093          });
094        }
095      }
096    
097      private static String pathToRubyInitFile(String pluginKey) {
098        return ROR_PATH + pluginKey + "/init.rb";
099      }
100    
101      @VisibleForTesting
102      static boolean hasRubyRailsApp(String pluginKey, ClassLoader classLoader) {
103        return classLoader.getResource(pathToRubyInitFile(pluginKey)) != null;
104    
105      }
106    
107      private void prepareDir(File appsDir) {
108        if (appsDir.exists() && appsDir.isDirectory()) {
109          try {
110            FileUtils.deleteDirectory(appsDir);
111          } catch (IOException e) {
112            throw new IllegalStateException("Fail to delete temp directory: " + appsDir);
113          }
114        }
115        try {
116          FileUtils.forceMkdir(appsDir);
117        } catch (IOException e) {
118          throw new IllegalStateException("Fail to create temp directory: " + appsDir);
119        }
120      }
121    }