added api to install plugin packages

This commit is contained in:
Sebastian Sdorra
2012-09-29 21:44:39 +02:00
parent 5c43ece689
commit a5ca1a55d2
6 changed files with 159 additions and 29 deletions

View File

@@ -44,8 +44,10 @@ import sonia.scm.util.IOUtil;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream; import java.util.zip.ZipInputStream;
@@ -66,6 +68,30 @@ public class ZipUnArchiver extends AbstractUnArchiver
//~--- methods -------------------------------------------------------------- //~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param inputStream
* @param outputDirectory
*
* @throws IOException
* @since 1.21
*/
public void extractArchive(InputStream inputStream, File outputDirectory)
throws IOException
{
ZipInputStream input = new ZipInputStream(inputStream);
ZipEntry entry = input.getNextEntry();
while (entry != null)
{
extractEntry(outputDirectory, input, entry);
entry = input.getNextEntry();
}
}
/** /**
* Method description * Method description
* *
@@ -77,27 +103,20 @@ public class ZipUnArchiver extends AbstractUnArchiver
*/ */
@Override @Override
protected void extractArchive(File archive, File outputDirectory) protected void extractArchive(File archive, File outputDirectory)
throws IOException throws IOException
{ {
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
{ {
logger.debug("extract zip \"{}\" to \"{}\"", archive.getPath(), logger.debug("extract zip \"{}\" to \"{}\"", archive.getPath(),
outputDirectory.getAbsolutePath()); outputDirectory.getAbsolutePath());
} }
ZipInputStream input = null; InputStream input = null;
try try
{ {
input = new ZipInputStream(new FileInputStream(archive)); input = new FileInputStream(archive);
extractArchive(input, outputDirectory);
ZipEntry entry = input.getNextEntry();
while (entry != null)
{
extractEntry(outputDirectory, input, entry);
entry = input.getNextEntry();
}
} }
finally finally
{ {
@@ -135,8 +154,8 @@ public class ZipUnArchiver extends AbstractUnArchiver
* @throws IOException * @throws IOException
*/ */
private void extractEntry(File outputDirectory, ZipInputStream input, private void extractEntry(File outputDirectory, ZipInputStream input,
ZipEntry entry) ZipEntry entry)
throws IOException throws IOException
{ {
try try
{ {
@@ -177,7 +196,7 @@ public class ZipUnArchiver extends AbstractUnArchiver
* @throws IOException * @throws IOException
*/ */
private void extractFile(ZipInputStream input, File outputFile) private void extractFile(ZipInputStream input, File outputFile)
throws IOException throws IOException
{ {
FileOutputStream output = null; FileOutputStream output = null;

View File

@@ -35,6 +35,9 @@ package sonia.scm.plugin;
//~--- JDK imports ------------------------------------------------------------ //~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection; import java.util.Collection;
/** /**
@@ -58,6 +61,17 @@ public interface PluginManager
*/ */
public void install(String id); public void install(String id);
/**
* Installs a plugin package from a inputstream.
*
*
* @param packageStream package input stream
*
* @throws IOException
* @since 1.21
*/
public void installPackage(InputStream packageStream) throws IOException;
/** /**
* Method description * Method description
* *

View File

@@ -107,6 +107,12 @@
</exclusions> </exclusions>
</dependency> </dependency>
<dependency>
<groupId>com.sun.jersey.contribs</groupId>
<artifactId>jersey-multipart</artifactId>
<version>${jersey.version}</version>
</dependency>
<!-- injection --> <!-- injection -->
<dependency> <dependency>
@@ -203,6 +209,12 @@
<version>${aether.version}</version> <version>${aether.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.sonatype.aether</groupId>
<artifactId>aether-connector-file</artifactId>
<version>${aether.version}</version>
</dependency>
<!-- rest documentation --> <!-- rest documentation -->
<dependency> <dependency>

View File

@@ -47,12 +47,18 @@ import sonia.scm.plugin.PluginInformationComparator;
//~--- JDK imports ------------------------------------------------------------ //~--- JDK imports ------------------------------------------------------------
import com.sun.jersey.multipart.FormDataParam;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.POST; import javax.ws.rs.POST;
import javax.ws.rs.Path; import javax.ws.rs.Path;
@@ -85,6 +91,33 @@ public class PluginResource
//~--- methods -------------------------------------------------------------- //~--- methods --------------------------------------------------------------
/**
* Installs a plugin from a package.<br />
* <br />
* <ul>
* <li>200 success</li>
* <li>500 internal server error</li>
* </ul>
*
*
*
* @param uploadedInputStream
* @return
*
* @throws IOException
*/
@POST
@Path("install-package")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response install(
@FormDataParam("package") InputStream uploadedInputStream)
throws IOException
{
pluginManager.installPackage(uploadedInputStream);
return Response.ok().build();
}
/** /**
* Installs a plugin.<br /> * Installs a plugin.<br />
* <br /> * <br />

View File

@@ -46,6 +46,7 @@ import org.slf4j.LoggerFactory;
import org.sonatype.aether.RepositorySystem; import org.sonatype.aether.RepositorySystem;
import org.sonatype.aether.collection.CollectRequest; import org.sonatype.aether.collection.CollectRequest;
import org.sonatype.aether.connector.async.AsyncRepositoryConnectorFactory; import org.sonatype.aether.connector.async.AsyncRepositoryConnectorFactory;
import org.sonatype.aether.connector.file.FileRepositoryConnectorFactory;
import org.sonatype.aether.graph.Dependency; import org.sonatype.aether.graph.Dependency;
import org.sonatype.aether.graph.DependencyFilter; import org.sonatype.aether.graph.DependencyFilter;
import org.sonatype.aether.graph.DependencyNode; import org.sonatype.aether.graph.DependencyNode;
@@ -343,6 +344,8 @@ public class AetherPluginHandler
DefaultArtifactDescriptorReader.class); DefaultArtifactDescriptorReader.class);
locator.addService(RepositoryConnectorFactory.class, locator.addService(RepositoryConnectorFactory.class,
AsyncRepositoryConnectorFactory.class); AsyncRepositoryConnectorFactory.class);
locator.addService(RepositoryConnectorFactory.class,
FileRepositoryConnectorFactory.class);
return locator.getService(RepositorySystem.class); return locator.getService(RepositorySystem.class);
} }

View File

@@ -35,6 +35,8 @@ package sonia.scm.plugin;
//~--- non-JDK imports -------------------------------------------------------- //~--- non-JDK imports --------------------------------------------------------
import com.google.common.collect.Sets;
import com.google.common.io.Files;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Provider; import com.google.inject.Provider;
import com.google.inject.Singleton; import com.google.inject.Singleton;
@@ -49,6 +51,7 @@ import sonia.scm.SCMContextProvider;
import sonia.scm.cache.Cache; import sonia.scm.cache.Cache;
import sonia.scm.cache.CacheManager; import sonia.scm.cache.CacheManager;
import sonia.scm.config.ScmConfiguration; import sonia.scm.config.ScmConfiguration;
import sonia.scm.io.ZipUnArchiver;
import sonia.scm.net.HttpClient; import sonia.scm.net.HttpClient;
import sonia.scm.security.SecurityContext; import sonia.scm.security.SecurityContext;
import sonia.scm.util.AssertUtil; import sonia.scm.util.AssertUtil;
@@ -59,6 +62,8 @@ import sonia.scm.util.Util;
//~--- JDK imports ------------------------------------------------------------ //~--- JDK imports ------------------------------------------------------------
import java.io.File;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
@@ -72,6 +77,7 @@ import java.util.LinkedHashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import javax.xml.bind.JAXB;
import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException; import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller; import javax.xml.bind.Unmarshaller;
@@ -82,7 +88,7 @@ import javax.xml.bind.Unmarshaller;
*/ */
@Singleton @Singleton
public class DefaultPluginManager public class DefaultPluginManager
implements PluginManager, ConfigChangedListener<ScmConfiguration> implements PluginManager, ConfigChangedListener<ScmConfiguration>
{ {
/** Field description */ /** Field description */
@@ -116,17 +122,16 @@ public class DefaultPluginManager
* @param clientProvider * @param clientProvider
*/ */
@Inject @Inject
public DefaultPluginManager( public DefaultPluginManager(SCMContextProvider context,
SCMContextProvider context, Provider<SecurityContext> securityContextProvicer,
Provider<SecurityContext> securityContextProvicer, ScmConfiguration configuration, PluginLoader pluginLoader,
ScmConfiguration configuration, PluginLoader pluginLoader, CacheManager cacheManager, Provider<HttpClient> clientProvider)
CacheManager cacheManager, Provider<HttpClient> clientProvider)
{ {
this.context = context; this.context = context;
this.securityContextProvicer = securityContextProvicer; this.securityContextProvicer = securityContextProvicer;
this.configuration = configuration; this.configuration = configuration;
this.cache = cacheManager.getCache(String.class, PluginCenter.class, this.cache = cacheManager.getCache(String.class, PluginCenter.class,
CACHE_NAME); CACHE_NAME);
this.clientProvider = clientProvider; this.clientProvider = clientProvider;
installedPlugins = new HashMap<String, Plugin>(); installedPlugins = new HashMap<String, Plugin>();
@@ -214,6 +219,50 @@ public class DefaultPluginManager
} }
} }
/**
* Method description
*
*
* @param packageStream
*
* @throws IOException
*/
@Override
public void installPackage(InputStream packageStream) throws IOException
{
SecurityUtil.assertIsAdmin(securityContextProvicer);
File tempDirectory = Files.createTempDir();
try
{
new ZipUnArchiver().extractArchive(packageStream, tempDirectory);
Plugin plugin = JAXB.unmarshal(new File(tempDirectory, "plugin.xml"),
Plugin.class);
// TODO check conditions
AetherPluginHandler aph = new AetherPluginHandler(this, context,
configuration);
Collection<PluginRepository> repositories =
Sets.newHashSet(new PluginRepository("package-repository",
"file://".concat(tempDirectory.getAbsolutePath())));
aph.setPluginRepositories(repositories);
aph.install(plugin.getInformation().getId());
plugin.getInformation().setState(PluginState.INSTALLED);
installedPlugins.put(plugin.getInformation().getId(), plugin);
}
finally
{
IOUtil.delete(tempDirectory);
}
}
/** /**
* Method description * Method description
* *
@@ -277,7 +326,7 @@ public class DefaultPluginManager
for (PluginInformation info : getInstalled()) for (PluginInformation info : getInstalled())
{ {
if (groupId.equals(info.getGroupId()) if (groupId.equals(info.getGroupId())
&& artefactId.equals(info.getArtifactId())) && artefactId.equals(info.getArtifactId()))
{ {
installed = info; installed = info;
@@ -453,7 +502,7 @@ public class DefaultPluginManager
} }
return url.replace("{version}", context.getVersion()).replace("{os}", return url.replace("{version}", context.getVersion()).replace("{os}",
os).replace("{arch}", arch); os).replace("{arch}", arch);
} }
/** /**
@@ -465,7 +514,7 @@ public class DefaultPluginManager
* @param filter * @param filter
*/ */
private void filter(Set<PluginInformation> target, private void filter(Set<PluginInformation> target,
Collection<PluginInformation> source, PluginFilter filter) Collection<PluginInformation> source, PluginFilter filter)
{ {
for (PluginInformation info : source) for (PluginInformation info : source)
{ {
@@ -588,7 +637,7 @@ public class DefaultPluginManager
if (pluginHandler == null) if (pluginHandler == null)
{ {
pluginHandler = new AetherPluginHandler(this, pluginHandler = new AetherPluginHandler(this,
SCMContext.getContext(), configuration); SCMContext.getContext(), configuration);
} }
pluginHandler.setPluginRepositories(center.getRepositories()); pluginHandler.setPluginRepositories(center.getRepositories());
@@ -643,7 +692,7 @@ public class DefaultPluginManager
PluginInformation installed = installedPlugin.getInformation(); PluginInformation installed = installedPlugin.getInformation();
if (isSamePlugin(available, installed) if (isSamePlugin(available, installed)
&& (installed.getState() == PluginState.CORE)) && (installed.getState() == PluginState.CORE))
{ {
core = true; core = true;
@@ -664,7 +713,7 @@ public class DefaultPluginManager
* @return * @return
*/ */
private boolean isNewer(PluginInformation available, private boolean isNewer(PluginInformation available,
PluginInformation installed) PluginInformation installed)
{ {
boolean result = false; boolean result = false;
PluginVersion version = PluginVersion.createVersion(available.getVersion()); PluginVersion version = PluginVersion.createVersion(available.getVersion());
@@ -689,7 +738,7 @@ public class DefaultPluginManager
private boolean isSamePlugin(PluginInformation p1, PluginInformation p2) private boolean isSamePlugin(PluginInformation p1, PluginInformation p2)
{ {
return p1.getGroupId().equals(p2.getGroupId()) return p1.getGroupId().equals(p2.getGroupId())
&& p1.getArtifactId().equals(p2.getArtifactId()); && p1.getArtifactId().equals(p2.getArtifactId());
} }
//~--- fields --------------------------------------------------------------- //~--- fields ---------------------------------------------------------------