* Plugins:

- Auto-update plugins after a router update
    - Add update-all button
This commit is contained in:
zzz
2012-01-15 21:15:08 +00:00
parent 9acabfb20f
commit fc6f4ecc74
6 changed files with 141 additions and 5 deletions

View File

@ -53,6 +53,10 @@ public class ConfigClientsHandler extends FormHandler {
installPlugin(); installPlugin();
return; return;
} }
if (_action.equals(_("Update All Installed Plugins"))) {
updateAllPlugins();
return;
}
// value // value
if (_action.startsWith("Start ")) { if (_action.startsWith("Start ")) {
String app = _action.substring(6); String app = _action.substring(6);
@ -321,6 +325,16 @@ public class ConfigClientsHandler extends FormHandler {
installPlugin(url); installPlugin(url);
} }
/** @since 0.8.13 */
private void updateAllPlugins() {
addFormNotice(_("Updating all plugins"));
PluginStarter.updateAll(_context);
// So that update() will post a status to the summary bar before we reload
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {}
}
private void installPlugin(String url) { private void installPlugin(String url) {
if ("true".equals(System.getProperty(UpdateHandler.PROP_UPDATE_IN_PROGRESS))) { if ("true".equals(System.getProperty(UpdateHandler.PROP_UPDATE_IN_PROGRESS))) {
addFormError(_("Plugin or update download already in progress.")); addFormError(_("Plugin or update download already in progress."));

View File

@ -22,10 +22,12 @@ import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
import net.i2p.router.Job; import net.i2p.router.Job;
import net.i2p.router.RouterContext; import net.i2p.router.RouterContext;
import net.i2p.router.RouterVersion;
import net.i2p.router.startup.ClientAppConfig; import net.i2p.router.startup.ClientAppConfig;
import net.i2p.router.startup.LoadClientAppsJob; import net.i2p.router.startup.LoadClientAppsJob;
import net.i2p.util.ConcurrentHashSet; import net.i2p.util.ConcurrentHashSet;
import net.i2p.util.FileUtil; import net.i2p.util.FileUtil;
import net.i2p.util.I2PAppThread;
import net.i2p.util.Log; import net.i2p.util.Log;
import net.i2p.util.Translate; import net.i2p.util.Translate;
import net.i2p.util.VersionComparator; import net.i2p.util.VersionComparator;
@ -63,9 +65,96 @@ public class PluginStarter implements Runnable {
} }
public void run() { public void run() {
if (_context.getBooleanPropertyDefaultTrue("plugins.autoUpdate") &&
(!Boolean.valueOf(System.getProperty(UpdateHandler.PROP_UPDATE_IN_PROGRESS)).booleanValue()) &&
(!RouterVersion.VERSION.equals(_context.getProperty("router.previousVersion"))))
updateAll(_context, true);
startPlugins(_context); startPlugins(_context);
} }
/**
* threaded
* @since 0.8.13
*/
static void updateAll(RouterContext ctx) {
Thread t = new I2PAppThread(new PluginUpdater(ctx), "PluginUpdater", true);
t.start();
}
/**
* thread
* @since 0.8.13
*/
private static class PluginUpdater implements Runnable {
private final RouterContext _ctx;
public PluginUpdater(RouterContext ctx) {
_ctx = ctx;
}
public void run() {
updateAll(_ctx, false);
}
}
/**
* inline
* @since 0.8.13
*/
private static void updateAll(RouterContext ctx, boolean delay) {
List<String> plugins = getPlugins();
Map<String, String> toUpdate = new HashMap();
for (String appName : plugins) {
Properties props = pluginProperties(ctx, appName);
String url = props.getProperty("updateURL");
if (url != null)
toUpdate.put(appName, url);
}
if (toUpdate.isEmpty())
return;
PluginUpdateChecker puc = PluginUpdateChecker.getInstance(ctx);
if (puc.isRunning())
return;
if (delay) {
// wait for proxy
System.setProperty(UpdateHandler.PROP_UPDATE_IN_PROGRESS, "true");
puc.setAppStatus(Messages.getString("Checking for plugin updates", ctx));
try {
Thread.sleep(3*60*1000);
} catch (InterruptedException ie) {}
System.setProperty(UpdateHandler.PROP_UPDATE_IN_PROGRESS, "false");
}
Log log = ctx.logManager().getLog(PluginStarter.class);
for (Map.Entry<String, String> entry : toUpdate.entrySet()) {
String appName = entry.getKey();
if (log.shouldLog(Log.WARN))
log.warn("Checking for update plugin: " + appName);
puc.update(appName);
do {
try {
Thread.sleep(5*1000);
} catch (InterruptedException ie) {}
} while (puc.isRunning());
if (!puc.isNewerAvailable()) {
if (log.shouldLog(Log.WARN))
log.warn("No update available for plugin: " + appName);
continue;
}
PluginUpdateHandler puh = PluginUpdateHandler.getInstance(ctx);
String url = entry.getValue();
if (log.shouldLog(Log.WARN))
log.warn("Updating plugin: " + appName);
puh.update(url);
do {
try {
Thread.sleep(5*1000);
} catch (InterruptedException ie) {}
} while (puh.isRunning());
}
}
/** this shouldn't throw anything */ /** this shouldn't throw anything */
static void startPlugins(RouterContext ctx) { static void startPlugins(RouterContext ctx) {
Log log = ctx.logManager().getLog(PluginStarter.class); Log log = ctx.logManager().getLog(PluginStarter.class);
@ -75,6 +164,9 @@ public class PluginStarter implements Runnable {
if (name.startsWith(PREFIX) && name.endsWith(ENABLED)) { if (name.startsWith(PREFIX) && name.endsWith(ENABLED)) {
if (Boolean.valueOf(props.getProperty(name)).booleanValue()) { if (Boolean.valueOf(props.getProperty(name)).booleanValue()) {
String app = name.substring(PREFIX.length(), name.lastIndexOf(ENABLED)); String app = name.substring(PREFIX.length(), name.lastIndexOf(ENABLED));
// plugins could have been started after update
if (isPluginRunning(app, ctx))
continue;
try { try {
if (!startPlugin(ctx, app)) if (!startPlugin(ctx, app))
log.error("Failed to start plugin: " + app); log.error("Failed to start plugin: " + app);

View File

@ -36,6 +36,7 @@ public class PluginUpdateChecker extends UpdateHandler {
private String _appName; private String _appName;
private String _oldVersion; private String _oldVersion;
private String _xpi2pURL; private String _xpi2pURL;
private volatile boolean _isNewerAvailable;
private static PluginUpdateChecker _instance; private static PluginUpdateChecker _instance;
public static final synchronized PluginUpdateChecker getInstance(RouterContext ctx) { public static final synchronized PluginUpdateChecker getInstance(RouterContext ctx) {
@ -49,12 +50,19 @@ public class PluginUpdateChecker extends UpdateHandler {
super(ctx); super(ctx);
} }
/** check all plugins */ /**
* check all plugins
* @deprecated not finished
*/
public void update() { public void update() {
Thread t = new I2PAppThread(new AllCheckerRunner(), "AllAppChecker", true); Thread t = new I2PAppThread(new AllCheckerRunner(), "AllAppChecker", true);
t.start(); t.start();
} }
/**
* check all plugins
* @deprecated not finished
*/
public class AllCheckerRunner implements Runnable { public class AllCheckerRunner implements Runnable {
public void run() { public void run() {
List<String> plugins = PluginStarter.getPlugins(); List<String> plugins = PluginStarter.getPlugins();
@ -85,12 +93,18 @@ public class PluginUpdateChecker extends UpdateHandler {
_xpi2pURL = xpi2pURL; _xpi2pURL = xpi2pURL;
_appName = appName; _appName = appName;
_oldVersion = oldVersion; _oldVersion = oldVersion;
_isNewerAvailable = false;
System.setProperty(PROP_UPDATE_IN_PROGRESS, "true"); System.setProperty(PROP_UPDATE_IN_PROGRESS, "true");
I2PAppThread update = new I2PAppThread(_pluginUpdateCheckerRunner, "AppChecker", true); I2PAppThread update = new I2PAppThread(_pluginUpdateCheckerRunner, "AppChecker", true);
update.start(); update.start();
} }
} }
/** @since 0.8.13 */
public void setAppStatus(String status) {
updateStatus(status);
}
public boolean isRunning() { public boolean isRunning() {
return _pluginUpdateCheckerRunner != null && _pluginUpdateCheckerRunner.isRunning(); return _pluginUpdateCheckerRunner != null && _pluginUpdateCheckerRunner.isRunning();
} }
@ -101,6 +115,11 @@ public class PluginUpdateChecker extends UpdateHandler {
return false; return false;
} }
/** @since 0.8.13 */
public boolean isNewerAvailable() {
return _isNewerAvailable;
}
private void scheduleStatusClean(String msg) { private void scheduleStatusClean(String msg) {
SimpleScheduler.getInstance().addEvent(new Cleaner(msg), 60*60*1000); SimpleScheduler.getInstance().addEvent(new Cleaner(msg), 60*60*1000);
} }
@ -126,6 +145,7 @@ public class PluginUpdateChecker extends UpdateHandler {
@Override @Override
protected void update() { protected void update() {
_isNewerAvailable = false;
updateStatus("<b>" + _("Checking for update of plugin {0}", _appName) + "</b>"); updateStatus("<b>" + _("Checking for update of plugin {0}", _appName) + "</b>");
// use the same settings as for updater // use the same settings as for updater
// always proxy, or else FIXME // always proxy, or else FIXME
@ -142,6 +162,10 @@ public class PluginUpdateChecker extends UpdateHandler {
} }
} }
public boolean isNewerAvailable() {
return _isNewerAvailable;
}
@Override @Override
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) { public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {
} }
@ -151,10 +175,12 @@ public class PluginUpdateChecker extends UpdateHandler {
String newVersion = TrustedUpdate.getVersionString(new ByteArrayInputStream(_baos.toByteArray())); String newVersion = TrustedUpdate.getVersionString(new ByteArrayInputStream(_baos.toByteArray()));
boolean newer = (new VersionComparator()).compare(newVersion, _oldVersion) > 0; boolean newer = (new VersionComparator()).compare(newVersion, _oldVersion) > 0;
String msg; String msg;
if (newer) if (newer) {
msg = "<b>" + _("New plugin version {0} is available", newVersion) + "</b>"; msg = "<b>" + _("New plugin version {0} is available", newVersion) + "</b>";
else _isNewerAvailable = true;
} else {
msg = "<b>" + _("No new version is available for plugin {0}", _appName) + "</b>"; msg = "<b>" + _("No new version is available for plugin {0}", _appName) + "</b>";
}
updateStatus(msg); updateStatus(msg);
scheduleStatusClean(msg); scheduleStatusClean(msg);
} }

View File

@ -93,7 +93,7 @@ public class PluginUpdateHandler extends UpdateHandler {
} }
private void scheduleStatusClean(String msg) { private void scheduleStatusClean(String msg) {
SimpleScheduler.getInstance().addEvent(new Cleaner(msg), 60*60*1000); SimpleScheduler.getInstance().addEvent(new Cleaner(msg), 20*60*1000);
} }
private class Cleaner implements SimpleTimer.TimedEvent { private class Cleaner implements SimpleTimer.TimedEvent {

View File

@ -127,7 +127,7 @@ public class UpdateHandler {
} }
public class UpdateRunner implements Runnable, EepGet.StatusListener { public class UpdateRunner implements Runnable, EepGet.StatusListener {
protected boolean _isRunning; protected volatile boolean _isRunning;
protected boolean done; protected boolean done;
protected EepGet _get; protected EepGet _get;
protected final DecimalFormat _pct = new DecimalFormat("0.0%"); protected final DecimalFormat _pct = new DecimalFormat("0.0%");

View File

@ -103,6 +103,7 @@ button span.hide{
<jsp:getProperty name="clientshelper" property="form2" /> <jsp:getProperty name="clientshelper" property="form2" />
<p><i><%=intl._("All changes require restart to take effect.")%></i> <p><i><%=intl._("All changes require restart to take effect.")%></i>
</p><hr><div class="formaction"> </p><hr><div class="formaction">
<input type="submit" class="cancel" name="foo" value="<%=intl._("Cancel")%>" />
<input type="submit" name="action" class="accept" value="<%=intl._("Save WebApp Configuration")%>" /> <input type="submit" name="action" class="accept" value="<%=intl._("Save WebApp Configuration")%>" />
</div></form></div> </div></form></div>
@ -114,6 +115,7 @@ button span.hide{
<input type="hidden" name="nonce" value="<%=pageNonce%>" > <input type="hidden" name="nonce" value="<%=pageNonce%>" >
<jsp:getProperty name="clientshelper" property="form3" /> <jsp:getProperty name="clientshelper" property="form3" />
<hr><div class="formaction"> <hr><div class="formaction">
<input type="submit" class="cancel" name="foo" value="<%=intl._("Cancel")%>" />
<input type="submit" name="action" class="accept" value="<%=intl._("Save Plugin Configuration")%>" /> <input type="submit" name="action" class="accept" value="<%=intl._("Save Plugin Configuration")%>" />
</div></form></div> </div></form></div>
@ -125,7 +127,9 @@ button span.hide{
<p> <p>
<input type="text" size="60" name="pluginURL" > <input type="text" size="60" name="pluginURL" >
</p><hr><div class="formaction"> </p><hr><div class="formaction">
<input type="submit" class="cancel" name="foo" value="<%=intl._("Cancel")%>" />
<input type="submit" name="action" class="download" value="<%=intl._("Install Plugin")%>" /> <input type="submit" name="action" class="download" value="<%=intl._("Install Plugin")%>" />
<input type="submit" name="action" class="reload" value="<%=intl._("Update All Installed Plugins")%>" />
</div></form></div> </div></form></div>
<% } %> <% } %>
</div></div></body></html> </div></div></body></html>