Browse Source

SONAR-3224 API: support Ruby on Rails applications

tags/3.0
Simon Brandhof 12 years ago
parent
commit
c152a8c09c
21 changed files with 536 additions and 26 deletions
  1. 1
    0
      sonar-application/src/main/assembly/temp/README.txt
  2. 2
    2
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java
  3. 1
    1
      sonar-core/src/main/java/org/sonar/core/plugins/PluginInstaller.java
  4. 6
    6
      sonar-core/src/test/java/org/sonar/core/plugins/PluginInstallerTest.java
  5. 0
    0
      sonar-core/src/test/resources/org/sonar/core/plugins/PluginInstallerTest/shouldCopyRuleExtensionsOnServerSide/checkstyle-extension.xml
  6. 2
    0
      sonar-plugin-api/src/main/java/org/sonar/api/platform/ServerFileSystem.java
  7. 44
    0
      sonar-plugin-api/src/main/java/org/sonar/api/web/RubyRailsApp.java
  8. 4
    0
      sonar-server/src/main/java/org/sonar/server/platform/DefaultServerFileSystem.java
  9. 1
    0
      sonar-server/src/main/java/org/sonar/server/platform/Platform.java
  10. 2
    2
      sonar-server/src/main/java/org/sonar/server/plugins/DefaultServerPluginRepository.java
  11. 4
    4
      sonar-server/src/main/java/org/sonar/server/plugins/PluginDeployer.java
  12. 1
    1
      sonar-server/src/main/java/org/sonar/server/plugins/StaticResourcesServlet.java
  13. 113
    0
      sonar-server/src/main/java/org/sonar/server/startup/ApplicationDeployer.java
  14. 107
    0
      sonar-server/src/main/java/org/sonar/server/startup/ClassLoaderUtils.java
  15. 5
    3
      sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java
  16. 24
    3
      sonar-server/src/main/webapp/WEB-INF/config/environment.rb
  17. 1
    1
      sonar-server/src/test/java/org/sonar/server/plugins/DefaultServerPluginRepositoryTest.java
  18. 3
    3
      sonar-server/src/test/java/org/sonar/server/plugins/PluginDeployerTest.java
  19. 83
    0
      sonar-server/src/test/java/org/sonar/server/startup/ApplicationDeployerTest.java
  20. 132
    0
      sonar-server/src/test/java/org/sonar/server/startup/ClassLoaderUtilsTest.java
  21. BIN
      sonar-server/src/test/resources/org/sonar/server/startup/ApplicationDeployerTest/FakeRubyRailsApp.jar

+ 1
- 0
sonar-application/src/main/assembly/temp/README.txt View File

@@ -0,0 +1 @@
This directory contains temporary data required by server. Do not clean up when server is up.

+ 2
- 2
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchPluginRepository.java View File

@@ -33,7 +33,7 @@ import org.sonar.api.config.Settings;
import org.sonar.api.platform.PluginMetadata;
import org.sonar.api.platform.PluginRepository;
import org.sonar.core.plugins.PluginClassloaders;
import org.sonar.core.plugins.PluginFileExtractor;
import org.sonar.core.plugins.PluginInstaller;
import org.sonar.core.plugins.RemotePlugin;

import java.io.File;
@@ -68,7 +68,7 @@ public class BatchPluginRepository implements PluginRepository {
}

void doStart(List<RemotePlugin> remotePlugins) {
PluginFileExtractor extractor = new PluginFileExtractor();
PluginInstaller extractor = new PluginInstaller();
metadataByKey = Maps.newHashMap();
for (RemotePlugin remote : remotePlugins) {
if (isAccepted(remote.getKey())) {

sonar-core/src/main/java/org/sonar/core/plugins/PluginFileExtractor.java → sonar-core/src/main/java/org/sonar/core/plugins/PluginInstaller.java View File

@@ -33,7 +33,7 @@ import java.net.URLClassLoader;
import java.util.List;
import java.util.zip.ZipEntry;

public class PluginFileExtractor {
public class PluginInstaller {

public DefaultPluginMetadata installInSameLocation(File pluginFile, boolean isCore, List<File> deprecatedExtensions) {
return install(pluginFile, isCore, deprecatedExtensions, null);

sonar-core/src/test/java/org/sonar/core/plugins/PluginFileExtractorTest.java → sonar-core/src/test/java/org/sonar/core/plugins/PluginInstallerTest.java View File

@@ -29,9 +29,9 @@ import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;

public class PluginFileExtractorTest {
public class PluginInstallerTest {

private PluginFileExtractor extractor= new PluginFileExtractor();
private PluginInstaller extractor= new PluginInstaller();

@Test
public void shouldExtractMetadata() {
@@ -60,7 +60,7 @@ public class PluginFileExtractorTest {

@Test
public void shouldCopyAndExtractDependencies() throws IOException {
File toDir = new File("target/test-tmp/PluginFileExtractorTest/shouldCopyAndExtractDependencies");
File toDir = new File("target/test-tmp/PluginInstallerTest/shouldCopyAndExtractDependencies");
FileUtils.forceMkdir(toDir);
FileUtils.cleanDirectory(toDir);

@@ -73,7 +73,7 @@ public class PluginFileExtractorTest {

@Test
public void shouldExtractOnlyDependencies() throws IOException {
File toDir = new File("target/test-tmp/PluginFileExtractorTest/shouldExtractOnlyDependencies");
File toDir = new File("target/test-tmp/PluginInstallerTest/shouldExtractOnlyDependencies");
FileUtils.forceMkdir(toDir);
FileUtils.cleanDirectory(toDir);

@@ -86,13 +86,13 @@ public class PluginFileExtractorTest {

@Test
public void shouldCopyRuleExtensionsOnServerSide() throws IOException {
File toDir = new File("target/test-tmp/PluginFileExtractorTest/shouldCopyRuleExtensionsOnServerSide");
File toDir = new File("target/test-tmp/PluginInstallerTest/shouldCopyRuleExtensionsOnServerSide");
FileUtils.forceMkdir(toDir);
FileUtils.cleanDirectory(toDir);

DefaultPluginMetadata metadata = DefaultPluginMetadata.create(getFile("sonar-checkstyle-plugin-2.8.jar"))
.setKey("checkstyle")
.addDeprecatedExtension(getFile("PluginFileExtractorTest/shouldCopyRuleExtensionsOnServerSide/checkstyle-extension.xml"));
.addDeprecatedExtension(getFile("PluginInstallerTest/shouldCopyRuleExtensionsOnServerSide/checkstyle-extension.xml"));
extractor.install(metadata, toDir);

assertThat(new File(toDir, "sonar-checkstyle-plugin-2.8.jar").exists(), is(true));

sonar-core/src/test/resources/org/sonar/core/plugins/PluginFileExtractorTest/shouldCopyRuleExtensionsOnServerSide/checkstyle-extension.xml → sonar-core/src/test/resources/org/sonar/core/plugins/PluginInstallerTest/shouldCopyRuleExtensionsOnServerSide/checkstyle-extension.xml View File


+ 2
- 0
sonar-plugin-api/src/main/java/org/sonar/api/platform/ServerFileSystem.java View File

@@ -30,6 +30,8 @@ import java.util.List;
public interface ServerFileSystem extends ServerComponent {

File getHomeDir();
File getTempDir();

/**
* @param suffixes the file suffixes. If null, then return all the files, whatever their suffix

+ 44
- 0
sonar-plugin-api/src/main/java/org/sonar/api/web/RubyRailsApp.java View File

@@ -0,0 +1,44 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2012 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar 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.
*
* Sonar 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 Sonar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.api.web;

import com.google.common.annotations.Beta;
import org.sonar.api.ServerExtension;

/**
* Complete Ruby on Rails application (controllers/helpers/models/views)
* @since 2.15
*/
@Beta
public abstract class RubyRailsApp implements ServerExtension {

/**
* The app key, ie the plugin key. It does not relate to URLs at all.
*/
public abstract String getKey();

/**
* The classloader path to the root directory. It contains a sub-directory named app/.
* <p>For example if Ruby on Rails controllers are located in /org/sonar/sqale/app/controllers/,
* then the path is /org/sonar/sqale</p>
*/
public abstract String getPath();

}

+ 4
- 0
sonar-server/src/main/java/org/sonar/server/platform/DefaultServerFileSystem.java View File

@@ -102,6 +102,10 @@ public class DefaultServerFileSystem implements ServerFileSystem {
public File getHomeDir() {
return homeDir;
}
public File getTempDir() {
return new File(homeDir, "temp");
}

public File getDeployDir() {
return deployDir;

+ 1
- 0
sonar-server/src/main/java/org/sonar/server/platform/Platform.java View File

@@ -220,6 +220,7 @@ public final class Platform {
private void executeStartupTasks() {
ComponentContainer startupContainer = servicesContainer.createChild();
startupContainer.addSingleton(GwtPublisher.class);
startupContainer.addSingleton(ApplicationDeployer.class);
startupContainer.addSingleton(RegisterMetrics.class);
startupContainer.addSingleton(RegisterRules.class);
startupContainer.addSingleton(RegisterProvidedProfiles.class);

+ 2
- 2
sonar-server/src/main/java/org/sonar/server/plugins/DefaultServerPluginRepository.java View File

@@ -80,13 +80,13 @@ public class DefaultServerPluginRepository implements ServerPluginRepository {
return pluginsByKey.get(key);
}

public ClassLoader getClassloader(String pluginKey) {
public ClassLoader getClassLoader(String pluginKey) {
return classloaders.get(pluginKey);
}

public Class getClass(String pluginKey, String classname) {
Class clazz = null;
ClassLoader classloader = getClassloader(pluginKey);
ClassLoader classloader = getClassLoader(pluginKey);
if (classloader != null) {
try {
clazz = classloader.loadClass(classname);

+ 4
- 4
sonar-server/src/main/java/org/sonar/server/plugins/PluginDeployer.java View File

@@ -31,7 +31,7 @@ import org.sonar.api.utils.Logs;
import org.sonar.api.utils.SonarException;
import org.sonar.api.utils.TimeProfiler;
import org.sonar.core.plugins.DefaultPluginMetadata;
import org.sonar.core.plugins.PluginFileExtractor;
import org.sonar.core.plugins.PluginInstaller;
import org.sonar.server.platform.DefaultServerFileSystem;
import org.sonar.server.platform.ServerStartException;

@@ -47,13 +47,13 @@ public class PluginDeployer implements ServerComponent {

private DefaultServerFileSystem fileSystem;
private Map<String, PluginMetadata> pluginByKeys = Maps.newHashMap();
private PluginFileExtractor extractor;
private PluginInstaller extractor;

public PluginDeployer(DefaultServerFileSystem fileSystem) {
this(fileSystem, new PluginFileExtractor());
this(fileSystem, new PluginInstaller());
}

PluginDeployer(DefaultServerFileSystem fileSystem, PluginFileExtractor extractor) {
PluginDeployer(DefaultServerFileSystem fileSystem, PluginInstaller extractor) {
this.fileSystem = fileSystem;
this.extractor = extractor;
}

+ 1
- 1
sonar-server/src/main/java/org/sonar/server/plugins/StaticResourcesServlet.java View File

@@ -46,7 +46,7 @@ public class StaticResourcesServlet extends HttpServlet {
String resource = getResourcePath(request);

DefaultServerPluginRepository pluginRepository = Platform.getInstance().getContainer().getComponentByType(DefaultServerPluginRepository.class);
ClassLoader classLoader = pluginRepository.getClassloader(pluginKey);
ClassLoader classLoader = pluginRepository.getClassLoader(pluginKey);
if (classLoader == null) {
LOG.error("Plugin not found: " + pluginKey);
response.sendError(HttpServletResponse.SC_NOT_FOUND);

+ 113
- 0
sonar-server/src/main/java/org/sonar/server/startup/ApplicationDeployer.java View File

@@ -0,0 +1,113 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2012 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar 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.
*
* Sonar 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 Sonar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.server.startup;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.platform.ServerFileSystem;
import org.sonar.api.web.RubyRailsApp;

import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;

/**
* Ruby on Rails requires the files to be on filesystem but not in Java classpath (JAR). This component extracts
* all the needed files from plugins and copy them to $SONAR_HOME/temp
*
* @since 2.15
*/
public class ApplicationDeployer {
private static final Logger LOG = LoggerFactory.getLogger(ApplicationDeployer.class);

private ServerFileSystem fileSystem;
private RubyRailsApp[] apps;

public ApplicationDeployer(ServerFileSystem fileSystem, RubyRailsApp[] apps) {
this.fileSystem = fileSystem;
this.apps = apps;
}

public ApplicationDeployer(ServerFileSystem fileSystem) {
this(fileSystem, new RubyRailsApp[0]);
}

public void start() throws IOException {
deployRubyRailsApps();
}

private void deployRubyRailsApps() {
LOG.info("Deploy Ruby on Rails applications");
File appsDir = prepareRubyRailsRootDirectory();

for (final RubyRailsApp app : apps) {
try {
deployRubyRailsApp(appsDir, app, app.getClass().getClassLoader());
} catch (Exception e) {
throw new IllegalStateException("Fail to deploy Ruby on Rails application: " + app.getKey(), e);
}
}
}

@VisibleForTesting
File prepareRubyRailsRootDirectory() {
File appsDir = new File(fileSystem.getTempDir(), "ror");
prepareDir(appsDir);
return appsDir;
}

@VisibleForTesting
static void deployRubyRailsApp(File appsDir, final RubyRailsApp app, ClassLoader appClassLoader) {
LOG.debug("Deploy: " + app.getKey());
File appDir = new File(appsDir, app.getKey());
if (appDir.exists()) {
LOG.error("Ruby on Rails application already exists: " + app.getKey());
} else {
ClassLoaderUtils.copyResources(appClassLoader, app.getPath(), appDir, new Function<String, String>() {
@Override
public String apply(@Nullable String relativePath) {
// relativePath format is: org/sonar/sqale/app/controllers/foo_controller.rb
// app path is: /org/sonar/sqale
// -> deployed file is app/controllers/foo_controller.rb
return StringUtils.substringAfter(relativePath, StringUtils.removeStart(app.getPath(), "/") + "/");
}
});
}
}

private void prepareDir(File appsDir) {
if (appsDir.exists() && appsDir.isDirectory()) {
try {
FileUtils.deleteDirectory(appsDir);
} catch (IOException e) {
throw new IllegalStateException("Fail to delete temp directory: " + appsDir);
}
}
try {
FileUtils.forceMkdir(appsDir);
} catch (IOException e) {
throw new IllegalStateException("Fail to create temp directory: " + appsDir);
}
}
}

+ 107
- 0
sonar-server/src/main/java/org/sonar/server/startup/ClassLoaderUtils.java View File

@@ -0,0 +1,107 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2012 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar 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.
*
* Sonar 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 Sonar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.server.startup;

import com.google.common.base.*;
import com.google.common.collect.Lists;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.CharEncoding;
import org.apache.commons.lang.StringUtils;

import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Collection;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
* TODO it this class needed in sonar-plugin-api ?
*
* @since 2.15
*/
public final class ClassLoaderUtils {

private ClassLoaderUtils() {
}

public static File copyResources(ClassLoader classLoader, String rootPath, File toDir) {
return copyResources(classLoader, rootPath, toDir, Functions.<String>identity());
}

public static File copyResources(ClassLoader classLoader, String rootPath, File toDir, Function<String, String> relocationFunction) {
Collection<String> relativePaths = listFiles(classLoader, rootPath);
for (String relativePath : relativePaths) {
URL resource = classLoader.getResource(relativePath);
String filename = relocationFunction.apply(relativePath);
File toFile = new File(toDir, filename);
try {
FileUtils.copyURLToFile(resource, toFile);
} catch (IOException e) {
throw new IllegalStateException("Fail to extract " + relativePath + " to " + toFile.getAbsolutePath());
}
}

return toDir;
}

public static Collection<String> listFiles(ClassLoader classLoader, String rootPath) {
return listResources(classLoader, rootPath, new Predicate<String>() {
@Override
public boolean apply(@Nullable String path) {
return !StringUtils.endsWith(path, "/");
}
});
}

public static Collection<String> listResources(ClassLoader classloader, String rootPath) {
return listResources(classloader, rootPath, Predicates.<String>alwaysTrue());
}

public static Collection<String> listResources(ClassLoader classloader, String rootPath, Predicate<String> predicate) {
try {
Collection<String> paths = Lists.newArrayList();
rootPath = StringUtils.removeStart(rootPath, "/");

URL root = classloader.getResource(rootPath);
if (root == null) {
return paths;
}
if (!"jar".equals(root.getProtocol())) {
throw new IllegalStateException("Unsupported protocol: " + root.getProtocol());
}
String jarPath = root.getPath().substring(5, root.getPath().indexOf("!")); //strip out only the JAR file
JarFile jar = new JarFile(URLDecoder.decode(jarPath, CharEncoding.UTF_8));
Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
String name = entries.nextElement().getName();
if (name.startsWith(rootPath) && predicate.apply(name)) {
paths.add(name);
}
}
return paths;
} catch (Exception e) {
throw Throwables.propagate(e);
}
}
}

+ 5
- 3
sonar-server/src/main/java/org/sonar/server/ui/JRubyFacade.java View File

@@ -21,12 +21,11 @@ package org.sonar.server.ui;

import org.apache.commons.configuration.Configuration;
import org.slf4j.LoggerFactory;
import org.sonar.api.CoreProperties;
import org.sonar.api.config.License;
import org.sonar.api.config.PropertyDefinitions;
import org.sonar.api.config.Settings;
import org.sonar.api.platform.ComponentContainer;
import org.sonar.api.platform.PluginMetadata;
import org.sonar.api.platform.PluginRepository;
import org.sonar.api.platform.*;
import org.sonar.api.profiles.ProfileExporter;
import org.sonar.api.profiles.ProfileImporter;
import org.sonar.api.resources.Language;
@@ -416,6 +415,9 @@ public final class JRubyFacade {
return License.readBase64(base64);
}

public String getServerHome() {
return getContainer().getComponentByType(Settings.class).getString(CoreProperties.SONAR_HOME);
}

public ReviewsNotificationManager getReviewsNotificationManager() {
return getContainer().getComponentByType(ReviewsNotificationManager.class);

+ 24
- 3
sonar-server/src/main/webapp/WEB-INF/config/environment.rb View File

@@ -5,6 +5,21 @@
require File.join(File.dirname(__FILE__), 'boot')
require 'color'

#
# Limitation of Rails 2.3 and Rails Engines (plugins) when threadsafe! is enabled in production mode
# See http://groups.google.com/group/rubyonrails-core/browse_thread/thread/9067bce01444fb24?pli=1
#
class EagerPluginLoader < Rails::Plugin::Loader
def add_plugin_load_paths
super
plugins.each do |plugin|
if configuration.cache_classes
configuration.eager_load_paths += plugin.load_paths
end
end
end
end

Rails::Initializer.run do |config|
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
@@ -15,8 +30,12 @@ Rails::Initializer.run do |config|
# you must remove the Active Record framework.
config.frameworks -= [ :action_mailer ]

# Add additional load paths for your own custom dirs
# config.load_paths += %W( #{RAILS_ROOT}/extras )
# This property can't be set in config/environments because of execution order
# See http://strd6.com/2009/04/cant-dup-nilclass-maybe-try-unloadable/
config.reload_plugins=(RAILS_ENV == 'development')

config.plugin_loader = EagerPluginLoader
config.plugin_paths << "#{Java::OrgSonarServerUi::JRubyFacade.getInstance().getServerHome()}/temp/ror"

# Force all environments to use the same logger level
# (by default production uses :info, the others :debug)
@@ -51,6 +70,8 @@ Rails::Initializer.run do |config|
# config.active_record.observers = :cacher, :garbage_collector, :forum_observer
end



class ActiveRecord::Migration
def self.alter_to_big_primary_key(tablename)
dialect = ::Java::OrgSonarServerUi::JRubyFacade.getInstance().getDatabase().getDialect().getActiveRecordDialectCode()
@@ -58,7 +79,7 @@ class ActiveRecord::Migration
when "postgre"
execute "ALTER TABLE #{tablename} ALTER COLUMN id TYPE bigint"
when "mysql"
execute "ALTER TABLE #{tablename} CHANGE id id BIGINT AUTO_INCREMENT";
execute "ALTER TABLE #{tablename} CHANGE id id BIGINT AUTO_INCREMENT"
when "derby"
# do nothing as alter can not do the job in Derby
when "oracle"

+ 1
- 1
sonar-server/src/test/java/org/sonar/server/plugins/DefaultServerPluginRepositoryTest.java View File

@@ -68,7 +68,7 @@ public class DefaultServerPluginRepositoryTest {

assertThat(repository.getPlugins().size(), Is.is(1));
assertThat(repository.getPlugin("artifactsize"), not(nullValue()));
assertThat(repository.getClassloader("artifactsize"), not(nullValue()));
assertThat(repository.getClassLoader("artifactsize"), not(nullValue()));
assertThat(repository.getClass("artifactsize", "org.sonar.plugins.artifactsize.ArtifactSizeMetrics"), not(nullValue()));
assertThat(repository.getClass("artifactsize", "org.Unknown"), nullValue());
assertThat(repository.getClass("other", "org.sonar.plugins.artifactsize.ArtifactSizeMetrics"), nullValue());

+ 3
- 3
sonar-server/src/test/java/org/sonar/server/plugins/PluginDeployerTest.java View File

@@ -25,7 +25,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.sonar.api.platform.PluginMetadata;
import org.sonar.core.plugins.PluginFileExtractor;
import org.sonar.core.plugins.PluginInstaller;
import org.sonar.server.platform.DefaultServerFileSystem;
import org.sonar.server.platform.ServerStartException;
import org.sonar.test.TestUtils;
@@ -39,7 +39,7 @@ import static org.junit.Assert.assertThat;

public class PluginDeployerTest {

private PluginFileExtractor extractor;
private PluginInstaller extractor;
private DefaultServerFileSystem fileSystem;
private File homeDir;
private File deployDir;
@@ -53,7 +53,7 @@ public class PluginDeployerTest {
homeDir = TestUtils.getResource(PluginDeployerTest.class, name.getMethodName());
deployDir = TestUtils.getTestTempDir(PluginDeployerTest.class, name.getMethodName() + "/deploy");
fileSystem = new DefaultServerFileSystem(null, homeDir, deployDir);
extractor = new PluginFileExtractor();
extractor = new PluginInstaller();
deployer = new PluginDeployer(fileSystem, extractor);
}


+ 83
- 0
sonar-server/src/test/java/org/sonar/server/startup/ApplicationDeployerTest.java View File

@@ -0,0 +1,83 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2012 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar 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.
*
* Sonar 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 Sonar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.server.startup;

import org.apache.commons.io.FileUtils;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.sonar.api.platform.ServerFileSystem;
import org.sonar.api.web.RubyRailsApp;

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class ApplicationDeployerTest {

@Rule
public TemporaryFolder temp = new TemporaryFolder();

@Test
public void deployRubyRailsApp() throws Exception {
File tempDir = this.temp.getRoot();
ClassLoader classLoader = new URLClassLoader(new URL[]{
getClass().getResource("/org/sonar/server/startup/ApplicationDeployerTest/FakeRubyRailsApp.jar").toURI().toURL()}, null);
ApplicationDeployer.deployRubyRailsApp(tempDir, new FakeRubyRailsApp(), classLoader);

File appDir = new File(tempDir, "fake");
assertThat(appDir.isDirectory(), is(true));
assertThat(appDir.exists(), is(true));
assertThat(FileUtils.listFiles(appDir, null, true).size(), is(2));
assertThat(new File(appDir, "app/controllers/fake_controller.rb").exists(), is(true));
assertThat(new File(appDir, "app/views/fake/index.html.erb").exists(), is(true));
}

@Test
public void prepareRubyRailsRootDirectory() throws Exception {
ServerFileSystem fileSystem = mock(ServerFileSystem.class);
File tempDir = this.temp.getRoot();
when(fileSystem.getTempDir()).thenReturn(tempDir);

File dir = new ApplicationDeployer(fileSystem, new RubyRailsApp[]{new FakeRubyRailsApp()}).prepareRubyRailsRootDirectory();

assertThat(dir.isDirectory(), is(true));
assertThat(dir.exists(), is(true));
assertThat(dir.getCanonicalPath(), is(new File(tempDir, "ror").getCanonicalPath()));
}

static class FakeRubyRailsApp extends RubyRailsApp {

@Override
public String getKey() {
return "fake";
}

@Override
public String getPath() {
return "/org/sonar/server/startup/ApplicationDeployerTest/FakeRubyRailsApp";
}
}
}

+ 132
- 0
sonar-server/src/test/java/org/sonar/server/startup/ClassLoaderUtilsTest.java View File

@@ -0,0 +1,132 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2012 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar 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.
*
* Sonar 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 Sonar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.server.startup;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.hamcrest.core.Is;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

import javax.annotation.Nullable;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Collection;

import static org.junit.Assert.assertThat;
import static org.junit.matchers.JUnitMatchers.hasItems;

public class ClassLoaderUtilsTest {

private ClassLoader classLoader;

@Rule
public TemporaryFolder temp = new TemporaryFolder();

@Before
public void prepareClassLoader() {
// This JAR file has the three following files :
// org/sonar/sqale/app/copyright.txt
// org/sonar/sqale/app/README.md
// org/sonar/other/other.txt
URL jarUrl = getClass().getResource("/org/sonar/server/startup/ClassLoaderUtilsTest/ClassLoaderUtilsTest.jar");
classLoader = new URLClassLoader(new URL[]{jarUrl}, /* no parent classloader */null);
}

@Test
public void listResources_unknown_root() {
Collection<String> strings = ClassLoaderUtils.listResources(classLoader, "unknown/directory");
assertThat(strings.size(), Is.is(0));
}

@Test
public void listResources_all() {
Collection<String> strings = ClassLoaderUtils.listResources(classLoader, "org/sonar/sqale");
assertThat(strings, hasItems(
"org/sonar/sqale/",
"org/sonar/sqale/app/",
"org/sonar/sqale/app/copyright.txt",
"org/sonar/sqale/app/README.md"));
assertThat(strings.size(), Is.is(4));
}

@Test
public void listResources_root_path_starts_with_slash() {
Collection<String> strings = ClassLoaderUtils.listResources(classLoader, "/org/sonar/sqale");
assertThat(strings, hasItems(
"org/sonar/sqale/",
"org/sonar/sqale/app/",
"org/sonar/sqale/app/copyright.txt",
"org/sonar/sqale/app/README.md"));
assertThat(strings.size(), Is.is(4));
}

@Test
public void listResources_use_predicate() {
Collection<String> strings = ClassLoaderUtils.listResources(classLoader, "org/sonar/sqale", new Predicate<String>() {
@Override
public boolean apply(@Nullable String s) {
return StringUtils.endsWith(s, "md");
}
});
assertThat(strings.size(), Is.is(1));
assertThat(strings, hasItems("org/sonar/sqale/app/README.md"));
}

@Test
public void listFiles() {
Collection<String> strings = ClassLoaderUtils.listFiles(classLoader, "org/sonar/sqale");
assertThat(strings, hasItems(
"org/sonar/sqale/app/copyright.txt",
"org/sonar/sqale/app/README.md"));
assertThat(strings.size(), Is.is(2));
}

@Test
public void copyRubyRailsApp() {
File toDir = temp.newFolder("dest");
ClassLoaderUtils.copyResources(classLoader, "org/sonar/sqale", toDir);

assertThat(FileUtils.listFiles(toDir, null, true).size(), Is.is(2));
assertThat(new File(toDir, "org/sonar/sqale/app/copyright.txt").exists(), Is.is(true));
assertThat(new File(toDir, "org/sonar/sqale/app/README.md").exists(), Is.is(true));
}

@Test
public void copyRubyRailsApp_relocate_files() {
File toDir = temp.newFolder("dest");
ClassLoaderUtils.copyResources(classLoader, "org/sonar/sqale", toDir, new Function<String, String>() {
@Override
public String apply(@Nullable String path) {
return "foo/" + FilenameUtils.getName(path);
}
});

assertThat(FileUtils.listFiles(toDir, null, true).size(), Is.is(2));
assertThat(new File(toDir, "foo/copyright.txt").exists(), Is.is(true));
assertThat(new File(toDir, "foo/README.md").exists(), Is.is(true));
}
}

BIN
sonar-server/src/test/resources/org/sonar/server/startup/ApplicationDeployerTest/FakeRubyRailsApp.jar View File


Loading…
Cancel
Save