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.HashSet;
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    public SysInfoConlet(Channel componentChannel) {
071        super(componentChannel);
072        setPeriodicRefresh(Duration.ofSeconds(1), () -> new Update());
073    }
074
075    /**
076     * On {@link ConsoleReady}, fire the {@link AddConletType}.
077     *
078     * @param event the event
079     * @param connection the console connection
080     * @throws TemplateNotFoundException the template not found exception
081     * @throws MalformedTemplateNameException the malformed template name
082     *             exception
083     * @throws ParseException the parse exception
084     * @throws IOException Signals that an I/O exception has occurred.
085     */
086    @Handler
087    public void onConsoleReady(ConsoleReady event, ConsoleConnection connection)
088            throws TemplateNotFoundException, MalformedTemplateNameException,
089            ParseException, IOException {
090        // Add SysInfoConlet resources to page
091        connection.respond(new AddConletType(type())
092            .addRenderMode(RenderMode.Preview).setDisplayNames(
093                localizations(connection.supportedLocales(), "conletName"))
094            .addScript(new ScriptResource()
095                .setRequires("jquery")
096                .setScriptType("module")
097                .setScriptUri(event.renderSupport().conletResource(
098                    type(), "SysInfo-functions.ftl.js")))
099            .addCss(event.renderSupport(), WebConsoleUtils.uriFromPath(
100                "SysInfo-style.css")));
101    }
102
103    @Override
104    protected Optional<SysInfoModel> createStateRepresentation(Event<?> event,
105            ConsoleConnection session, String conletId) throws Exception {
106        return Optional.of(new SysInfoModel());
107    }
108
109    @Override
110    protected Set<RenderMode> doRenderConlet(RenderConletRequestBase<?> event,
111            ConsoleConnection connection, String conletId,
112            SysInfoModel conletState) throws Exception {
113        Set<RenderMode> renderedAs = new HashSet<>();
114        if (event.renderAs().contains(RenderMode.Preview)) {
115            Template tpl
116                = freemarkerConfig().getTemplate("SysInfo-preview.ftl.html");
117            connection.respond(new RenderConlet(type(), conletId,
118                processTemplate(event, tpl,
119                    fmModel(event, connection, conletId, conletState)))
120                        .setRenderAs(
121                            RenderMode.Preview.addModifiers(event.renderAs()))
122                        .setSupportedModes(MODES));
123            renderedAs.add(RenderMode.Preview);
124        }
125        if (event.renderAs().contains(RenderMode.View)) {
126            Template tpl
127                = freemarkerConfig().getTemplate("SysInfo-view.ftl.html");
128            connection.respond(new RenderConlet(type(), conletId,
129                processTemplate(event, tpl,
130                    fmModel(event, connection, conletId, conletState)))
131                        .setRenderAs(
132                            RenderMode.View.addModifiers(event.renderAs()))
133                        .setSupportedModes(MODES));
134            renderedAs.add(RenderMode.View);
135        }
136        if (!renderedAs.isEmpty()) {
137            updateView(connection, conletId);
138        }
139        return renderedAs;
140    }
141
142    private void updateView(ConsoleConnection connection, String conletId) {
143        if (!connection.isConnected()) {
144            return;
145        }
146        Runtime runtime = Runtime.getRuntime();
147        connection.respond(new NotifyConletView(type(),
148            conletId, "updateMemorySizes",
149            System.currentTimeMillis(), runtime.maxMemory(),
150            runtime.totalMemory(),
151            runtime.totalMemory() - runtime.freeMemory()));
152    }
153
154    /**
155     * Handle the periodic update event by sending {@link NotifyConletView}
156     * events.
157     *
158     * @param event the event
159     * @param connection the console connection
160     */
161    @Handler
162    public void onUpdate(Update event, ConsoleConnection connection) {
163        for (String conletId : conletIds(connection)) {
164            updateView(connection, conletId);
165        }
166    }
167
168    @Override
169    @SuppressWarnings("PMD.DoNotCallGarbageCollectionExplicitly")
170    protected void doUpdateConletState(NotifyConletModel event,
171            ConsoleConnection connection, SysInfoModel conletState)
172            throws Exception {
173        event.stop();
174        System.gc();
175        for (String conletId : conletIds(connection)) {
176            updateView(connection, conletId);
177        }
178    }
179
180    /**
181     * The conlet's model.
182     */
183    @SuppressWarnings("serial")
184    public static class SysInfoModel implements Serializable {
185
186        /**
187         * Return the system properties.
188         *
189         * @return the properties
190         */
191        public Properties systemProperties() {
192            return System.getProperties();
193        }
194
195        /**
196         * Return the {@link Runtime}.
197         *
198         * @return the runtime
199         */
200        public Runtime runtime() {
201            return Runtime.getRuntime();
202        }
203    }
204
205}