From 903f21409b0bb61eda28e014a38ab0d9d2b151d5 Mon Sep 17 00:00:00 2001
From: srosse <none@none>
Date: Mon, 19 Nov 2012 13:47:41 +0100
Subject: [PATCH] OO-422: new layout for the administration tree, break the
 "system infromations" tabbed panel in single controllers under a main menu
 point, add a new system informations controller with some VIP values

---
 .../admin/_i18n/LocalStrings_de.properties    |   4 +
 .../olat/admin/cache/AllCachesController.java |   1 +
 .../sysinfo/CoreFunctionsController.java      |  86 ++++
 .../JavaEnvironmmentPropertiesController.java | 155 +++++++
 .../admin/sysinfo/JavaMemoryController.java   | 112 +++++
 .../admin/sysinfo/JavaThreadsController.java  | 129 ++++++
 .../olat/admin/sysinfo/JavaVMController.java  |  67 ++-
 .../olat/admin/sysinfo/SysinfoController.java | 382 ++++++------------
 .../admin/sysinfo/ThreadInfosManager.java     | 139 +++++++
 .../org/olat/admin/sysinfo/ThreadView.java    | 149 +++++++
 .../admin/sysinfo/_content/buildinfo.html     |  19 -
 .../olat/admin/sysinfo/_content/coreinfo.html |   5 +
 .../admin/sysinfo/_content/hibernateinfo.html |  21 +-
 .../olat/admin/sysinfo/_content/infomsg.html  |   8 +-
 .../admin/sysinfo/_content/jvm_envprops.html  |   4 +
 .../admin/sysinfo/_content/jvm_memory.html    |   5 +
 .../admin/sysinfo/_content/jvm_threads.html   |   5 +
 .../olat/admin/sysinfo/_content/memory.html   |   3 +
 .../olat/admin/sysinfo/_content/sysinfo.html  |  22 +-
 .../sysinfo/_i18n/LocalStrings_de.properties  |  34 ++
 .../admin/sysinfo/_spring/sysinfoContext.xml  |  30 +-
 .../generic/layout/GenericMainController.java |  19 +-
 .../restapi/system/ThreadsWebService.java     |  96 +----
 .../org/olat/restapi/system/vo/ThreadVO.java  |  31 +-
 .../org/olat/_spring/extensionContext.xml     |  50 ++-
 .../java/org/olat/restapi/SystemTest.java     |   2 -
 26 files changed, 1138 insertions(+), 440 deletions(-)
 create mode 100644 src/main/java/org/olat/admin/sysinfo/CoreFunctionsController.java
 create mode 100644 src/main/java/org/olat/admin/sysinfo/JavaEnvironmmentPropertiesController.java
 create mode 100644 src/main/java/org/olat/admin/sysinfo/JavaMemoryController.java
 create mode 100644 src/main/java/org/olat/admin/sysinfo/JavaThreadsController.java
 create mode 100644 src/main/java/org/olat/admin/sysinfo/ThreadInfosManager.java
 create mode 100644 src/main/java/org/olat/admin/sysinfo/ThreadView.java
 delete mode 100644 src/main/java/org/olat/admin/sysinfo/_content/buildinfo.html
 create mode 100644 src/main/java/org/olat/admin/sysinfo/_content/coreinfo.html
 create mode 100644 src/main/java/org/olat/admin/sysinfo/_content/jvm_envprops.html
 create mode 100644 src/main/java/org/olat/admin/sysinfo/_content/jvm_memory.html
 create mode 100644 src/main/java/org/olat/admin/sysinfo/_content/jvm_threads.html
 create mode 100644 src/main/java/org/olat/admin/sysinfo/_content/memory.html

diff --git a/src/main/java/org/olat/admin/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/admin/_i18n/LocalStrings_de.properties
index 9ae4fde885f..21654c60793 100644
--- a/src/main/java/org/olat/admin/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/admin/_i18n/LocalStrings_de.properties
@@ -36,6 +36,8 @@ menu.deletedusers=Gel\u00F6schte Benutzer
 menu.deletedusers.alt=Benutzer, die in OLAT gel\u00F6scht wurden
 menu.devel=Development
 menu.devel.alt=Development tools
+menu.javavm=Java VM Infos
+menu.javavm.alt=Java Virtual Machine informations
 menu.snoop=Snoop user request
 menu.snoop.alt=Snoop user requests
 menu.groupmanagergroup=Gruppenverwalter
@@ -44,6 +46,8 @@ menu.i18n=Sprachen
 menu.i18n.alt=Konfiguration der Standard- und aktivierten Systemsprachen
 menu.imadmin=Instant-Messaging
 menu.imadmin.alt=Instant-Messaging administrieren
+menu.infomsg=Info messages
+menu.infomsg.alt=Info messages
 menu.jmx=JMX
 menu.jmx.alt=JMW-Werte betrachten
 menu.errors=Errors
diff --git a/src/main/java/org/olat/admin/cache/AllCachesController.java b/src/main/java/org/olat/admin/cache/AllCachesController.java
index 0059ab53594..7e63fc8ee00 100644
--- a/src/main/java/org/olat/admin/cache/AllCachesController.java
+++ b/src/main/java/org/olat/admin/cache/AllCachesController.java
@@ -84,6 +84,7 @@ public class AllCachesController extends BasicController {
 		myContent = createVelocityContainer("index");
 		
 		TableGuiConfiguration tableConfig = new TableGuiConfiguration();
+		tableConfig.setDownloadOffered(true);
 		
 		tableCtr = new TableController(tableConfig, ureq, getWindowControl(), getTranslator());		
 		tableCtr.addColumnDescriptor(new DefaultColumnDescriptor("cache.name", 0, null, ureq.getLocale()));
diff --git a/src/main/java/org/olat/admin/sysinfo/CoreFunctionsController.java b/src/main/java/org/olat/admin/sysinfo/CoreFunctionsController.java
new file mode 100644
index 00000000000..1244dbf0ba3
--- /dev/null
+++ b/src/main/java/org/olat/admin/sysinfo/CoreFunctionsController.java
@@ -0,0 +1,86 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.admin.sysinfo;
+
+import org.olat.core.CoreSpringFactory;
+import org.olat.core.commons.chiefcontrollers.BaseChiefController;
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.form.flexible.FormItemContainer;
+import org.olat.core.gui.components.form.flexible.elements.MultipleSelectionElement;
+import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
+import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer;
+import org.olat.core.gui.control.Controller;
+import org.olat.core.gui.control.WindowControl;
+import org.olat.core.servlets.WebDAVManager;
+import org.olat.restapi.RestModule;
+
+/**
+ * 
+ * Initial date: 19.11.2012<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class CoreFunctionsController extends FormBasicController {
+	
+	/**
+	 * @param ureq
+	 * @param wControl
+	 */
+	public CoreFunctionsController(UserRequest ureq, WindowControl wControl) {
+		super(ureq, wControl, "coreinfo");
+
+		initForm(ureq);
+	}
+
+	@Override
+	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
+
+		//server informations
+		FormLayoutContainer serverCont = FormLayoutContainer.createDefaultFormLayout("functions", getTranslator());
+		formLayout.add(serverCont);
+		formLayout.add("functions", serverCont);
+
+		MultipleSelectionElement clusterEl
+			= uifactory.addCheckboxesHorizontal("webdav", "core.webdav", serverCont, new String[]{"xx"}, new String[]{""}, null);
+		clusterEl.setEnabled(false);
+		clusterEl.select("xx", WebDAVManager.getInstance().isEnabled());
+
+		MultipleSelectionElement jsMathEl
+			= uifactory.addCheckboxesHorizontal("jsmath", "core.jsMath", serverCont, new String[]{"xx"}, new String[]{""}, null);
+		jsMathEl.setEnabled(false);
+		jsMathEl.select("xx", BaseChiefController.isJsMathEnabled());
+		
+		MultipleSelectionElement restEl
+		= uifactory.addCheckboxesHorizontal("restapi", "core.restapi", serverCont, new String[]{"xx"}, new String[]{""}, null);
+		restEl.setEnabled(false);
+		RestModule restModule = CoreSpringFactory.getImpl(RestModule.class);
+		restEl.select("xx", restModule.isEnabled());
+	
+	}
+	
+	protected void doDispose() {
+		//
+	}
+
+	@Override
+	protected void formOK(UserRequest ureq) {
+		//
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/admin/sysinfo/JavaEnvironmmentPropertiesController.java b/src/main/java/org/olat/admin/sysinfo/JavaEnvironmmentPropertiesController.java
new file mode 100644
index 00000000000..2194de72f9c
--- /dev/null
+++ b/src/main/java/org/olat/admin/sysinfo/JavaEnvironmmentPropertiesController.java
@@ -0,0 +1,155 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.admin.sysinfo;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.Component;
+import org.olat.core.gui.components.table.DefaultColumnDescriptor;
+import org.olat.core.gui.components.table.DefaultTableDataModel;
+import org.olat.core.gui.components.table.TableController;
+import org.olat.core.gui.components.table.TableGuiConfiguration;
+import org.olat.core.gui.components.velocity.VelocityContainer;
+import org.olat.core.gui.control.Event;
+import org.olat.core.gui.control.WindowControl;
+import org.olat.core.gui.control.controller.BasicController;
+
+/**
+ * 
+ * Initial date: 19.11.2012<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class JavaEnvironmmentPropertiesController extends BasicController {
+	private static final int lineCut = 100;
+	
+	private final TableController tableCtr;
+	private final VelocityContainer mainVC;
+	
+	public JavaEnvironmmentPropertiesController(UserRequest ureq, WindowControl wControl) {
+		super(ureq, wControl);
+		
+		mainVC = createVelocityContainer("jvm_envprops");
+		
+		TableGuiConfiguration tableConfig = new TableGuiConfiguration();
+		tableConfig.setDownloadOffered(true);
+		tableConfig.setPageingEnabled(false);
+		tableCtr = new TableController(tableConfig, ureq, getWindowControl(), getTranslator());
+		tableCtr.addColumnDescriptor(new DefaultColumnDescriptor(Cols.name.i18nKey(), Cols.name.ordinal(), null, getLocale()));
+		tableCtr.addColumnDescriptor(new DefaultColumnDescriptor(Cols.value.i18nKey(), Cols.value.ordinal(), null, getLocale()));
+		listenTo(tableCtr);
+		loadModel();
+		mainVC.put("javaenv", tableCtr.getInitialComponent());
+		
+		putInitialPanel(mainVC);
+	}
+	
+	private void loadModel() {
+		Properties p = System.getProperties();
+		List<NameValuePair> pairs = new ArrayList<NameValuePair>(p.size());
+		for(Map.Entry<Object,Object> entry : p.entrySet()) {
+			String name = entry.getKey().toString();
+			String value = entry.getValue().toString();
+			pairs.add(new NameValuePair(name, value));
+		}
+		tableCtr.setTableDataModel(new PropertiesDataModel(pairs));
+	}
+	
+	@Override
+	protected void doDispose() {
+		//
+	}
+
+	@Override
+	protected void event(UserRequest ureq, Component source, Event event) {
+		//
+	}
+
+	private enum Cols {
+		name("java.envprops.name"),
+		value("java.envprops.value");
+		
+		private final String i18nKey;
+		
+		private Cols(String i18nKey) {
+			this.i18nKey = i18nKey;
+		}
+		
+		public String i18nKey() {
+			return i18nKey;
+		}
+	}
+	
+	private static class NameValuePair {
+		private final String name;
+		private final String value;
+		
+		public NameValuePair(String name, String value) {
+			this.name = name;
+			this.value = value;
+		}
+
+		public String getName() {
+			return name;
+		}
+
+		public String getValue() {
+			return value;
+		}
+	}
+
+	private static class PropertiesDataModel extends DefaultTableDataModel<NameValuePair> {
+		public PropertiesDataModel(List<NameValuePair> threads) {
+			super(threads);
+		}
+
+		@Override
+		public int getColumnCount() {
+			return 5;
+		}
+
+		@Override
+		public Object getValueAt(int row, int col) {
+			NameValuePair view = getObject(row);
+			switch(Cols.values()[col]) {
+				case name: return view.getName();
+				case value: {
+					String value = view.getValue();
+					if(value.length() < lineCut) {
+						return value;
+					}
+					StringBuilder props = new StringBuilder(value.length() + 50);
+					while (value.length() > lineCut) {
+						value = (props.length() == 0 ? "" : "<br />") + value.substring(lineCut);
+						props.append(value.substring(0,	value.length() > lineCut ? lineCut : value.length()));
+					}
+					return props.toString();
+				}
+				default: return "ERROR";
+			}
+		}
+	}
+	
+
+}
diff --git a/src/main/java/org/olat/admin/sysinfo/JavaMemoryController.java b/src/main/java/org/olat/admin/sysinfo/JavaMemoryController.java
new file mode 100644
index 00000000000..142bacad147
--- /dev/null
+++ b/src/main/java/org/olat/admin/sysinfo/JavaMemoryController.java
@@ -0,0 +1,112 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.admin.sysinfo;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.MemoryPoolMXBean;
+import java.lang.management.MemoryType;
+import java.lang.management.MemoryUsage;
+import java.util.List;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.Component;
+import org.olat.core.gui.components.link.Link;
+import org.olat.core.gui.components.link.LinkFactory;
+import org.olat.core.gui.components.velocity.VelocityContainer;
+import org.olat.core.gui.control.Event;
+import org.olat.core.gui.control.WindowControl;
+import org.olat.core.gui.control.controller.BasicController;
+import org.olat.core.util.StringHelper;
+
+/**
+ * 
+ * Initial date: 19.11.2012<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class JavaMemoryController extends BasicController {
+
+	private final Link gcButton;
+	private final VelocityContainer mainVC;
+	
+	public JavaMemoryController(UserRequest ureq, WindowControl wControl) {
+		super(ureq, wControl);
+		
+		mainVC = createVelocityContainer("jvm_memory");
+		gcButton = LinkFactory.createButton("run.gc", mainVC, this);
+
+		loaddModel();
+		putInitialPanel(mainVC);
+	}
+	
+	private void loaddModel() {
+		mainVC.contextPut("memory", getMemoryInfos());
+	}
+	
+	@Override
+	protected void doDispose() {
+		//
+	}
+
+	@Override
+	protected void event(UserRequest ureq, Component source, Event event) {
+		if (source == gcButton){
+			Runtime.getRuntime().gc();
+			getWindowControl().setInfo("Garbage collection done.");
+			loaddModel();
+		}
+	}
+	
+	private String getMemoryInfos() {
+		Runtime r = Runtime.getRuntime();
+		StringBuilder sb = new StringBuilder();
+		appendFormattedKeyValue(sb, "Processors", new Integer(r.availableProcessors()));
+		appendFormattedKeyValue(sb, "Total Memory", StringHelper.formatMemory(r.totalMemory()));
+		appendFormattedKeyValue(sb, "Free Memory", StringHelper.formatMemory(r.freeMemory()));
+		appendFormattedKeyValue(sb, "Max Memory", StringHelper.formatMemory(r.maxMemory()));
+		
+		sb.append("<br />Detailed Memory Information (Init/Used/Max)<br/> ");
+		List<MemoryPoolMXBean> pools = ManagementFactory.getMemoryPoolMXBeans();
+		for(MemoryPoolMXBean item:pools) {
+		    String name = item.getName();
+		    MemoryType type = item.getType();
+		    appendFormattedKeyValue(sb, name, " Type: " + type);
+		    MemoryUsage usage = item.getUsage();
+		    appendFormattedKeyValue(sb, "Usage", StringHelper.formatMemory(usage.getInit()) + "/" + StringHelper.formatMemory(usage.getUsed()) + "/" + StringHelper.formatMemory(usage.getMax()));
+		    MemoryUsage peak = item.getPeakUsage();
+		    appendFormattedKeyValue(sb, "Peak", StringHelper.formatMemory(peak.getInit()) + "/" + StringHelper.formatMemory(peak.getUsed()) + "/" + StringHelper.formatMemory(peak.getMax()));
+		    MemoryUsage collections = item.getCollectionUsage();
+		    if (collections!= null){
+		    	appendFormattedKeyValue(sb, "Collections", StringHelper.formatMemory(collections.getInit()) + "/" + StringHelper.formatMemory(collections.getUsed()) + "/" + StringHelper.formatMemory(collections.getMax()));
+		    }
+		    sb.append("<hr/>");
+		}
+		
+		return sb.toString();
+	}
+	
+	private void appendFormattedKeyValue(StringBuilder sb, String key, Object value) {
+		sb.append("&nbsp;&nbsp;&nbsp;<b>");
+		sb.append(key);
+		sb.append(":</b>&nbsp;");
+		sb.append(value);
+		sb.append("<br />");
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/admin/sysinfo/JavaThreadsController.java b/src/main/java/org/olat/admin/sysinfo/JavaThreadsController.java
new file mode 100644
index 00000000000..1b414eab971
--- /dev/null
+++ b/src/main/java/org/olat/admin/sysinfo/JavaThreadsController.java
@@ -0,0 +1,129 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.admin.sysinfo;
+
+import java.util.List;
+
+import org.olat.core.CoreSpringFactory;
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.Component;
+import org.olat.core.gui.components.table.DefaultColumnDescriptor;
+import org.olat.core.gui.components.table.DefaultTableDataModel;
+import org.olat.core.gui.components.table.TableController;
+import org.olat.core.gui.components.table.TableGuiConfiguration;
+import org.olat.core.gui.components.velocity.VelocityContainer;
+import org.olat.core.gui.control.Event;
+import org.olat.core.gui.control.WindowControl;
+import org.olat.core.gui.control.controller.BasicController;
+
+/**
+ * 
+ * Initial date: 19.11.2012<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class JavaThreadsController extends BasicController {
+
+	private final TableController tableCtr;
+	private final VelocityContainer mainVC;
+	
+	private final ThreadInfosManager threadInfosManager;
+	
+	public JavaThreadsController(UserRequest ureq, WindowControl wControl) {
+		super(ureq, wControl);
+		
+		threadInfosManager = CoreSpringFactory.getImpl(ThreadInfosManager.class);
+		
+		mainVC = createVelocityContainer("jvm_threads");
+
+		TableGuiConfiguration tableConfig = new TableGuiConfiguration();
+		tableConfig.setDownloadOffered(true);
+		tableConfig.setPageingEnabled(false);
+		tableCtr = new TableController(tableConfig, ureq, getWindowControl(), getTranslator());
+		tableCtr.addColumnDescriptor(new DefaultColumnDescriptor(Cols.name.i18nKey(), Cols.name.ordinal(), null, getLocale()));
+		tableCtr.addColumnDescriptor(new DefaultColumnDescriptor(Cols.state.i18nKey(), Cols.state.ordinal(), null, getLocale()));
+		tableCtr.addColumnDescriptor(new DefaultColumnDescriptor(Cols.cpuPercent.i18nKey(), Cols.cpuPercent.ordinal(), null, getLocale()));		
+		tableCtr.addColumnDescriptor(new DefaultColumnDescriptor(Cols.cpuTime.i18nKey(), Cols.cpuTime.ordinal(), null, getLocale()));
+		listenTo(tableCtr);
+		loadModel();
+		mainVC.put("threads", tableCtr.getInitialComponent());
+		
+		putInitialPanel(mainVC);
+	}
+	
+	private void loadModel() {
+		List<ThreadView> threads = threadInfosManager.getThreadViews();
+		ThreadsDataModel model = new ThreadsDataModel(threads);
+		tableCtr.setTableDataModel(model);
+	}
+	
+	@Override
+	protected void doDispose() {
+		//
+	}
+
+	@Override
+	protected void event(UserRequest ureq, Component source, Event event) {
+		//
+	}
+
+	private enum Cols {
+		name("java.thread.name"),
+		state("java.thread.alive"),
+		cpuPercent("java.thread.cpu.percent"),
+		cpuTime("java.thread.cpu.time");
+		
+		private final String i18nKey;
+		
+		private Cols(String i18nKey) {
+			this.i18nKey = i18nKey;
+		}
+		
+		public String i18nKey() {
+			return i18nKey;
+		}
+	}
+
+	private static class ThreadsDataModel extends DefaultTableDataModel<ThreadView> {
+		public ThreadsDataModel(List<ThreadView> threads) {
+			super(threads);
+		}
+
+		@Override
+		public int getColumnCount() {
+			return 5;
+		}
+
+		@Override
+		public Object getValueAt(int row, int col) {
+			ThreadView view = getObject(row);
+			switch(Cols.values()[col]) {
+				case name: return view.getName();
+				case state: return view.getState().name();
+				case cpuPercent: return view.getCpuUsagePercent();
+				case cpuTime: {
+					long timeInNanoSeconds = view.getCpuTime();
+					return (double)timeInNanoSeconds / (1000.0d * 1000.0d * 1000.0d);
+				}
+				default: return "ERROR";
+			}
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/admin/sysinfo/JavaVMController.java b/src/main/java/org/olat/admin/sysinfo/JavaVMController.java
index acf4a9a7618..2d2527c94ca 100644
--- a/src/main/java/org/olat/admin/sysinfo/JavaVMController.java
+++ b/src/main/java/org/olat/admin/sysinfo/JavaVMController.java
@@ -21,6 +21,12 @@ package org.olat.admin.sysinfo;
 
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
+import org.olat.core.gui.components.link.Link;
+import org.olat.core.gui.components.link.LinkFactory;
+import org.olat.core.gui.components.segmentedview.SegmentViewComponent;
+import org.olat.core.gui.components.segmentedview.SegmentViewEvent;
+import org.olat.core.gui.components.segmentedview.SegmentViewFactory;
+import org.olat.core.gui.components.velocity.VelocityContainer;
 import org.olat.core.gui.control.Event;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.core.gui.control.controller.BasicController;
@@ -33,10 +39,32 @@ import org.olat.core.gui.control.controller.BasicController;
  */
 public class JavaVMController extends BasicController {
 	
+	private final Link memoryLink, threadsLink, envPropsLink;
+	private final SegmentViewComponent segmentView;
+	private final VelocityContainer mainVC;
+	
+	private JavaMemoryController memoryCtrl;
+	private JavaThreadsController threadsCtrl;
+	private JavaEnvironmmentPropertiesController envPropsCtrl;
+	
 	public JavaVMController(UserRequest ureq, WindowControl wControl) {
 		super(ureq, wControl);
+
+		mainVC = createVelocityContainer("segments");
 		
+		segmentView = SegmentViewFactory.createSegmentView("segments", mainVC, this);
+		memoryLink = LinkFactory.createLink("java.memory", mainVC, this);
+		segmentView.addSegment(memoryLink, true);
+		
+		threadsLink = LinkFactory.createLink("java.threads", mainVC, this);
+		segmentView.addSegment(threadsLink, false);
 
+		envPropsLink = LinkFactory.createLink("java.envProps", mainVC, this);
+		segmentView.addSegment(envPropsLink, false);
+		
+		mainVC.put("segments", segmentView);
+		doOpenMemory(ureq);
+		putInitialPanel(mainVC);
 	}
 	
 	@Override
@@ -46,12 +74,43 @@ public class JavaVMController extends BasicController {
 
 	@Override
 	protected void event(UserRequest ureq, Component source, Event event) {
-		//
+		if(source == segmentView) {
+			if(event instanceof SegmentViewEvent) {
+				SegmentViewEvent sve = (SegmentViewEvent)event;
+				String segmentCName = sve.getComponentName();
+				Component clickedLink = mainVC.getComponent(segmentCName);
+				if (clickedLink == memoryLink) {
+					doOpenMemory(ureq);
+				} else if (clickedLink == threadsLink) {
+					doOpenThreads(ureq);
+				} else if (clickedLink == envPropsLink) {
+					doOpenEnvProps(ureq);
+				}
+			}
+		}
 	}
-
-
-
 	
+	private void doOpenMemory(UserRequest ureq) {
+		if(memoryCtrl == null) {
+			memoryCtrl = new JavaMemoryController(ureq, getWindowControl());
+			listenTo(memoryCtrl);
+		}
+		mainVC.put("segmentCmp", memoryCtrl.getInitialComponent());
+	}
 
+	private void doOpenThreads(UserRequest ureq) {
+		if(threadsCtrl == null) {
+			threadsCtrl = new JavaThreadsController(ureq, getWindowControl());
+			listenTo(threadsCtrl);
+		}
+		mainVC.put("segmentCmp", threadsCtrl.getInitialComponent());
+	}
 
+	private void doOpenEnvProps(UserRequest ureq) {
+		if(envPropsCtrl == null) {
+			envPropsCtrl = new JavaEnvironmmentPropertiesController(ureq, getWindowControl());
+			listenTo(envPropsCtrl);
+		}
+		mainVC.put("segmentCmp", envPropsCtrl.getInitialComponent());
+	}
 }
diff --git a/src/main/java/org/olat/admin/sysinfo/SysinfoController.java b/src/main/java/org/olat/admin/sysinfo/SysinfoController.java
index a92912b368c..811f66f4a3b 100644
--- a/src/main/java/org/olat/admin/sysinfo/SysinfoController.java
+++ b/src/main/java/org/olat/admin/sysinfo/SysinfoController.java
@@ -28,45 +28,25 @@ package org.olat.admin.sysinfo;
 import java.io.File;
 import java.io.IOException;
 import java.lang.management.ManagementFactory;
-import java.lang.management.MemoryPoolMXBean;
-import java.lang.management.MemoryType;
-import java.lang.management.MemoryUsage;
-import java.text.SimpleDateFormat;
+import java.lang.management.MemoryMXBean;
+import java.util.Calendar;
 import java.util.Date;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
 
 import org.olat.basesecurity.BaseSecurity;
-import org.olat.basesecurity.BaseSecurityManager;
-import org.olat.basesecurity.Constants;
 import org.olat.core.CoreSpringFactory;
-import org.olat.core.commons.chiefcontrollers.BaseChiefController;
 import org.olat.core.dispatcher.DispatcherAction;
 import org.olat.core.gui.UserRequest;
-import org.olat.core.gui.components.Component;
-import org.olat.core.gui.components.link.Link;
-import org.olat.core.gui.components.link.LinkFactory;
-import org.olat.core.gui.components.tabbedpane.TabbedPane;
-import org.olat.core.gui.components.tabbedpane.TabbedPaneChangedEvent;
-import org.olat.core.gui.components.velocity.VelocityContainer;
+import org.olat.core.gui.components.form.flexible.FormItemContainer;
+import org.olat.core.gui.components.form.flexible.elements.MultipleSelectionElement;
+import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
+import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer;
 import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.DefaultController;
-import org.olat.core.gui.control.Event;
 import org.olat.core.gui.control.WindowControl;
-import org.olat.core.gui.control.controller.BasicController;
-import org.olat.core.gui.control.generic.dtabs.Activateable2;
 import org.olat.core.helpers.Settings;
-import org.olat.core.id.context.ContextEntry;
-import org.olat.core.id.context.StateEntry;
-import org.olat.core.logging.OLATSecurityException;
-import org.olat.core.servlets.WebDAVManager;
+import org.olat.core.util.Formatter;
 import org.olat.core.util.StringHelper;
 import org.olat.core.util.WebappHelper;
-import org.olat.core.util.resource.OresHelper;
 
 
 /**
@@ -75,255 +55,147 @@ import org.olat.core.util.resource.OresHelper;
 *
 * @author Felix Jost
 */
-public class SysinfoController extends BasicController implements Activateable2 {
-
-	private static final String ACTION_INFOMSG = "infomsg";
-	private static final String ACTION_SYSINFO = "sysinfo";
-
-	private VelocityContainer mySysinfo;
-
-	private TabbedPane tabbedPane;
-	private Link gcButton;
-	private Controller clusterController;
-	private Controller infoMsgCtrl;
+public class SysinfoController extends FormBasicController {
+	
+	private final BaseSecurity securityManager;
 	
 	/**
 	 * @param ureq
 	 * @param wControl
 	 */
 	public SysinfoController(UserRequest ureq, WindowControl wControl) {
-		super(ureq, wControl);
-
-		BaseSecurity mgr = BaseSecurityManager.getInstance();
-		if (!mgr.isIdentityPermittedOnResourceable(
-				ureq.getIdentity(), 
-				Constants.PERMISSION_ACCESS, 
-				OresHelper.lookupType(this.getClass())))
-			throw new OLATSecurityException("Insufficient permissions to access SysinfoController");
-
-		
-		mySysinfo = createVelocityContainer("sysinfo");
-		gcButton = LinkFactory.createButton("run.gc", mySysinfo, this);
-		// add system startup time
-		SimpleDateFormat startupTimeFormatter = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z", ureq.getLocale());
-		mySysinfo.contextPut("startupTime", startupTimeFormatter.format(new Date(WebappHelper.getTimeOfServerStartup())));		
+		super(ureq, wControl, "sysinfo");
 		
-		
-
-		
-		//info message controller has two implementations (SingleVM or cluster)
-		InfoMessageManager InfoMgr = (InfoMessageManager)CoreSpringFactory.getBean(InfoMessageManager.class);
-		infoMsgCtrl = InfoMgr.getInfoMessageController(ureq, getWindowControl());
-			
-		tabbedPane = new TabbedPane("tp", ureq.getLocale());
-		tabbedPane.addTab(ACTION_INFOMSG,infoMsgCtrl.getInitialComponent());
-		//fxdiff: FXOLAT-79 check fxadmin-rights
-		tabbedPane.addTab(ACTION_SYSINFO, mySysinfo);
-		
-		//fxdiff: no cluster anyway:
-//		AutoCreator controllerCreator = (AutoCreator)CoreSpringFactory.getBean("clusterAdminControllerCreator");
-//		clusterController = controllerCreator.createController(ureq, wControl);
-//		tabbedPane.addTab("Cluster", clusterController.getInitialComponent());
-		
-		VelocityContainer myBuildinfo = createVelocityContainer("buildinfo");
-		fillBuildInfoTab(myBuildinfo);		
-		tabbedPane.addTab("buildinfo", myBuildinfo);
+		securityManager = CoreSpringFactory.getImpl(BaseSecurity.class);
 
-		tabbedPane.addListener(this);
-		putInitialPanel(tabbedPane);
+		initForm(ureq);
 	}
 
-	private void fillBuildInfoTab(VelocityContainer myBuildinfo) {
-		List<Map> properties = new LinkedList<Map>();
-		Map<String, String> m = new HashMap<String, String>();
-		m.put("key", "Version");
-		m.put("value", Settings.getFullVersionInfo());
-		properties.add(m);
-		
-		m = new HashMap<String, String>();
-		m.put("key", "HG changeset on build");
-		m.put("value", Settings.getRepoRevision());
-		properties.add(m);
-		
-		m = new HashMap<String, String>();
-		m.put("key", "isClusterMode");
-		m.put("value", Settings.getClusterMode().equals("Cluster") ? "true"  : "false" );
-		properties.add(m);
-		
-		m = new HashMap<String, String>();
-		m.put("key", "nodeId");
-		m.put("value", Settings.getNodeInfo().equals("") ? "N1" : Settings.getNodeInfo());
-		properties.add(m);
-		
-		m = new HashMap<String, String>();
-		m.put("key", "serverStartTime");
-		final Date timeOfServerStartup = new Date(WebappHelper.getTimeOfServerStartup());
-		m.put("value", String.valueOf(timeOfServerStartup));
-		properties.add(m);
-		
-		m = new HashMap<String, String>();
-		m.put("key", "Build date");
-		m.put("value", String.valueOf(Settings.getBuildDate()));
-		properties.add(m);
-		
+	@Override
+	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
+		Formatter format = Formatter.getInstance(getLocale());
+
+		//runtime informations
+		FormLayoutContainer runtimeCont = FormLayoutContainer.createDefaultFormLayout("runtime", getTranslator());
+		formLayout.add(runtimeCont);
+		formLayout.add("runtime", runtimeCont);
+		
+		String startup = format.formatDateAndTime(new Date(WebappHelper.getTimeOfServerStartup()));
+		uifactory.addStaticTextElement("runtime.startup", "runtime.startup", startup, runtimeCont);
+		
+		Calendar lastLoginMonthlyLimit = Calendar.getInstance();
+		//users monthly
+		lastLoginMonthlyLimit.add(Calendar.MONTH, -1);
+		Long userLastMonth = securityManager.countUniqueUserLoginsSince(lastLoginMonthlyLimit.getTime());
+		lastLoginMonthlyLimit.add(Calendar.MONTH, -5); // -1 -5 = -6 for half a year
+		Long userLastSixMonths = securityManager.countUniqueUserLoginsSince(lastLoginMonthlyLimit.getTime());
+		uifactory.addStaticTextElement("users1month", "runtime.users.lastmonth", userLastMonth.toString(), runtimeCont);
+		uifactory.addStaticTextElement("users6month", "runtime.users.last6months", userLastSixMonths.toString(), runtimeCont);
+		
+		//users daily
+		Calendar lastLoginDailyLimit = Calendar.getInstance();
+		lastLoginDailyLimit.add(Calendar.DAY_OF_YEAR, -1);
+		Long userLastDay = securityManager.countUniqueUserLoginsSince(lastLoginDailyLimit.getTime());
+		lastLoginDailyLimit.add(Calendar.DAY_OF_YEAR, -6); // -1 - 6 = -7 for last week
+		Long userLast6Days = securityManager.countUniqueUserLoginsSince(lastLoginDailyLimit.getTime());
+		uifactory.addStaticTextElement("userslastday", "runtime.users.lastday", userLastDay.toString(), runtimeCont);
+		uifactory.addStaticTextElement("userslastweek", "runtime.users.lastweek", userLast6Days.toString(), runtimeCont);
+
+		//memory
+		String memoryPage = velocity_root + "/memory.html";
+		FormLayoutContainer memoryCont = FormLayoutContainer.createCustomFormLayout("memory", getTranslator(), memoryPage);
+		runtimeCont.add(memoryCont);
+		memoryCont.contextPut("used", getHeapValue());
+		memoryCont.contextPut("tooltip", getHeapTooltip());
+		memoryCont.setLabel("runtime.memory", null);
+		
+		FormLayoutContainer permGenCont = FormLayoutContainer.createCustomFormLayout("permgen", getTranslator(), memoryPage);
+		runtimeCont.add(permGenCont);
+		permGenCont.contextPut("used", getNonHeapValue());
+		permGenCont.contextPut("tooltip", getNonHeapTooltip());
+		permGenCont.setLabel("runtime.memory.permGen", null);
+		
+		//controllers
+		int controllerCnt = DefaultController.getControllerCount();
+		uifactory.addStaticTextElement("controllercount", "runtime.controllercount", Integer.toString(controllerCnt), runtimeCont);
+		int numOfDispatchingThreads = DispatcherAction.getConcurrentCounter();
+		uifactory.addStaticTextElement("dispatchingthreads", "runtime.dispatchingthreads", Integer.toString(numOfDispatchingThreads), runtimeCont);
+
+		//server informations
+		FormLayoutContainer serverCont = FormLayoutContainer.createDefaultFormLayout("server", getTranslator());
+		formLayout.add(serverCont);
+		formLayout.add("server", serverCont);
+		
+		//version
+		uifactory.addStaticTextElement("version", "sysinfo.version", Settings.getFullVersionInfo(), serverCont);
+		uifactory.addStaticTextElement("version.hg", "sysinfo.version.hg", Settings.getRepoRevision(), serverCont);
+		String buildDate = format.formatDateAndTime(Settings.getBuildDate());
+		uifactory.addStaticTextElement("version.date", "sysinfo.version.date", buildDate, serverCont);
+
+		//cluster
+		boolean clusterMode = "Cluster".equals(Settings.getClusterMode());
+		MultipleSelectionElement clusterEl
+			= uifactory.addCheckboxesHorizontal("cluster", "sysinfo.cluster", serverCont, new String[]{"xx"}, new String[]{""}, null);
+		clusterEl.setEnabled(false);
+		clusterEl.select("xx", clusterMode);
+		
+		String nodeId = StringHelper.containsNonWhitespace(Settings.getNodeInfo()) ? Settings.getNodeInfo() : "N1";
+		uifactory.addStaticTextElement("node", "sysinfo.node", nodeId, serverCont);
+
 		File baseDir = new File(WebappHelper.getContextRoot(), "..");
-		m = new HashMap<String, String>();
+		String baseDirPath = null;
 		try {
-			m.put("key", "baseDir");
-			m.put("value", baseDir.getCanonicalPath());
+			baseDirPath = baseDir.getCanonicalPath();
 		} catch (IOException e1) {
-			// then fall back to unresolved path
-			m.put("key", "baseDir");
-			m.put("value", baseDir.getAbsolutePath());
+			baseDirPath = baseDir.getAbsolutePath();
 		}
-		properties.add(m);
-				
-		m = new HashMap<String, String>();
-		m.put("key", "jsMathEnabled");
-		boolean jsMathEnabled = BaseChiefController.isJsMathEnabled();
-		m.put("value", Boolean.toString(jsMathEnabled));
-		properties.add(m);
-		
-		m = new HashMap<String, String>();
-		m.put("key", "WebDAVEnabled");
-		boolean webDavEnabled = WebDAVManager.getInstance().isEnabled();
-		m.put("value", Boolean.toString(webDavEnabled));
-		properties.add(m);
-		
-		myBuildinfo.contextPut("properties", properties);
+		uifactory.addStaticTextElement("sysinfo.basedir", "sysinfo.basedir", baseDirPath, serverCont);
 	}
-
-
-
-	@Override
-	//fxdiff BAKS-7 Resume function
-	public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) {
-		if(entries != null && entries.isEmpty()) return;
-		tabbedPane.activate(ureq, entries, state);
+	
+	private String getHeapValue() {
+		MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
+    long used = memoryBean.getHeapMemoryUsage().getUsed();
+    long max = memoryBean.getHeapMemoryUsage().getMax();
+		return toPercent(used, max);
 	}
-
-	/**
-	 * @see org.olat.core.gui.control.DefaultController#event(org.olat.core.gui.UserRequest, org.olat.core.gui.components.Component, org.olat.core.gui.control.Event)
-	 */
-	public void event(UserRequest ureq, Component source, Event event) {
-		if (source == tabbedPane) { // those must be links
-			TabbedPaneChangedEvent tbcEvent = (TabbedPaneChangedEvent)event;
-			Component newComponent = tbcEvent.getNewComponent();
-			//fxdiff BAKS-7 Resume function
-			tabbedPane.addToHistory(ureq, getWindowControl());
-			if (newComponent == infoMsgCtrl.getInitialComponent()) {
-				
-			}
-			
-			else if (newComponent == mySysinfo) {
-				Runtime r = Runtime.getRuntime();
-				StringBuilder sb = new StringBuilder();
-				appendFormattedKeyValue(sb, "Processors", new Integer(r.availableProcessors()));
-				appendFormattedKeyValue(sb, "Total Memory", StringHelper.formatMemory(r.totalMemory()));
-				appendFormattedKeyValue(sb, "Free Memory", StringHelper.formatMemory(r.freeMemory()));
-				appendFormattedKeyValue(sb, "Max Memory", StringHelper.formatMemory(r.maxMemory()));
-				
-				sb.append("<br />Detailed Memory Information (Init/Used/Max)<br/> ");
-				Iterator<MemoryPoolMXBean> iter = ManagementFactory.getMemoryPoolMXBeans().iterator();
-				while (iter.hasNext()) {
-				    MemoryPoolMXBean item = iter.next();
-				    String name = item.getName();
-				    MemoryType type = item.getType();
-				    appendFormattedKeyValue(sb, name, " Type: " + type);
-				    MemoryUsage usage = item.getUsage();
-				    appendFormattedKeyValue(sb, "Usage", StringHelper.formatMemory(usage.getInit()) + "/" + StringHelper.formatMemory(usage.getUsed()) + "/" + StringHelper.formatMemory(usage.getMax()));
-				    MemoryUsage peak = item.getPeakUsage();
-				    appendFormattedKeyValue(sb, "Peak", StringHelper.formatMemory(peak.getInit()) + "/" + StringHelper.formatMemory(peak.getUsed()) + "/" + StringHelper.formatMemory(peak.getMax()));
-				    MemoryUsage collections = item.getCollectionUsage();
-				    if (collections!= null){
-				    	appendFormattedKeyValue(sb, "Collections", StringHelper.formatMemory(collections.getInit()) + "/" + StringHelper.formatMemory(collections.getUsed()) + "/" + StringHelper.formatMemory(collections.getMax()));
-				    }
-				    sb.append("<hr/>");
-				}
-				
-				int controllerCnt = DefaultController.getControllerCount();
-				sb.append("<br />Controller Count (active and not disposed):"+controllerCnt);
-				sb.append("<br />Concurrent Dispatching Threads: "+DispatcherAction.getConcurrentCounter());
-				mySysinfo.contextPut("memory", sb.toString());
-				mySysinfo.contextPut("threads",getThreadsInfo());
-				mySysinfo.contextPut("javaenv", getJavaenv());
-				
-			}
-		} 
-		else if (source == gcButton){
-			Runtime.getRuntime().gc();
-			getWindowControl().setInfo("Garbage collection done.");
-			event(ureq, tabbedPane, new TabbedPaneChangedEvent(null, mySysinfo));
-		}
-		
-		
+	
+	private String getHeapTooltip() {
+		MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
+    long used = toMB(memoryBean.getHeapMemoryUsage().getUsed());
+    long max = toMB(memoryBean.getHeapMemoryUsage().getMax());
+		return translate("runtime.memory.tooltip", new String[]{ Long.toString(used), Long.toString(max)});
 	}
-
-	private String getJavaenv() {
-		Properties p = System.getProperties();
-		Iterator it = p.keySet().iterator();
-		StringBuilder props = new StringBuilder();
-		int lineCut = 100;
-		while (it.hasNext()) {
-			String key = (String) it.next();
-			props.append("<b>" + key + "</b>&nbsp;=&nbsp;");
-			String value = p.getProperty(key);
-			if (value.length() <= lineCut)
-				props.append(value);
-			else {
-				props.append(value.substring(0, lineCut - key.length()));
-				while (value.length() > lineCut) {
-					value = "<br />" + value.substring(lineCut);
-					props.append(value.substring(0,	value.length() > lineCut ? lineCut : value.length()));
-				}
-			}
-			props.append("<br />");
-		}
-		return props.toString();
+	
+	private String getNonHeapValue() {
+		MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
+		long used = memoryBean.getNonHeapMemoryUsage().getUsed();
+    long max = memoryBean.getNonHeapMemoryUsage().getMax();
+		return toPercent(used, max);
 	}
-
-
 	
-	private void appendFormattedKeyValue(StringBuilder sb, String key, Object value) {
-		sb.append("&nbsp;&nbsp;&nbsp;<b>");
-		sb.append(key);
-		sb.append(":</b>&nbsp;");
-		sb.append(value);
-		sb.append("<br />");
+	private String getNonHeapTooltip() {
+		MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
+    long used = toMB(memoryBean.getNonHeapMemoryUsage().getUsed());
+    long max = toMB(memoryBean.getNonHeapMemoryUsage().getMax());
+		return translate("runtime.memory.tooltip", new String[]{ Long.toString(used), Long.toString(max)});
 	}
 	
-	private String getThreadsInfo() {
-		StringBuilder sb = new StringBuilder("<pre>threads:<br />");
-		try { // to be sure
-			ThreadGroup tg = Thread.currentThread().getThreadGroup();
-			int actCnt = tg.activeCount();
-			int grpCnt = tg.activeGroupCount();
-			sb.append("about "+actCnt +" threads, "+grpCnt+" groups<br /><br />");
-			Thread[] threads = new Thread[actCnt];
-			tg.enumerate(threads, true);
-			for (int i = 0; i < actCnt; i++) {
-				Thread tr = threads[i];
-				if (tr != null) { // thread may have finished in the meantime 
-					String name = tr.getName();
-					boolean alive = tr.isAlive();
-					boolean interrupted = tr.isInterrupted();
-					sb.append("Thread: (alive = "+alive+", interrupted: "+interrupted+", group:"+tr.getThreadGroup().getName()+") "+name+"<br />");
-				}
-			}
-		}
-		catch (Exception e) {
-			sb.append("exception occured:"+e.getMessage());
-		}
-		return sb.toString()+"</pre>";
+	private final String toPercent(long used, long max) {
+		double ratio = (double)used / (double)max;
+    double percent = ratio * 100.0d;
+    return Math.round(percent) + "%";
 	}
-
-	/**
-	 * 
-	 * @see org.olat.core.gui.control.DefaultController#doDispose(boolean)
-	 */
+	
+	private final long toMB(long val) {
+		return val / (1024 * 1024);
+	}
+	
 	protected void doDispose() {
-		if (clusterController != null) {
-			clusterController.dispose();
-		}
+		//
+	}
+
+	@Override
+	protected void formOK(UserRequest ureq) {
+		//
 	}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/admin/sysinfo/ThreadInfosManager.java b/src/main/java/org/olat/admin/sysinfo/ThreadInfosManager.java
new file mode 100644
index 00000000000..bfa8d954baf
--- /dev/null
+++ b/src/main/java/org/olat/admin/sysinfo/ThreadInfosManager.java
@@ -0,0 +1,139 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.admin.sysinfo;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.RuntimeMXBean;
+import java.lang.management.ThreadInfo;
+import java.lang.management.ThreadMXBean;
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import org.olat.core.logging.OLog;
+import org.olat.core.logging.Tracing;
+import org.olat.core.util.WorkThreadInformations;
+import org.olat.restapi.system.Sampler;
+import org.springframework.stereotype.Service;
+
+/**
+ * 
+ * <h3>Description:</h3>
+ * 
+ * Initial Date:  21 juin 2010 <br>
+ * @author srosse, stephane.rosse@frentix.com, www.frentix.com
+ */
+@Service
+public class ThreadInfosManager implements Sampler {
+	
+	private static final OLog log = Tracing.createLoggerFor(ThreadInfosManager.class);
+	
+	private final static NumberFormat percentFormat = NumberFormat.getPercentInstance(Locale.ENGLISH);
+	
+	private long prevUpTime;
+	private Map<Long,ThreadView> threadMap = new HashMap<Long,ThreadView>();
+
+	public List<ThreadView> getThreadViews() {
+		if(threadMap.isEmpty()) {
+			takeSample();
+		}
+
+		List<ThreadView> threads = new ArrayList<ThreadView>(threadMap.values());
+		Collections.sort(threads);
+		return threads;
+	}
+
+  @Override
+	public synchronized void takeSample() {
+    	updateTimeSeries();
+	}
+
+
+	private void updateTimeSeries() {
+		ThreadMXBean threadProxy = ManagementFactory.getThreadMXBean();
+		RuntimeMXBean runtimeProxy  = ManagementFactory.getRuntimeMXBean();
+		ThreadInfo tis[] = threadProxy.dumpAllThreads(false, false);
+
+		List<String> currentThreadNames = new ArrayList<String>();
+		
+		Set<Long> currentThreadIds = new HashSet<Long>();
+		for (ThreadInfo ti : tis) {
+			if (!threadMap.containsKey(ti.getThreadId())) {
+				ThreadView threadVO = new ThreadView();
+				threadVO.setId(ti.getThreadId());
+				threadVO.setName(ti.getThreadName());
+				threadVO.setState(ti.getThreadState());
+				threadMap.put(ti.getThreadId(), threadVO);
+			}
+			currentThreadIds.add(ti.getThreadId());
+		}
+		WorkThreadInformations.currentThreadNames(currentThreadNames);
+		
+		for (ThreadView threadVO:threadMap.values()) {
+			threadVO.setPrevCpuTime(Math.max(0, threadVO.getCpuTime()));
+			threadVO.setCpuTime(Math.max(0, threadProxy.getThreadCpuTime(threadVO.getId())));
+		}
+		
+		long upTime = runtimeProxy.getUptime();
+		if (prevUpTime > 0L && upTime > prevUpTime) {
+			// elapsedTime is in ms
+			long elapsedTime = upTime - prevUpTime;
+			for (ThreadView threadVO:threadMap.values()) {
+				// elapsedCpu is in ns
+				long elapsedCpu = threadVO.getCpuTime() - threadVO.getPrevCpuTime();
+				// cpuUsage could go higher than 100% because elapsedTime
+				// and elapsedCpu are not fetched simultaneously. Limit to
+				// 99% to avoid Chart showing a scale from 0% to 200%.
+				float cpuUsage = Math.min(99f, elapsedCpu / (elapsedTime * 1000000F));
+				threadVO.setCpuUsage(cpuUsage);
+				threadVO.setCpuUsagePercent(percentFormat.format(cpuUsage));
+				
+				if(cpuUsage > 0.8) {
+					threadVO.setWarningCounter(threadVO.getWarningCounter() + 1);
+					if(threadVO.getWarningCounter() >= 2) {
+						String currentWork = WorkThreadInformations.get(threadVO.getName());
+						if(currentWork == null) {
+							currentWork = "unkown";
+						}
+						log.info("High usage on thread:" + threadVO + " because thread work at: " + currentWork);
+					}
+				} else {
+					threadVO.setWarningCounter(0);
+				}
+			}
+		}
+		prevUpTime = upTime;
+		
+		//clean-up closed threads
+		for (Iterator<Map.Entry<Long,ThreadView>> it=threadMap.entrySet().iterator(); it.hasNext(); ) {
+			Map.Entry<Long,ThreadView> entry = it.next();
+			if(!currentThreadIds.contains(entry.getKey())) {
+				it.remove();
+			}
+		}
+	}
+}
diff --git a/src/main/java/org/olat/admin/sysinfo/ThreadView.java b/src/main/java/org/olat/admin/sysinfo/ThreadView.java
new file mode 100644
index 00000000000..3817a56df0e
--- /dev/null
+++ b/src/main/java/org/olat/admin/sysinfo/ThreadView.java
@@ -0,0 +1,149 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.admin.sysinfo;
+
+
+/**
+ * 
+ * Initial date: 19.11.2012<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class ThreadView implements Comparable<ThreadView> {
+
+	private long id;
+	private String name;
+	private String groupName;
+	private float cpuUsage;
+	private String cpuUsagePercent;
+	private long cpuTime;
+	private long prevCpuTime = 0l;
+	private int warningCounter = 0;
+	private Thread.State state;
+	
+	
+	public ThreadView() {
+		//
+	}
+	
+	public long getId() {
+		return id;
+	}
+	
+	public void setId(long id) {
+		this.id = id;
+	}
+	
+	public String getName() {
+		return name;
+	}
+	
+	public void setName(String name) {
+		this.name = name;
+	}
+	
+	public String getGroupName() {
+		return groupName;
+	}
+
+	public void setGroupName(String groupName) {
+		this.groupName = groupName;
+	}
+
+	public float getCpuUsage() {
+		return cpuUsage;
+	}
+	
+	public void setCpuUsage(float cpuUsage) {
+		this.cpuUsage = cpuUsage;
+	}
+
+	public String getCpuUsagePercent() {
+		return cpuUsagePercent;
+	}
+
+	public void setCpuUsagePercent(String cpuUsagePercent) {
+		this.cpuUsagePercent = cpuUsagePercent;
+	}
+
+	/**
+	 * @return CPU time in nanoseconds
+	 */
+	public long getCpuTime() {
+		return cpuTime;
+	}
+
+	public void setCpuTime(long cpuTime) {
+		this.cpuTime = cpuTime;
+	}
+
+	public long getPrevCpuTime() {
+		return prevCpuTime;
+	}
+
+	public void setPrevCpuTime(long prevCpuTime) {
+		this.prevCpuTime = prevCpuTime;
+	}
+
+	public int getWarningCounter() {
+		return warningCounter;
+	}
+
+	public void setWarningCounter(int warningCounter) {
+		this.warningCounter = warningCounter;
+	}
+
+	public Thread.State getState() {
+		return state;
+	}
+
+	public void setState(Thread.State state) {
+		this.state = state;
+	}
+
+	@Override
+	public int compareTo(ThreadView o) {
+		return name.compareToIgnoreCase(o.name);
+	}
+
+	@Override
+	public int hashCode() {
+		return (int)id;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if(this == obj) {
+			return true;
+		}
+		if(obj instanceof ThreadView) {
+			ThreadView thread = (ThreadView)obj;
+			return id == thread.id;
+		}
+		return false;
+	}
+
+	@Override
+	public String toString() {
+		StringBuilder sb = new StringBuilder();
+		sb.append("ThreadInfo[id=").append(id).append(":name=").append(name).append(":cpu=").append(cpuUsagePercent).append("]");
+		return sb.toString();
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/admin/sysinfo/_content/buildinfo.html b/src/main/java/org/olat/admin/sysinfo/_content/buildinfo.html
deleted file mode 100644
index d8d6620cba1..00000000000
--- a/src/main/java/org/olat/admin/sysinfo/_content/buildinfo.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<h4>$r.translate("buildinfo")</h4>
-<p/>
-<fieldset>
-	<legend>Server</legend>
-	<table class="b_table b_grid">
-		<thead>
-			<tr>
-				<th>key</th>
-				<th>value</th>
-			</tr>
-		</thead>
-		#foreach($property in $properties)
-		<tr>
-			<td>$property.key</td>
-			<td>$property.value</td>
-		</tr>
-		#end
-	</table>
-</fieldset>
diff --git a/src/main/java/org/olat/admin/sysinfo/_content/coreinfo.html b/src/main/java/org/olat/admin/sysinfo/_content/coreinfo.html
new file mode 100644
index 00000000000..6cba385909f
--- /dev/null
+++ b/src/main/java/org/olat/admin/sysinfo/_content/coreinfo.html
@@ -0,0 +1,5 @@
+<fieldset><legend>$r.translate("core.functions")</legend>
+$r.render("functions")
+</fieldset>
+
+
diff --git a/src/main/java/org/olat/admin/sysinfo/_content/hibernateinfo.html b/src/main/java/org/olat/admin/sysinfo/_content/hibernateinfo.html
index 739b74b350f..5ded1263058 100644
--- a/src/main/java/org/olat/admin/sysinfo/_content/hibernateinfo.html
+++ b/src/main/java/org/olat/admin/sysinfo/_content/hibernateinfo.html
@@ -1,11 +1,13 @@
-<h4>$r.translate("title.hibernate.statistics")</h4>
+<div class="o_buttons_box_right">
 #if ($isStatisticsEnabled)
-  $r.render("disable.hibernate.statistics")  ,  $r.render("clear.hibernate.statistics")
+  $r.render("disable.hibernate.statistics") $r.render("clear.hibernate.statistics")
 #else
   $r.render("enable.hibernate.statistics")
 #end
-<br>
-<br>
+</div>
+
+<br/><br/>
+<fieldset><legend>$r.translate("title.hibernate.statistics")</legend>
 Hibernate.ConnectCount : $hibernateStatistics.connectCount<br>
 Hibernate.FlushCount : $hibernateStatistics.flushCount<br>
 Hibernate.SessionOpenCount : $hibernateStatistics.sessionOpenCount<br>
@@ -19,16 +21,19 @@ Hibernate.QueryCacheHitCount : $hibernateStatistics.queryCacheHitCount<br>
 Hibernate.QueryCacheMissCount : $hibernateStatistics.queryCacheMissCount<br>
 Hibernate.TransactionCount : $hibernateStatistics.transactionCount<br>
 Hibernate.SuccessfulTransactionCount : $hibernateStatistics.successfulTransactionCount<br>
-<br>
-<h4>List all queries</h4>
+</fieldset>
+
+<fieldset><legend>List all queries</legend>
 #foreach ($query in $hibernateStatistics.queries)
   <b>$query</b><br>
   $hibernateStatistics.getQueryStatistics($query)<br>
 #end
-<br>
-<h4>List all entities </h4>
+</fieldset>
+
+<fieldset><legend>List all entities</legend>
 #foreach ($entity in $hibernateStatistics.entityNames)
   <b>$entity</b><br>
   $hibernateStatistics.getEntityStatistics($entity)<br>
 #end
+</fieldset>
 
diff --git a/src/main/java/org/olat/admin/sysinfo/_content/infomsg.html b/src/main/java/org/olat/admin/sysinfo/_content/infomsg.html
index b4651b1d184..f9a098df1d5 100644
--- a/src/main/java/org/olat/admin/sysinfo/_content/infomsg.html
+++ b/src/main/java/org/olat/admin/sysinfo/_content/infomsg.html
@@ -1,6 +1,8 @@
-<p>
-	<i>Message Admin-Token: $!admintoken</i>
-</p>
+<fieldset>
+	<legend>Admin.</legend>
+	<p><i>Message Admin-Token: $!admintoken</i></p>
+</fieldset>
+
 
 <fieldset>
 <legend>$r.translate("infomsg.title")</legend>
diff --git a/src/main/java/org/olat/admin/sysinfo/_content/jvm_envprops.html b/src/main/java/org/olat/admin/sysinfo/_content/jvm_envprops.html
new file mode 100644
index 00000000000..9f14c774df4
--- /dev/null
+++ b/src/main/java/org/olat/admin/sysinfo/_content/jvm_envprops.html
@@ -0,0 +1,4 @@
+<fieldset><legend>$r.translate("java.envProps.title")</legend>
+$r.render("javaenv")
+</fieldset>
+
diff --git a/src/main/java/org/olat/admin/sysinfo/_content/jvm_memory.html b/src/main/java/org/olat/admin/sysinfo/_content/jvm_memory.html
new file mode 100644
index 00000000000..062521d2105
--- /dev/null
+++ b/src/main/java/org/olat/admin/sysinfo/_content/jvm_memory.html
@@ -0,0 +1,5 @@
+
+<fieldset><legend>$r.translate("sysinfo.memory")</legend>
+$r.render("run.gc")
+</fieldset>
+$memory
\ No newline at end of file
diff --git a/src/main/java/org/olat/admin/sysinfo/_content/jvm_threads.html b/src/main/java/org/olat/admin/sysinfo/_content/jvm_threads.html
new file mode 100644
index 00000000000..a9525561389
--- /dev/null
+++ b/src/main/java/org/olat/admin/sysinfo/_content/jvm_threads.html
@@ -0,0 +1,5 @@
+<fieldset><legend>$r.translate("java.threads.title")</legend>
+$r.render("threads")
+</fieldset>
+
+
diff --git a/src/main/java/org/olat/admin/sysinfo/_content/memory.html b/src/main/java/org/olat/admin/sysinfo/_content/memory.html
new file mode 100644
index 00000000000..25c04eb8931
--- /dev/null
+++ b/src/main/java/org/olat/admin/sysinfo/_content/memory.html
@@ -0,0 +1,3 @@
+<div class="o_eff_statement_progress" ext:qtip="${tooltip}">
+	<div class="o_eff_statement_solved" style="width:${used}">&#160;</div>
+</div>
\ No newline at end of file
diff --git a/src/main/java/org/olat/admin/sysinfo/_content/sysinfo.html b/src/main/java/org/olat/admin/sysinfo/_content/sysinfo.html
index 67456f116a6..a2488072ea8 100644
--- a/src/main/java/org/olat/admin/sysinfo/_content/sysinfo.html
+++ b/src/main/java/org/olat/admin/sysinfo/_content/sysinfo.html
@@ -1,14 +1,8 @@
-<h4>$r.translate("sysinfo")</h4>
-<p>
-	$r.translate("sysinfo.startuptime") : $startupTime
-</p>
-<h4>$r.translate("sysinfo.memory")</h4>
-$memory
-<br><br>
-$r.render("run.gc")
-<br>
-<h4>Current Threads</h4>
-$threads
-<br>
-<h4>JAVA Environment Properties</h4>
-$javaenv
+<fieldset><legend>$r.translate("runtime")</legend>
+$r.render("runtime")
+</fieldset>
+
+<fieldset><legend>$r.translate("sysinfo")</legend>
+$r.render("server")
+</fieldset>
+
diff --git a/src/main/java/org/olat/admin/sysinfo/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/admin/sysinfo/_i18n/LocalStrings_de.properties
index 91580387c2c..e4d367a029d 100644
--- a/src/main/java/org/olat/admin/sysinfo/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/admin/sysinfo/_i18n/LocalStrings_de.properties
@@ -6,6 +6,10 @@ buildinfo=Build Information
 clear.hibernate.statistics=Hibernate Statistik l\u00F6schen
 disable.hibernate.statistics=Hibernate Statistik ausschalten
 enable.hibernate.statistics=Hibernate Statistik einschalten
+core.functions=Funktionen Ubersicht
+core.webdav=WebDAV
+core.jsMath=JS Math
+core.restapi=REST API
 error.date=Datum
 error.format=(tt.mm.jjjj)
 error.number=Fehlernummer
@@ -41,6 +45,19 @@ loglevels=Log levels
 loglevels.title=Log4J log levels
 resetloglevels=Alle loglevels auf INFO zur\u00FCcksetzen
 run.gc=Run garbage collection
+java.memory=Memory
+java.threads=Threads
+java.threads.title=Current threads
+java.thread.name=Name
+java.thread.group=Gruppe
+java.thread.alive=Alive
+java.thread.interrupted=Interrupted
+java.thread.cpu.percent=CPU %
+java.thread.cpu.time=CPU Zeit
+java.envProps=Environment properties
+java.envProps.title=Java Environment properties
+java.envprops.name=Name
+java.envprops.value=Wert
 sess.access=Letzter Zugriff
 sess.active = Anzahl der Nutzer, die in den letzten {0} Minuten geklickt haben
 sess.attributes.title=Attribute
@@ -90,6 +107,23 @@ snoop=Snoop
 sysinfo=Systeminformation
 sysinfo.memory=Speicherinformation
 sysinfo.startuptime=Server Startzeit
+sysinfo.version=Version
+sysinfo.version.hg=Mercurial Version
+sysinfo.version.date=Build Datum
+sysinfo.cluster=Cluster
+sysinfo.basedir=Root Ordner
+sysinfo.node=Node
+runtime=Runtime infos
+runtime.startup=Startup
+runtime.users.lastmonth=Active users within last month
+runtime.users.last6months=Active users within last six months
+runtime.users.lastday=Active users within last day
+runtime.users.lastweek=Active users within last week
+runtime.controllercount=Controllers (active and not disposed)
+runtime.dispatchingthreads=Concurrent Dispatching Threads
+runtime.memory=Memory
+runtime.memory.permGen=Memory (Perm. gen.)
+runtime.memory.tooltip={0} MB von {1} MB available
 title.hibernate.statistics=Hibernate Datenbank Zugriff Statistik
 usersession.title=Information \u00FCber Benutzer-Sessions
 filesystemtest=File System Test (Verteilte File Tests mit write/read für clustering). Wichtig nicht auf produktiven Systemen starten, kann zu Performance Problemen führen! 
diff --git a/src/main/java/org/olat/admin/sysinfo/_spring/sysinfoContext.xml b/src/main/java/org/olat/admin/sysinfo/_spring/sysinfoContext.xml
index e9f5111fff7..635808a1238 100644
--- a/src/main/java/org/olat/admin/sysinfo/_spring/sysinfoContext.xml
+++ b/src/main/java/org/olat/admin/sysinfo/_spring/sysinfoContext.xml
@@ -1,20 +1,26 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <beans xmlns="http://www.springframework.org/schema/beans"
 	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xmlns:context="http://www.springframework.org/schema/context"
 	xsi:schemaLocation="
   http://www.springframework.org/schema/beans 
-  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
+  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
+  http://www.springframework.org/schema/context 
+  http://www.springframework.org/schema/context/spring-context-3.0.xsd">
+
+	<context:component-scan base-package="org.olat.admin.sysinfo" />
+
+	<bean id="org.olat.admin.sysinfo.InfoMessageManager" 
+		class="org.olat.admin.sysinfo.InfoMessageManager">
+		<constructor-arg index="0"  ref="coordinatorManager"/>
+		<constructor-arg index="1" value="${node.id}" />
+		<property name="actionController">
+		<!-- there are two versions of the infoMessageController (singleVM or Cluster versions). Dynamic creation based on the value in olat.properties-->	
+			<bean class="org.olat.core.gui.control.creator.AutoCreator" scope="prototype">
+				<property name="className" value="org.olat.admin.sysinfo.InfoMessageController${cluster.mode}"/>
+			</bean>
+		</property>
+	</bean>
 
-<bean id="org.olat.admin.sysinfo.InfoMessageManager" 
-	class="org.olat.admin.sysinfo.InfoMessageManager">
-	<constructor-arg index="0"  ref="coordinatorManager"/>
-	<constructor-arg index="1" value="${node.id}" />
-	<property name="actionController">
-	<!-- there are two versions of the infoMessageController (singleVM or Cluster versions). Dynamic creation based on the value in olat.properties-->	
-		<bean class="org.olat.core.gui.control.creator.AutoCreator" scope="prototype">
-			<property name="className" value="org.olat.admin.sysinfo.InfoMessageController${cluster.mode}"/>
-		</bean>
-	</property>
-</bean>
 
 </beans>
\ No newline at end of file
diff --git a/src/main/java/org/olat/core/gui/control/generic/layout/GenericMainController.java b/src/main/java/org/olat/core/gui/control/generic/layout/GenericMainController.java
index 031ddf790b7..d49300255ea 100644
--- a/src/main/java/org/olat/core/gui/control/generic/layout/GenericMainController.java
+++ b/src/main/java/org/olat/core/gui/control/generic/layout/GenericMainController.java
@@ -276,9 +276,22 @@ public abstract class GenericMainController extends MainLayoutBasicController im
 			GenericTreeNode parentNode = (GenericTreeNode) gtm.getNodeById(childNodeEntry.getValue());
 			if (parentNode != null) {
 				parentNode.addChild(childNode);
-				if (parentNode.getDelegate() == null) {
-					parentNode.setDelegate(childNode);
-					parentNode.setUserObject(childNode.getUserObject());
+				if (parentNode.getDelegate() == null  ) {
+					boolean addDelegate = true;
+					
+					//add delegate only if hte parent hasn't not a controller defined
+					Object uo = parentNode.getUserObject();
+					if(uo instanceof GenericActionExtension) {
+						GenericActionExtension gae = (GenericActionExtension)uo;
+						if(StringHelper.containsNonWhitespace(gae.getClassNameOfCorrespondingController())) {
+							addDelegate = false;
+						}
+					}
+					
+					if(addDelegate) {
+						parentNode.setDelegate(childNode);
+						parentNode.setUserObject(childNode.getUserObject());
+					}
 				}
 			} else {
 				logWarn("Could not add navigation-menu (" + childNode.getTitle() + ") to parent:: " + childNodeEntry.getValue(), null);
diff --git a/src/main/java/org/olat/restapi/system/ThreadsWebService.java b/src/main/java/org/olat/restapi/system/ThreadsWebService.java
index 4ad9ac59706..4dc27698162 100644
--- a/src/main/java/org/olat/restapi/system/ThreadsWebService.java
+++ b/src/main/java/org/olat/restapi/system/ThreadsWebService.java
@@ -20,19 +20,10 @@
 package org.olat.restapi.system;
 
 import java.lang.management.ManagementFactory;
-import java.lang.management.RuntimeMXBean;
-import java.lang.management.ThreadInfo;
 import java.lang.management.ThreadMXBean;
-import java.text.NumberFormat;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
 
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
@@ -40,9 +31,9 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 
-import org.olat.core.logging.OLog;
-import org.olat.core.logging.Tracing;
-import org.olat.core.util.WorkThreadInformations;
+import org.olat.admin.sysinfo.ThreadInfosManager;
+import org.olat.admin.sysinfo.ThreadView;
+import org.olat.core.CoreSpringFactory;
 import org.olat.restapi.system.vo.ThreadVO;
 import org.olat.restapi.system.vo.ThreadVOes;
 import org.olat.restapi.system.vo.ThreadsVO;
@@ -55,13 +46,6 @@ import org.olat.restapi.system.vo.ThreadsVO;
  * @author srosse, stephane.rosse@frentix.com, www.frentix.com
  */
 public class ThreadsWebService implements Sampler {
-	
-	private static final OLog log = Tracing.createLoggerFor(ThreadsWebService.class);
-	
-	private final static NumberFormat percentFormat = NumberFormat.getPercentInstance(Locale.ENGLISH);
-	
-	private long prevUpTime;
-	private Map<Long,ThreadVO> threadMap = new HashMap<Long,ThreadVO>();
 
 	@GET
 	@Produces(MediaType.TEXT_PLAIN)
@@ -83,11 +67,12 @@ public class ThreadsWebService implements Sampler {
 	@Path("cpu")
 	@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
 	public synchronized Response getThreadsCpu() {
-		if(threadMap.isEmpty()) {
-			takeSample();
+		List<ThreadView> threadViews = CoreSpringFactory.getImpl(ThreadInfosManager.class).getThreadViews();
+		List<ThreadVO> threads = new ArrayList<ThreadVO>(threadViews.size());
+		for(ThreadView view: threadViews) {
+			threads.add(new ThreadVO(view));
 		}
 
-		List<ThreadVO> threads = new ArrayList<ThreadVO>(threadMap.values());
 		Collections.sort(threads);
 		ThreadVO[] threadVos = threads.toArray(new ThreadVO[threads.size()]);
 		ThreadVOes voes = new ThreadVOes();
@@ -98,69 +83,6 @@ public class ThreadsWebService implements Sampler {
 	
   @Override
 	public synchronized void takeSample() {
-    	updateTimeSeries();
-	}
-
-	private void updateTimeSeries() {
-		ThreadMXBean threadProxy = ManagementFactory.getThreadMXBean();
-		RuntimeMXBean runtimeProxy  = ManagementFactory.getRuntimeMXBean();
-		ThreadInfo tis[] = threadProxy.dumpAllThreads(false, false);
-		
-		List<String> currentThreadNames = new ArrayList<String>();
-		
-		Set<Long> currentThreadIds = new HashSet<Long>();
-		for (ThreadInfo ti : tis) {
-			if (!threadMap.containsKey(ti.getThreadId())) {
-				ThreadVO threadVO = new ThreadVO();
-				threadVO.setId(ti.getThreadId());
-				threadVO.setName(ti.getThreadName());
-				threadMap.put(ti.getThreadId(), threadVO);
-			}
-			currentThreadIds.add(ti.getThreadId());
-		}
-		WorkThreadInformations.currentThreadNames(currentThreadNames);
-		
-		for (ThreadVO threadVO:threadMap.values()) {
-			threadVO.setPrevCpuTime(Math.max(0, threadVO.getCpuTime()));
-			threadVO.setCpuTime(Math.max(0, threadProxy.getThreadCpuTime(threadVO.getId())));
-		}
-		
-		long upTime = runtimeProxy.getUptime();
-		if (prevUpTime > 0L && upTime > prevUpTime) {
-			// elapsedTime is in ms
-			long elapsedTime = upTime - prevUpTime;
-			for (ThreadVO threadVO:threadMap.values()) {
-				// elapsedCpu is in ns
-				long elapsedCpu = threadVO.getCpuTime() - threadVO.getPrevCpuTime();
-				// cpuUsage could go higher than 100% because elapsedTime
-				// and elapsedCpu are not fetched simultaneously. Limit to
-				// 99% to avoid Chart showing a scale from 0% to 200%.
-				float cpuUsage = Math.min(99f, elapsedCpu / (elapsedTime * 1000000F));
-				threadVO.setCpuUsage(cpuUsage);
-				threadVO.setCpuUsagePercent(percentFormat.format(cpuUsage));
-				
-				if(cpuUsage > 0.8) {
-					threadVO.setWarningCounter(threadVO.getWarningCounter() + 1);
-					if(threadVO.getWarningCounter() >= 2) {
-						String currentWork = WorkThreadInformations.get(threadVO.getName());
-						if(currentWork == null) {
-							currentWork = "unkown";
-						}
-						log.info("High usage on thread:" + threadVO + " because thread work at: " + currentWork);
-					}
-				} else {
-					threadVO.setWarningCounter(0);
-				}
-			}
-		}
-		prevUpTime = upTime;
-		
-		//clean-up closed threads
-		for (Iterator<Map.Entry<Long,ThreadVO>> it=threadMap.entrySet().iterator(); it.hasNext(); ) {
-			Map.Entry<Long,ThreadVO> entry = it.next();
-			if(!currentThreadIds.contains(entry.getKey())) {
-				it.remove();
-			}
-		}
+  	CoreSpringFactory.getImpl(ThreadInfosManager.class).takeSample();
 	}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/restapi/system/vo/ThreadVO.java b/src/main/java/org/olat/restapi/system/vo/ThreadVO.java
index 2edaab1dccc..1b567c5c96f 100644
--- a/src/main/java/org/olat/restapi/system/vo/ThreadVO.java
+++ b/src/main/java/org/olat/restapi/system/vo/ThreadVO.java
@@ -23,7 +23,8 @@ import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlAttribute;
 import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlTransient;
+
+import org.olat.admin.sysinfo.ThreadView;
 
 /**
  * 
@@ -48,21 +49,23 @@ public class ThreadVO implements Comparable<ThreadVO> {
 	private String cpuUsagePercent;
 	@XmlAttribute(name="cpuTime")
 	private long cpuTime;
-	@XmlTransient
-	private long prevCpuTime = 0l;
-	@XmlTransient
-	private int warningCounter = 0;
 	
 	public ThreadVO() {
 		//make JAXB happy
 	}
 	
-	public ThreadVO(Long id, String name, Float cpuUsage) {
+	public ThreadVO(Long id, String name, float cpuUsage) {
 		this.id = id;
 		this.name = name;
 		this.cpuUsage = cpuUsage;
 	}
 	
+	public ThreadVO(ThreadView view) {
+		this(view.getId(), view.getName(), view.getCpuUsage());
+		cpuUsagePercent = view.getCpuUsagePercent();
+		cpuTime = view.getCpuTime();
+	}
+	
 	public long getId() {
 		return id;
 	}
@@ -103,22 +106,6 @@ public class ThreadVO implements Comparable<ThreadVO> {
 		this.cpuTime = cpuTime;
 	}
 
-	public long getPrevCpuTime() {
-		return prevCpuTime;
-	}
-
-	public void setPrevCpuTime(long prevCpuTime) {
-		this.prevCpuTime = prevCpuTime;
-	}
-
-	public int getWarningCounter() {
-		return warningCounter;
-	}
-
-	public void setWarningCounter(int warningCounter) {
-		this.warningCounter = warningCounter;
-	}
-
 	@Override
 	public int compareTo(ThreadVO o) {
 		return name.compareToIgnoreCase(o.name);
diff --git a/src/main/resources/serviceconfig/org/olat/_spring/extensionContext.xml b/src/main/resources/serviceconfig/org/olat/_spring/extensionContext.xml
index 7fd2ad3c1db..ca1a32cf8a4 100644
--- a/src/main/resources/serviceconfig/org/olat/_spring/extensionContext.xml
+++ b/src/main/resources/serviceconfig/org/olat/_spring/extensionContext.xml
@@ -17,6 +17,11 @@
 	<!-- The system menu point -->
 	<bean class="org.olat.core.extensions.action.GenericActionExtension" init-method="initExtensionPoints">
 		<property name="order" value="7100" />
+		<property name="actionController">	
+			<bean class="org.olat.core.gui.control.creator.AutoCreator" scope="prototype">
+				<property name="className" value="org.olat.admin.sysinfo.SysinfoController"/>
+			</bean>
+		</property>
 		<property name="navigationKey" value="system" />
 		<property name="nodeIdentifierIfParent" value="systemParent" />
 		<property name="translationPackage" value="org.olat.admin" />
@@ -33,14 +38,14 @@
 			<property name="order" value="7110" />
 			<property name="actionController">	
 				<bean class="org.olat.core.gui.control.creator.AutoCreator" scope="prototype">
-					<property name="className" value="org.olat.admin.sysinfo.SysinfoController"/>
+					<property name="className" value="org.olat.admin.sysinfo.InfoMessageControllerSingleVM"/>
 				</bean>
 			</property>
 			<property name="navigationKey" value="sysinfo" />
 			<property name="parentTreeNodeIdentifier" value="systemParent" /> 
 			<property name="translationPackage" value="org.olat.admin"/>
-			<property name="i18nActionKey" value="menu.sysinfo"/>
-			<property name="i18nDescriptionKey" value="menu.sysinfo.alt"/>
+			<property name="i18nActionKey" value="menu.infomsg"/>
+			<property name="i18nDescriptionKey" value="menu.infomsg.alt"/>
 			<property name="extensionPoints">
 				<list>	
 					<value>org.olat.admin.SystemAdminMainController</value>		
@@ -148,6 +153,26 @@
 			</property>
 	</bean>
 	
+	<!-- System / java -->
+	<bean class="org.olat.core.extensions.action.GenericActionExtension"  init-method="initExtensionPoints">
+			<property name="order" value="7170" />
+			<property name="actionController">	
+				<bean class="org.olat.core.gui.control.creator.AutoCreator" scope="prototype">
+					<property name="className" value="org.olat.admin.sysinfo.JavaVMController"/>
+				</bean>
+			</property>
+			<property name="navigationKey" value="locks" />
+			<property name="parentTreeNodeIdentifier" value="systemParent" /> 
+			<property name="translationPackage" value="org.olat.admin"/>
+			<property name="i18nActionKey" value="menu.javavm"/>
+			<property name="i18nDescriptionKey" value="menu.javavm.alt"/>
+			<property name="extensionPoints">
+				<list>	
+					<value>org.olat.admin.SystemAdminMainController</value>		
+				</list>
+			</property>
+	</bean>
+	
 	
 	<!-- The devel menu point -->
 	<bean class="org.olat.core.extensions.action.GenericActionExtension" init-method="initExtensionPoints">
@@ -204,6 +229,11 @@
 	<!--  the "systemconfig" parent node -->
 	<bean class="org.olat.core.extensions.action.GenericActionExtension"  init-method="initExtensionPoints">
 			<property name="order" value="7200" />
+			<property name="actionController">	
+				<bean class="org.olat.core.gui.control.creator.AutoCreator" scope="prototype">
+					<property name="className" value="org.olat.admin.sysinfo.CoreFunctionsController"/>
+				</bean>
+			</property>
 			<property name="navigationKey" value="sysconfig" />
 			<property name="nodeIdentifierIfParent" value="sysconfigParent" />
 			<property name="translationPackage" value="org.olat.admin" />
@@ -227,7 +257,7 @@
 			<property name="translationPackage" value="org.olat.admin"/>
 			<property name="i18nActionKey" value="menu.layout"/>
 			<property name="i18nDescriptionKey" value="menu.layout.alt"/>
-			<property name="parentTreeNodeIdentifier" value="sysconfigParent" /> 
+			<property name="parentTreeNodeIdentifier" value="sysAdminMenueNodeCustomizing" /> 
 			<property name="extensionPoints">
 				<list>	
 					<value>org.olat.admin.SystemAdminMainController</value>		
@@ -392,12 +422,11 @@
 				</list>
 			</property>
 			<property name="enabled" value="${instantMessaging.enable}"/>
-			<property name="parentTreeNodeIdentifier" value="sysconfigParent" /> 
+			<property name="parentTreeNodeIdentifier" value="modulesParent" /> 
 		</bean>	
 
 
-	<!-- SYSADMIN customizing parent node -->
-		<!-- 
+		<!-- Customizing parent node -->
 		<bean class="org.olat.core.extensions.action.GenericActionExtension" init-method="initExtensionPoints">
 			<property name="order" value="7400" />
 			<property name="navigationKey" value="customizing" />
@@ -411,9 +440,8 @@
 				</list>
 			</property>
 		</bean>	
-		-->
 	
-		<!-- "Sprachanpassungswerkzeug" -->
+		<!-- Customizin / "Sprachanpassungswerkzeug" -->
 		<bean class="org.olat.core.extensions.action.GenericActionExtension" init-method="initExtensionPoints">
 			<property name="order" value="7401" />
 			<property name="actionController">
@@ -431,7 +459,7 @@
 					<value>org.olat.admin.SystemAdminMainController</value>		
 				</list>
 			</property>
-			<property name="parentTreeNodeIdentifier" value="sysconfigParent" />
+			<property name="parentTreeNodeIdentifier" value="sysAdminMenueNodeCustomizing" />
 		</bean>
 		
 		<!-- System registration -->
@@ -451,7 +479,7 @@
 					<value>org.olat.admin.SystemAdminMainController</value>		
 				</list>
 			</property>
-			<property name="parentTreeNodeIdentifier" value="sysconfigParent" />
+			<property name="parentTreeNodeIdentifier" value="sysAdminMenueNodeCustomizing" />
 		</bean>	
 		
 		
diff --git a/src/test/java/org/olat/restapi/SystemTest.java b/src/test/java/org/olat/restapi/SystemTest.java
index 55a05971e3a..721cbdd3fe7 100644
--- a/src/test/java/org/olat/restapi/SystemTest.java
+++ b/src/test/java/org/olat/restapi/SystemTest.java
@@ -106,8 +106,6 @@ public class SystemTest extends OlatJerseyTestCase {
 		Assert.assertTrue(threadVo.getCpuTime() >= 0);
 		Assert.assertTrue(threadVo.getCpuUsage() >= 0.0f);
 		Assert.assertTrue(threadVo.getId() > 0l);
-		Assert.assertTrue(threadVo.getPrevCpuTime() >= 0f);
-		Assert.assertTrue(threadVo.getWarningCounter() >= 0);
 		conn.shutdown();	
 	}
 
-- 
GitLab