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 }