001/*
002 * JGrapes Event Driven Framework
003 * Copyright (C) 2017-2018 Michael N. Lipp
004 * 
005 * This program is free software; you can redistribute it and/or modify it 
006 * under the terms of the GNU Affero General Public License as published by 
007 * the Free Software Foundation; either version 3 of the License, or 
008 * (at your option) any later version.
009 * 
010 * This program is distributed in the hope that it will be useful, but 
011 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
012 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License 
013 * for more details.
014 * 
015 * You should have received a copy of the GNU Affero General Public License along 
016 * with this program; if not, see <http://www.gnu.org/licenses/>.
017 */
018
019package org.jgrapes.webconlet.sysinfo;
020
021import freemarker.core.ParseException;
022import freemarker.template.MalformedTemplateNameException;
023import freemarker.template.Template;
024import freemarker.template.TemplateNotFoundException;
025import java.io.IOException;
026import java.io.Serializable;
027import java.time.Duration;
028import java.util.EnumSet;
029import java.util.Optional;
030import java.util.Properties;
031import java.util.Set;
032import org.jgrapes.core.Channel;
033import org.jgrapes.core.Event;
034import org.jgrapes.core.Manager;
035import org.jgrapes.core.annotation.Handler;
036import org.jgrapes.webconsole.base.Conlet.RenderMode;
037import org.jgrapes.webconsole.base.ConsoleConnection;
038import org.jgrapes.webconsole.base.WebConsoleUtils;
039import org.jgrapes.webconsole.base.events.AddConletType;
040import org.jgrapes.webconsole.base.events.AddPageResources.ScriptResource;
041import org.jgrapes.webconsole.base.events.ConsoleReady;
042import org.jgrapes.webconsole.base.events.NotifyConletModel;
043import org.jgrapes.webconsole.base.events.NotifyConletView;
044import org.jgrapes.webconsole.base.events.RenderConlet;
045import org.jgrapes.webconsole.base.events.RenderConletRequestBase;
046import org.jgrapes.webconsole.base.freemarker.FreeMarkerConlet;
047
048/**
049 * 
050 */
051public class SysInfoConlet
052        extends FreeMarkerConlet<SysInfoConlet.SysInfoModel> {
053
054    private static final Set<RenderMode> MODES = RenderMode.asSet(
055        RenderMode.Preview, RenderMode.View);
056
057    /**
058     * The periodically generated update event.
059     */
060    public static class Update extends Event<Void> {
061    }
062
063    /**
064     * Creates a new component with its channel set to the given channel.
065     * 
066     * @param componentChannel the channel that the component's handlers listen
067     *            on by default and that {@link Manager#fire(Event, Channel...)}
068     *            sends the event to
069     */
070    @SuppressWarnings("PMD.ConstructorCallsOverridableMethod")
071    public SysInfoConlet(Channel componentChannel) {
072        super(componentChannel);
073        setPeriodicRefresh(Duration.ofSeconds(1), () -> new Update());
074    }
075
076    /**
077     * On {@link ConsoleReady}, fire the {@link AddConletType}.
078     *
079     * @param event the event
080     * @param connection the console connection
081     * @throws TemplateNotFoundException the template not found exception
082     * @throws MalformedTemplateNameException the malformed template name
083     *             exception
084     * @throws ParseException the parse exception
085     * @throws IOException Signals that an I/O exception has occurred.
086     */
087    @Handler
088    public void onConsoleReady(ConsoleReady event, ConsoleConnection connection)
089            throws TemplateNotFoundException, MalformedTemplateNameException,
090            ParseException, IOException {
091        // Add SysInfoConlet resources to page
092        connection.respond(new AddConletType(type())
093            .addRenderMode(RenderMode.Preview).setDisplayNames(
094                localizations(connection.supportedLocales(), "conletName"))
095            .addScript(new ScriptResource()
096                .setRequires("jquery")
097                .setScriptType("module")
098                .setScriptUri(event.renderSupport().conletResource(
099                    type(), "SysInfo-functions.ftl.js")))
100            .addCss(event.renderSupport(), WebConsoleUtils.uriFromPath(
101                "SysInfo-style.css")));
102    }
103
104    @Override
105    protected Optional<SysInfoModel> createStateRepresentation(Event<?> event,
106            ConsoleConnection session, String conletId) throws Exception {
107        return Optional.of(new SysInfoModel());
108    }
109
110    @Override
111    protected Set<RenderMode> doRenderConlet(RenderConletRequestBase<?> event,
112            ConsoleConnection connection, String conletId,
113            SysInfoModel conletState) throws Exception {
114        Set<RenderMode> renderedAs = EnumSet.noneOf(RenderMode.class);
115        if (event.renderAs().contains(RenderMode.Preview)) {
116            Template tpl
117                = freemarkerConfig().getTemplate("SysInfo-preview.ftl.html");
118            connection.respond(new RenderConlet(type(), conletId,
119                processTemplate(event, tpl,
120                    fmModel(event, connection, conletId, conletState)))
121                        .setRenderAs(
122                            RenderMode.Preview.addModifiers(event.renderAs()))
123                        .setSupportedModes(MODES));
124            renderedAs.add(RenderMode.Preview);
125        }
126        if (event.renderAs().contains(RenderMode.View)) {
127            Template tpl
128                = freemarkerConfig().getTemplate("SysInfo-view.ftl.html");
129            connection.respond(new RenderConlet(type(), conletId,
130                processTemplate(event, tpl,
131                    fmModel(event, connection, conletId, conletState)))
132                        .setRenderAs(
133                            RenderMode.View.addModifiers(event.renderAs()))
134                        .setSupportedModes(MODES));
135            renderedAs.add(RenderMode.View);
136        }
137        if (!renderedAs.isEmpty()) {
138            updateView(connection, conletId);
139        }
140        return renderedAs;
141    }
142
143    private void updateView(ConsoleConnection connection, String conletId) {
144        if (!connection.isConnected()) {
145            return;
146        }
147        Runtime runtime = Runtime.getRuntime();
148        connection.respond(new NotifyConletView(type(),
149            conletId, "updateMemorySizes",
150            System.currentTimeMillis(), runtime.maxMemory(),
151            runtime.totalMemory(),
152            runtime.totalMemory() - runtime.freeMemory()));
153    }
154
155    /**
156     * Handle the periodic update event by sending {@link NotifyConletView}
157     * events.
158     *
159     * @param event the event
160     * @param connection the console connection
161     */
162    @Handler
163    public void onUpdate(Update event, ConsoleConnection connection) {
164        for (String conletId : conletIds(connection)) {
165            updateView(connection, conletId);
166        }
167    }
168
169    @Override
170    @SuppressWarnings("PMD.DoNotCallGarbageCollectionExplicitly")
171    protected void doUpdateConletState(NotifyConletModel event,
172            ConsoleConnection connection, SysInfoModel conletState)
173            throws Exception {
174        event.stop();
175        System.gc();
176        for (String conletId : conletIds(connection)) {
177            updateView(connection, conletId);
178        }
179    }
180
181    /**
182     * The conlet's model.
183     */
184    @SuppressWarnings("serial")
185    public static class SysInfoModel implements Serializable {
186
187        /**
188         * Return the system properties.
189         *
190         * @return the properties
191         */
192        public Properties systemProperties() {
193            return System.getProperties();
194        }
195
196        /**
197         * Return the {@link Runtime}.
198         *
199         * @return the runtime
200         */
201        public Runtime runtime() {
202            return Runtime.getRuntime();
203        }
204    }
205
206}