Added the ability to enable or disable plugins on startup

This commit is contained in:
Ziver Koc 2020-06-25 23:36:37 +02:00
parent b3f28c8276
commit dc829b9487
8 changed files with 190 additions and 34 deletions

34
Hal.iml
View file

@ -26,23 +26,7 @@
</content> </content>
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="Zutil" exported="" /> <orderEntry type="module" module-name="Zutil" />
<orderEntry type="module-library" exported="">
<library name="lib">
<CLASSES>
<root url="file://$MODULE_DIR$/lib" />
</CLASSES>
<JAVADOC />
<NATIVE>
<root url="file://$MODULE_DIR$/lib" />
</NATIVE>
<SOURCES>
<root url="jar://$USER_HOME$/.ideaLibSources/wzwave-0.0.3-sources.jar!/" />
<root url="jar://$USER_HOME$/.ideaLibSources/netty-all-4.0.46.Final-sources.jar!/" />
</SOURCES>
<jarDirectory url="file://$MODULE_DIR$/lib" recursive="false" />
</library>
</orderEntry>
<orderEntry type="module-library" exported=""> <orderEntry type="module-library" exported="">
<library> <library>
<CLASSES> <CLASSES>
@ -73,5 +57,21 @@
<SOURCES /> <SOURCES />
</library> </library>
</orderEntry> </orderEntry>
<orderEntry type="module-library" exported="">
<library name="lib">
<CLASSES>
<root url="file://$MODULE_DIR$/lib" />
</CLASSES>
<JAVADOC />
<NATIVE>
<root url="file://$MODULE_DIR$/lib" />
</NATIVE>
<SOURCES>
<root url="jar://$USER_HOME$/.ideaLibSources/wzwave-0.0.3-sources.jar!/" />
<root url="jar://$USER_HOME$/.ideaLibSources/netty-all-4.0.46.Final-sources.jar!/" />
</SOURCES>
<jarDirectory url="file://$MODULE_DIR$/lib" recursive="false" />
</library>
</orderEntry>
</component> </component>
</module> </module>

View file

@ -15,7 +15,7 @@
<property name="releaseDir" value="${buildRoot}/release" /> <property name="releaseDir" value="${buildRoot}/release" />
<property name="reportsDir" value="${buildRoot}/reports" /> <property name="reportsDir" value="${buildRoot}/reports" />
<property name="releaseJar" value="hal.jar" /> <property name="releaseJar" value="hal.jar" />
<property name="zutilVersion" value="1.0.249" /> <property name="zutilVersion" value="1.0.250" />
<!-- ________________________ CLASSPATH ________________________ --> <!-- ________________________ CLASSPATH ________________________ -->

Binary file not shown.

View file

@ -7,16 +7,37 @@
<table class="table table-hover table-condensed"> <table class="table table-hover table-condensed">
<thead> <thead>
<th class="col-md-2">Name</th> <th class="col-md-3">Name</th>
<th class="col-md-8">Version</th> <th class="col-md-8">Version</th>
<th class="col-md-1 text-right">Actions</th>
</thead> </thead>
{{#plugins}} {{#plugins}}
<tr> <tr>
<td>{{.getName()}}</td> <td>{{.getName()}}</td>
<td>{{.getVersion()}}</td> <td>{{.getVersion()}}</td>
<td>
<form method="POST">
<input type="hidden" name="action" value="modify">
<input type="hidden" name="action_id" value="{{.getName()}}">
<div class="btn-toolbar pull-right">
<input class="toggle-switch" type="checkbox" name="enabled"
data-size="mini" data-on-color="danger"
{{#.isEnabled()}}checked{{/.isEnabled()}} >
</div>
</form>
</td>
</tr> </tr>
{{/plugins}} {{/plugins}}
</table> </table>
</div> </div>
</div> </div>
</div> </div>
<script>
$(function (){
$(".toggle-switch").on("switchChange.bootstrapSwitch", function (event, state) {
$(this).closest('form').submit();
});
});
</script>

View file

@ -76,8 +76,9 @@ public class HalContext {
Integer.parseInt(dbConf.getProperty(PROPERTY_DB_VERSION)) : Integer.parseInt(dbConf.getProperty(PROPERTY_DB_VERSION)) :
-1); -1);
logger.info("DB version: "+ dbVersion); logger.info("DB version: "+ dbVersion);
if(defaultDBVersion > dbVersion ) { if(defaultDBVersion > dbVersion ) {
logger.info("Starting DB upgrade..."); logger.info("Starting DB upgrade from v" + dbVersion + " to v" + defaultDBVersion + "...");
if(dbFile != null){ if(dbFile != null){
File backupDB = FileUtil.getNextFile(dbFile); File backupDB = FileUtil.getNextFile(dbFile);
logger.fine("Backing up DB to: "+ backupDB); logger.fine("Backing up DB to: "+ backupDB);
@ -91,6 +92,7 @@ public class HalContext {
handler.setTargetDB(db); handler.setTargetDB(db);
logger.fine("Performing pre-upgrade activities"); logger.fine("Performing pre-upgrade activities");
//read upgrade path preferences from the reference database //read upgrade path preferences from the reference database
referenceDB.exec("SELECT * FROM db_version_history" referenceDB.exec("SELECT * FROM db_version_history"
+ " WHERE db_version <= " + defaultDBVersion + " WHERE db_version <= " + defaultDBVersion
@ -111,6 +113,7 @@ public class HalContext {
handler.upgrade(); handler.upgrade();
logger.fine("Performing post-upgrade activities"); logger.fine("Performing post-upgrade activities");
//read upgrade path preferences from the reference database //read upgrade path preferences from the reference database
referenceDB.exec("SELECT * FROM db_version_history" referenceDB.exec("SELECT * FROM db_version_history"
+ " WHERE db_version <= " + defaultDBVersion + " WHERE db_version <= " + defaultDBVersion
@ -225,7 +228,6 @@ public class HalContext {
/** /**
* For testing purposes. * For testing purposes.
* @param db
*/ */
public static void setDB(DBConnection db){ public static void setDB(DBConnection db){
HalContext.db = db; HalContext.db = db;

View file

@ -6,6 +6,7 @@ import se.hal.intf.HalWebPage;
import se.hal.intf.HalJsonPage; import se.hal.intf.HalJsonPage;
import se.hal.page.*; import se.hal.page.*;
import se.hal.struct.Event; import se.hal.struct.Event;
import se.hal.struct.PluginConfig;
import se.hal.struct.Sensor; import se.hal.struct.Sensor;
import se.hal.struct.TriggerFlow; import se.hal.struct.TriggerFlow;
import zutil.db.DBConnection; import zutil.db.DBConnection;
@ -14,16 +15,20 @@ import zutil.log.LogUtil;
import zutil.net.http.HttpServer; import zutil.net.http.HttpServer;
import zutil.net.http.page.HttpFilePage; import zutil.net.http.page.HttpFilePage;
import zutil.net.http.page.HttpRedirectPage; import zutil.net.http.page.HttpRedirectPage;
import zutil.plugin.PluginData;
import zutil.plugin.PluginManager; import zutil.plugin.PluginManager;
import java.sql.SQLException;
import java.util.*; import java.util.*;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.logging.Logger;
/** /**
* Main class for Hal * Main class for Hal
*/ */
public class HalServer { public class HalServer {
private static final Logger logger = LogUtil.getLogger();
private static ScheduledExecutorService daemonExecutor; private static ScheduledExecutorService daemonExecutor;
private static List<HalDaemon> daemons = new ArrayList<>(); private static List<HalDaemon> daemons = new ArrayList<>();
@ -42,15 +47,40 @@ public class HalServer {
// init DB and other configurations // init DB and other configurations
HalContext.initialize(); HalContext.initialize();
DBConnection db = HalContext.getDB(); DBConnection db = HalContext.getDB();
pluginManager = new PluginManager("./");
// init Managers // ------------------------------------
// Init Plugins
// ------------------------------------
logger.info("Looking for plugins.");
pluginManager = new PluginManager();
// Disable plugins based on settings
for (PluginData plugin : getPlugins()) {
PluginConfig pluginConfig = PluginConfig.getPluginConfig(db, plugin.getName());
if (pluginConfig != null && !pluginConfig.isEnabled()){
logger.info("Disabling plugin '" + plugin.getName() + "'.");
plugin.setEnabled(false);
}
}
// ------------------------------------
// Init Managers
// ------------------------------------
logger.info("Initializing managers.");
HalAlertManager.initialize(); HalAlertManager.initialize();
ControllerManager.initialize(pluginManager); ControllerManager.initialize(pluginManager);
TriggerManager.initialize(pluginManager); TriggerManager.initialize(pluginManager);
// ------------------------------------
// Import sensors,events and triggers // Import sensors,events and triggers
// ------------------------------------
logger.info("Initializing Sensors and Events.");
for(Sensor sensor : Sensor.getLocalSensors(db)){ for(Sensor sensor : Sensor.getLocalSensors(db)){
ControllerManager.getInstance().register(sensor); ControllerManager.getInstance().register(sensor);
} }
@ -63,14 +93,23 @@ public class HalServer {
} }
// ------------------------------------
// Init daemons // Init daemons
// ------------------------------------
logger.info("Initializing daemons.");
// We set only one thread for easier troubleshooting // We set only one thread for easier troubleshooting
daemonExecutor = Executors.newScheduledThreadPool(1); daemonExecutor = Executors.newScheduledThreadPool(1);
for (Iterator<HalDaemon> it=pluginManager.getObjectIterator(HalDaemon.class); it.hasNext(); ) for (Iterator<HalDaemon> it = pluginManager.getSingletonIterator(HalDaemon.class); it.hasNext(); )
registerDaemon(it.next()); registerDaemon(it.next());
// ------------------------------------
// Init http server // Init http server
// ------------------------------------
logger.info("Initializing HTTP Server.");
HalWebPage.getRootNav().createSubNav("Sensors"); HalWebPage.getRootNav().createSubNav("Sensors");
HalWebPage.getRootNav().createSubNav("Events").setWeight(100); HalWebPage.getRootNav().createSubNav("Events").setWeight(100);
HalWebPage.getRootNav().createSubNav("Settings").setWeight(200); HalWebPage.getRootNav().createSubNav("Settings").setWeight(200);
@ -79,22 +118,38 @@ public class HalServer {
http.setDefaultPage(new HttpFilePage(FileUtil.find("resource/web/"))); http.setDefaultPage(new HttpFilePage(FileUtil.find("resource/web/")));
http.setPage("/", new HttpRedirectPage("/map")); http.setPage("/", new HttpRedirectPage("/map"));
http.setPage(HalAlertManager.getInstance().getUrl(), HalAlertManager.getInstance()); http.setPage(HalAlertManager.getInstance().getUrl(), HalAlertManager.getInstance());
for (Iterator<HalWebPage> it = pluginManager.getObjectIterator(HalJsonPage.class); it.hasNext(); ) for (Iterator<HalWebPage> it = pluginManager.getSingletonIterator(HalJsonPage.class); it.hasNext(); )
registerPage(it.next()); registerPage(it.next());
for (Iterator<HalWebPage> it = pluginManager.getObjectIterator(HalWebPage.class); it.hasNext(); ) for (Iterator<HalWebPage> it = pluginManager.getSingletonIterator(HalWebPage.class); it.hasNext(); )
registerPage(it.next()); registerPage(it.next());
http.start(); http.start();
} }
public static PluginManager getPluginManager() { public static void setPluginEnabled(String name, boolean enabled) throws SQLException {
return pluginManager; DBConnection db = HalContext.getDB();
PluginConfig pluginConfig = PluginConfig.getPluginConfig(db, name);
if (pluginConfig == null)
pluginConfig = new PluginConfig(name);
logger.info("Plugin '" + name + "' has been " + (enabled ? "enabled" : "disabled") + ", change will take affect after restart.");
pluginManager.getPluginData(name).setEnabled(enabled);
pluginConfig.setEnabled(enabled);
pluginConfig.save(db);
} }
public static List<PluginData> getPlugins() {
return pluginManager.toArray();
}
public static void registerDaemon(HalDaemon daemon){ public static void registerDaemon(HalDaemon daemon){
daemons.add(daemon); daemons.add(daemon);
daemon.initiate(daemonExecutor); daemon.initiate(daemonExecutor);
} }
public static void registerPage(HalWebPage page){ public static void registerPage(HalWebPage page){
pages.add(page); pages.add(page);
http.setPage(page.getId(), page); http.setPage(page.getId(), page);

View file

@ -3,6 +3,11 @@ package se.hal.page;
import se.hal.HalContext; import se.hal.HalContext;
import se.hal.HalServer; import se.hal.HalServer;
import se.hal.intf.HalWebPage; import se.hal.intf.HalWebPage;
import se.hal.page.HalAlertManager.AlertLevel;
import se.hal.page.HalAlertManager.AlertTTL;
import se.hal.page.HalAlertManager.HalAlert;
import se.hal.struct.devicedata.SwitchEventData;
import zutil.ObjectUtil;
import zutil.db.DBConnection; import zutil.db.DBConnection;
import zutil.io.file.FileUtil; import zutil.io.file.FileUtil;
import zutil.parser.Templator; import zutil.parser.Templator;
@ -27,12 +32,17 @@ public class PluginConfigWebPage extends HalWebPage {
Map<String, String> request) Map<String, String> request)
throws Exception{ throws Exception{
DBConnection db = HalContext.getDB(); if (request.containsKey("action")) {
String name = request.get("action_id");
HalServer.setPluginEnabled(name,
(request.containsKey("enabled") && "on".equals(request.get("enabled"))));
PluginManager pluginManager = HalServer.getPluginManager(); HalAlertManager.getInstance().addAlert(new HalAlert(
AlertLevel.SUCCESS, "Successfully updated plugin " + name + ", change will take affect after restart.", AlertTTL.ONE_VIEW));
}
Templator tmpl = new Templator(FileUtil.find(TEMPLATE)); Templator tmpl = new Templator(FileUtil.find(TEMPLATE));
tmpl.set("plugins", pluginManager.toArray()); tmpl.set("plugins", HalServer.getPlugins());
return tmpl; return tmpl;
} }

View file

@ -0,0 +1,68 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2020 Ziver Koc
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package se.hal.struct;
import zutil.db.DBConnection;
import zutil.db.bean.DBBean;
import zutil.db.bean.DBBeanSQLResultHandler;
import java.sql.PreparedStatement;
import java.sql.SQLException;
@DBBean.DBTable(value="plugin")
public class PluginConfig extends DBBean {
private String name;
private boolean enabled;
/**
* @return a PluginConfig bean for the specific plugin name.
*/
public static PluginConfig getPluginConfig(DBConnection db, String name) throws SQLException {
PreparedStatement stmt = db.getPreparedStatement( "SELECT plugin.* FROM plugin WHERE name == ?" );
stmt.setString(1, name);
return DBConnection.exec(stmt, DBBeanSQLResultHandler.create(PluginConfig.class, db) );
}
public PluginConfig() {}
public PluginConfig(String name) {
this.name = name;
}
public String getName() {
return name;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}