diff options
author | James Moger <james.moger@gitblit.com> | 2014-04-01 00:10:43 -0400 |
---|---|---|
committer | James Moger <james.moger@gitblit.com> | 2014-04-10 19:00:52 -0400 |
commit | e5d0bacbf746e09a9194822b231cb27090f58973 (patch) | |
tree | e6d7d2f96384aa89a4f965ad1f47fb130ec907a4 /src/main/java/com/gitblit/manager/PluginManager.java | |
parent | ec3b719dab4ae3c51d8bd52ab2b4176f82c5006f (diff) | |
download | gitblit-e5d0bacbf746e09a9194822b231cb27090f58973.tar.gz gitblit-e5d0bacbf746e09a9194822b231cb27090f58973.zip |
Implement simple JSON-based plugin registry and install command
Diffstat (limited to 'src/main/java/com/gitblit/manager/PluginManager.java')
-rw-r--r-- | src/main/java/com/gitblit/manager/PluginManager.java | 250 |
1 files changed, 245 insertions, 5 deletions
diff --git a/src/main/java/com/gitblit/manager/PluginManager.java b/src/main/java/com/gitblit/manager/PluginManager.java index e23aaec0..7b03f50d 100644 --- a/src/main/java/com/gitblit/manager/PluginManager.java +++ b/src/main/java/com/gitblit/manager/PluginManager.java @@ -15,31 +15,57 @@ */ package com.gitblit.manager; +import java.io.BufferedInputStream; import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.Proxy; +import java.net.URL; +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ro.fortsoft.pf4j.DefaultPluginManager; +import ro.fortsoft.pf4j.PluginVersion; import ro.fortsoft.pf4j.PluginWrapper; import com.gitblit.Keys; +import com.gitblit.models.PluginRegistry; +import com.gitblit.models.PluginRegistry.PluginRegistration; +import com.gitblit.models.PluginRegistry.PluginRelease; +import com.gitblit.utils.Base64; import com.gitblit.utils.FileUtils; +import com.gitblit.utils.JsonUtils; +import com.gitblit.utils.StringUtils; +import com.google.common.io.Files; +import com.google.common.io.InputSupplier; /** * The plugin manager maintains the lifecycle of plugins. It is exposed as * Dagger bean. The extension consumers supposed to retrieve plugin manager * from the Dagger DI and retrieve extensions provided by active plugins. - * + * * @author David Ostrovsky - * + * */ public class PluginManager extends DefaultPluginManager implements IPluginManager { private final Logger logger = LoggerFactory.getLogger(getClass()); - + private final IRuntimeManager runtimeManager; + // timeout defaults of Maven 3.0.4 in seconds + private int connectTimeout = 20; + + private int readTimeout = 12800; + public PluginManager(IRuntimeManager runtimeManager) { super(runtimeManager.getFileOrFolder(Keys.plugins.folder, "${baseFolder}/plugins")); this.runtimeManager = runtimeManager; @@ -60,13 +86,13 @@ public class PluginManager extends DefaultPluginManager implements IPluginManage stopPlugins(); return null; } - + @Override public boolean deletePlugin(PluginWrapper pw) { File folder = runtimeManager.getFileOrFolder(Keys.plugins.folder, "${baseFolder}/plugins"); File pluginFolder = new File(folder, pw.getPluginPath()); File pluginZip = new File(folder, pw.getPluginPath() + ".zip"); - + if (pluginFolder.exists()) { FileUtils.delete(pluginFolder); } @@ -75,4 +101,218 @@ public class PluginManager extends DefaultPluginManager implements IPluginManage } return true; } + + @Override + public boolean refreshRegistry() { + String dr = "http://gitblit.github.io/gitblit-registry/plugins.json"; + String url = runtimeManager.getSettings().getString(Keys.plugins.registry, dr); + try { + return download(url); + } catch (Exception e) { + logger.error(String.format("Failed to retrieve plugins.json from %s", url), e); + } + return false; + } + + protected List<PluginRegistry> getRegistries() { + List<PluginRegistry> list = new ArrayList<PluginRegistry>(); + File folder = runtimeManager.getFileOrFolder(Keys.plugins.folder, "${baseFolder}/plugins"); + FileFilter jsonFilter = new FileFilter() { + @Override + public boolean accept(File file) { + return !file.isDirectory() && file.getName().toLowerCase().endsWith(".json"); + } + }; + + File [] files = folder.listFiles(jsonFilter); + if (files == null || files.length == 0) { + // automatically retrieve the registry if we don't have a local copy + refreshRegistry(); + files = folder.listFiles(jsonFilter); + } + + if (files == null || files.length == 0) { + return list; + } + + for (File file : files) { + PluginRegistry registry = null; + try { + String json = FileUtils.readContent(file, "\n"); + registry = JsonUtils.fromJsonString(json, PluginRegistry.class); + } catch (Exception e) { + logger.error("Failed to deserialize " + file, e); + } + if (registry != null) { + list.add(registry); + } + } + return list; + } + + @Override + public List<PluginRegistration> getRegisteredPlugins() { + List<PluginRegistration> list = new ArrayList<PluginRegistration>(); + Map<String, PluginRegistration> map = new TreeMap<String, PluginRegistration>(); + for (PluginRegistry registry : getRegistries()) { + List<PluginRegistration> registrations = registry.registrations; + list.addAll(registrations); + for (PluginRegistration reg : registrations) { + reg.installedRelease = null; + map.put(reg.id, reg); + } + } + for (PluginWrapper pw : getPlugins()) { + String id = pw.getDescriptor().getPluginId(); + PluginVersion pv = pw.getDescriptor().getVersion(); + PluginRegistration reg = map.get(id); + if (reg != null) { + reg.installedRelease = pv.toString(); + } + } + return list; + } + + @Override + public PluginRegistration lookupPlugin(String idOrName) { + for (PluginRegistry registry : getRegistries()) { + PluginRegistration reg = registry.lookup(idOrName); + if (reg != null) { + return reg; + } + } + return null; + } + + @Override + public PluginRelease lookupRelease(String idOrName, String version) { + for (PluginRegistry registry : getRegistries()) { + PluginRegistration reg = registry.lookup(idOrName); + if (reg != null) { + PluginRelease pv; + if (StringUtils.isEmpty(version)) { + pv = reg.getCurrentRelease(); + } else { + pv = reg.getRelease(version); + } + if (pv != null) { + return pv; + } + } + } + return null; + } + + + /** + * Installs the plugin from the plugin version. + * + * @param pv + * @throws IOException + * @return true if successful + */ + @Override + public boolean installPlugin(PluginRelease pv) { + return installPlugin(pv.url); + } + + /** + * Installs the plugin from the url. + * + * @param url + * @return true if successful + */ + @Override + public boolean installPlugin(String url) { + try { + if (!download(url)) { + return false; + } + // TODO stop, unload, load + } catch (IOException e) { + logger.error("Failed to install plugin from " + url, e); + } + return true; + } + + /** + * Download a file to the plugins folder. + * + * @param url + * @return + * @throws IOException + */ + protected boolean download(String url) throws IOException { + File pFolder = runtimeManager.getFileOrFolder(Keys.plugins.folder, "${baseFolder}/plugins"); + File tmpFile = new File(pFolder, StringUtils.getSHA1(url) + ".tmp"); + if (tmpFile.exists()) { + tmpFile.delete(); + } + + URL u = new URL(url); + final URLConnection conn = getConnection(u); + + // try to get the server-specified last-modified date of this artifact + long lastModified = conn.getHeaderFieldDate("Last-Modified", System.currentTimeMillis()); + + Files.copy(new InputSupplier<InputStream>() { + @Override + public InputStream getInput() throws IOException { + return new BufferedInputStream(conn.getInputStream()); + } + }, tmpFile); + + File destFile = new File(pFolder, StringUtils.getLastPathElement(u.getPath())); + if (destFile.exists()) { + destFile.delete(); + } + tmpFile.renameTo(destFile); + destFile.setLastModified(lastModified); + + return true; + } + + protected URLConnection getConnection(URL url) throws IOException { + java.net.Proxy proxy = getProxy(url); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(proxy); + if (java.net.Proxy.Type.DIRECT != proxy.type()) { + String auth = getProxyAuthorization(url); + conn.setRequestProperty("Proxy-Authorization", auth); + } + + String username = null; + String password = null; + if (!StringUtils.isEmpty(username) && !StringUtils.isEmpty(password)) { + // set basic authentication header + String auth = Base64.encodeBytes((username + ":" + password).getBytes()); + conn.setRequestProperty("Authorization", "Basic " + auth); + } + + // configure timeouts + conn.setConnectTimeout(connectTimeout * 1000); + conn.setReadTimeout(readTimeout * 1000); + + switch (conn.getResponseCode()) { + case HttpURLConnection.HTTP_MOVED_TEMP: + case HttpURLConnection.HTTP_MOVED_PERM: + // handle redirects by closing this connection and opening a new + // one to the new location of the requested resource + String newLocation = conn.getHeaderField("Location"); + if (!StringUtils.isEmpty(newLocation)) { + logger.info("following redirect to {0}", newLocation); + conn.disconnect(); + return getConnection(new URL(newLocation)); + } + } + + return conn; + } + + protected Proxy getProxy(URL url) { + return java.net.Proxy.NO_PROXY; + } + + protected String getProxyAuthorization(URL url) { + return ""; + } } |