001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: https://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: https://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.ui.apps.cacheadmin;
029
030import org.opencms.cache.CmsLruCache;
031import org.opencms.flex.CmsFlexCache;
032import org.opencms.loader.CmsImageLoader;
033import org.opencms.loader.I_CmsImageCache;
034import org.opencms.main.CmsLog;
035import org.opencms.main.OpenCms;
036import org.opencms.monitor.CmsMemoryStatus;
037import org.opencms.ui.A_CmsUI;
038import org.opencms.ui.CmsVaadinUtils;
039import org.opencms.ui.FontOpenCms;
040import org.opencms.ui.apps.A_CmsWorkplaceApp;
041import org.opencms.ui.apps.CmsFileExplorer;
042import org.opencms.ui.apps.Messages;
043import org.opencms.ui.components.CmsInfoButton;
044import org.opencms.util.CmsFileUtil;
045import org.opencms.util.CmsStringUtil;
046
047import java.io.File;
048import java.util.LinkedHashMap;
049import java.util.List;
050import java.util.Map;
051
052import org.apache.commons.io.FileUtils;
053import org.apache.commons.logging.Log;
054
055import com.vaadin.server.Sizeable.Unit;
056import com.vaadin.ui.Component;
057import com.vaadin.ui.HorizontalSplitPanel;
058import com.vaadin.ui.UI;
059import com.vaadin.ui.themes.ValoTheme;
060import com.vaadin.v7.event.FieldEvents.TextChangeEvent;
061import com.vaadin.v7.event.FieldEvents.TextChangeListener;
062import com.vaadin.v7.shared.ui.label.ContentMode;
063import com.vaadin.v7.ui.Label;
064import com.vaadin.v7.ui.ProgressBar;
065import com.vaadin.v7.ui.TextField;
066import com.vaadin.v7.ui.VerticalLayout;
067
068/**
069 * Class for the app which shows the content of caches.<p>
070 */
071public class CmsCacheViewApp extends A_CmsWorkplaceApp {
072
073    /**
074     * Modes to run this app.
075     */
076    public static enum Mode {
077        /**Shows FlexCache.*/
078        FlexCache,
079        /**Shows ImageCache.*/
080        ImageCache;
081    }
082
083    /** The logger for this class. */
084    private static final Log LOG = CmsLog.getLog(CmsCacheViewApp.class);
085
086    /**Mode.*/
087    private Mode m_mode;
088
089    /** The file table filter input. */
090    private TextField m_siteTableFilter;
091
092    /**
093     * public constructor.<p>
094     *
095     * @param mode of app
096     */
097    public CmsCacheViewApp(Mode mode) {
098
099        m_mode = mode;
100    }
101
102    /**
103     * Creates in info button for java cache statistics.<p>
104     *
105     * @return CmsInfoButton
106     */
107    public static CmsInfoButton getJavaStatisticButton() {
108
109        return getJavaStatisticButton(OpenCms.getMemoryMonitor().getMemoryStatus());
110
111    }
112
113    /**
114     * Creates in info button for java cache statistics.<p>
115     * @param memory memory object
116     *
117     * @return CmsInfoButton
118     */
119    public static CmsInfoButton getJavaStatisticButton(CmsMemoryStatus memory) {
120
121        Map<String, String> infoMap = new LinkedHashMap<String, String>();
122
123        infoMap.put(
124            CmsVaadinUtils.getMessageText(Messages.GUI_CACHE_JAVA_HEAP_MAX_0),
125            CmsFileUtil.formatFilesize(
126                memory.getMaxMemory() * 1048576,
127                A_CmsUI.getCmsObject().getRequestContext().getLocale()));
128        infoMap.put(
129            CmsVaadinUtils.getMessageText(Messages.GUI_CACHE_JAVA_HEAP_TOTAL_0),
130            CmsFileUtil.formatFilesize(
131                memory.getTotalMemory() * 1048576,
132                A_CmsUI.getCmsObject().getRequestContext().getLocale()));
133        infoMap.put(
134            CmsVaadinUtils.getMessageText(Messages.GUI_CACHE_JAVA_HEAP_USED_0),
135            CmsFileUtil.formatFilesize(
136                memory.getUsedMemory() * 1048576,
137                A_CmsUI.getCmsObject().getRequestContext().getLocale()));
138        infoMap.put(
139            CmsVaadinUtils.getMessageText(Messages.GUI_CACHE_JAVA_HEAP_FREE_0),
140            CmsFileUtil.formatFilesize(
141                memory.getFreeMemory() * 1048576,
142                A_CmsUI.getCmsObject().getRequestContext().getLocale()));
143
144        CmsInfoButton info = new CmsInfoButton(infoMap);
145        VerticalLayout prog = new VerticalLayout();
146        Label label = new Label();
147        label.setContentMode(ContentMode.HTML);
148        label.setValue(
149            "<p>" + CmsVaadinUtils.getMessageText(Messages.GUI_CACHE_FLEXCACHE_LABEL_MEMORY_BLOCK_0) + "</p>");
150        prog.addComponent(label);
151        prog.addComponent(getProgressBar((((float)memory.getUsage() / 100))));
152        info.addAdditionalElement(prog, 0);
153        info.setWindowCaption(CmsVaadinUtils.getMessageText(Messages.GUI_CACHE_FLEX_0));
154        info.setDescription(CmsVaadinUtils.getMessageText(Messages.GUI_CACHE_FLEX_0));
155        return info;
156    }
157
158    /**
159     * Gets a Progressbar with css style set.<p>
160     *
161     * @param value of the bar
162     * @return vaadin ProgressBar
163     */
164    public static ProgressBar getProgressBar(float value) {
165
166        ProgressBar res = new ProgressBar(value);
167        String style = "";
168        if (value > 0.75) {
169            style = "o-nearlyfull";
170        }
171        if (value > 0.9) {
172            style = "o-full";
173        }
174        res.addStyleName(style);
175        return res;
176    }
177
178    /**
179     * Creates in info button for flex cache statistics.<p>
180     *
181     * @return CmsInfoButton
182     */
183    protected static CmsInfoButton getFlexStatisticButton() {
184
185        Map<String, String> infoMap = new LinkedHashMap<String, String>();
186
187        CmsFlexCache cache = OpenCms.getFlexCache();
188        CmsLruCache entryLruCache = cache.getEntryLruCache();
189
190        infoMap.put(
191            CmsVaadinUtils.getMessageText(Messages.GUI_CACHE_FLEXCACHE_LABEL_STATS_KEYS_0),
192            String.valueOf(cache.keySize()));
193        infoMap.put(
194            CmsVaadinUtils.getMessageText(Messages.GUI_CACHE_FLEXCACHE_LABEL_STATS_VARIATIONS_0),
195            String.valueOf(cache.size()));
196        infoMap.put(
197            CmsVaadinUtils.getMessageText(Messages.GUI_CACHE_FLEXCACHE_LABEL_MEMORY_MAXSIZE_0),
198            CmsFileUtil.formatFilesize(
199                entryLruCache.getMaxCacheCosts(),
200                A_CmsUI.getCmsObject().getRequestContext().getLocale()));
201        infoMap.put(
202            CmsVaadinUtils.getMessageText(Messages.GUI_CACHE_FLEXCACHE_LABEL_MEMORY_CURSIZE_0),
203            CmsFileUtil.formatFilesize(
204                entryLruCache.getObjectCosts(),
205                A_CmsUI.getCmsObject().getRequestContext().getLocale()));
206        CmsInfoButton info = new CmsInfoButton(infoMap);
207        VerticalLayout prog = new VerticalLayout();
208        Label label = new Label();
209        label.setContentMode(ContentMode.HTML);
210        label.setValue(
211            "<p>" + CmsVaadinUtils.getMessageText(Messages.GUI_CACHE_FLEXCACHE_LABEL_MEMORY_BLOCK_0) + "</p>");
212        prog.addComponent(label);
213
214        prog.addComponent(
215            getProgressBar((float)entryLruCache.getObjectCosts() / (float)entryLruCache.getMaxCacheCosts()));
216        info.addAdditionalElement(prog, 0);
217        info.setWindowCaption(CmsVaadinUtils.getMessageText(Messages.GUI_CACHE_FLEX_0));
218        info.setDescription(CmsVaadinUtils.getMessageText(Messages.GUI_CACHE_FLEX_0));
219        return info;
220    }
221
222    /**
223     * Creates in info button for image cache statistics.<p>
224     *
225     * @return CmsInfoButton
226     */
227    protected static CmsInfoButton getImageStatisticButton() {
228
229        String repositoryPath = CmsImageLoader.getImageRepositoryPath();
230        I_CmsImageCache imageCache = CmsImageLoader.getImageCache();
231        CmsInfoButton info;
232        if (imageCache != null) {
233            // Listing a remote cache can be expensive and should not delay opening the cache app.
234            info = CmsInfoButton.createLazy(() -> getImageCacheStatistics(imageCache, null));
235        } else {
236            info = new CmsInfoButton(getImageCacheStatistics(null, repositoryPath));
237        }
238
239        info.setWindowCaption(CmsVaadinUtils.getMessageText(Messages.GUI_CACHE_IMAGE_0));
240        info.setDescription(CmsVaadinUtils.getMessageText(Messages.GUI_CACHE_IMAGE_0));
241        return info;
242    }
243
244    /** Calculates image cache statistics. */
245    private static Map<String, String> getImageCacheStatistics(I_CmsImageCache imageCache, String repositoryPath) {
246
247        final long[] size = new long[] {0L};
248        Map<String, String> infoMap = new LinkedHashMap<String, String>();
249        if (imageCache != null) {
250            try {
251                imageCache.visitEntries((key, length) -> size[0] += length);
252            } catch (Exception e) {
253                LOG.warn("Unable to calculate image cache statistics.", e);
254            }
255        } else if (repositoryPath != null) {
256            File repository = new File(repositoryPath);
257            if (repository.exists()) {
258                size[0] = FileUtils.sizeOfDirectory(repository);
259            }
260        }
261        infoMap.put(
262            CmsVaadinUtils.getMessageText(Messages.GUI_CACHE_IMAGECACHE_LABEL_MEMORY_BLOCK_0),
263            CmsFileUtil.formatFilesize(size[0], A_CmsUI.getCmsObject().getRequestContext().getLocale()));
264        return infoMap;
265    }
266
267    /**
268     * @see org.opencms.ui.apps.A_CmsWorkplaceApp#getBreadCrumbForState(java.lang.String)
269     */
270    @Override
271    protected LinkedHashMap<String, String> getBreadCrumbForState(String state) {
272
273        LinkedHashMap<String, String> crumbs = new LinkedHashMap<String, String>();
274
275        if (CmsStringUtil.isEmptyOrWhitespaceOnly(state)) {
276            if (Mode.FlexCache.equals(m_mode)) {
277                crumbs.put("", CmsVaadinUtils.getMessageText(Messages.GUI_CACHE_VIEW_FLEX_0));
278            } else {
279                crumbs.put("", CmsVaadinUtils.getMessageText(Messages.GUI_CACHE_VIEW_IMAGE_0));
280            }
281            return crumbs;
282        }
283        return new LinkedHashMap<String, String>(); //size==1 & state was not empty -> state doesn't match to known path
284    }
285
286    /**
287     * @see org.opencms.ui.apps.A_CmsWorkplaceApp#getComponentForState(java.lang.String)
288     */
289    @Override
290    protected Component getComponentForState(String state) {
291
292        //remove filter field
293        if (m_siteTableFilter != null) {
294            m_infoLayout.removeComponent(m_siteTableFilter);
295            m_siteTableFilter = null;
296        }
297
298        if (Mode.FlexCache.equals(m_mode)) {
299            m_rootLayout.setMainHeightFull(true);
300            return getFlexViewComponent();
301        }
302
303        if (Mode.ImageCache.equals(m_mode)) {
304            m_rootLayout.setMainHeightFull(true);
305            return getImageViewComponent();
306        }
307        return null;
308    }
309
310    /**
311     * @see org.opencms.ui.apps.A_CmsWorkplaceApp#getSubNavEntries(java.lang.String)
312     */
313    @Override
314    protected List<NavEntry> getSubNavEntries(String state) {
315
316        return null;
317
318    }
319
320    /**
321     * Layout for the Flex Cache View includings statistics and cache table.<p>
322     *
323     * @return vaadin component
324     */
325    private Component getFlexViewComponent() {
326
327        final CmsFlexCacheTable table = new CmsFlexCacheTable();
328        m_siteTableFilter = new TextField();
329        m_siteTableFilter.setIcon(FontOpenCms.FILTER);
330        m_siteTableFilter.setInputPrompt(
331            Messages.get().getBundle(UI.getCurrent().getLocale()).key(Messages.GUI_EXPLORER_FILTER_0));
332        m_siteTableFilter.addStyleName(ValoTheme.TEXTFIELD_INLINE_ICON);
333        m_siteTableFilter.setWidth("200px");
334        m_siteTableFilter.addTextChangeListener(new TextChangeListener() {
335
336            private static final long serialVersionUID = 1L;
337
338            public void textChange(TextChangeEvent event) {
339
340                table.filterTable(event.getText());
341            }
342        });
343        m_infoLayout.addComponent(m_siteTableFilter);
344
345        m_uiContext.addToolbarButton(getFlexStatisticButton());
346        m_uiContext.addToolbarButton(CmsFlushCache.getFlushToolButton());
347
348        table.setSizeFull();
349        return table;
350    }
351
352    /**
353     * Creates the view for the image cache.<p>
354     *
355     * @return a vaadin vertical layout with the information about the image cache
356     */
357    private Component getImageViewComponent() {
358
359        m_siteTableFilter = new TextField();
360
361        HorizontalSplitPanel sp = new HorizontalSplitPanel();
362        sp.setSizeFull();
363        VerticalLayout intro = CmsVaadinUtils.getInfoLayout(Messages.GUI_CACHE_IMAGE_INTRODUCTION_0);
364        VerticalLayout nullResult = CmsVaadinUtils.getInfoLayout(Messages.GUI_CACHE_IMAGE_NO_RESULTS_0);
365        final CmsImageCacheTable table = new CmsImageCacheTable(intro, nullResult, m_siteTableFilter);
366
367        sp.setFirstComponent(new CmsImageCacheInput(table));
368
369        VerticalLayout secC = new VerticalLayout();
370        secC.setSizeFull();
371        secC.addComponent(intro);
372        secC.addComponent(nullResult);
373        secC.addComponent(table);
374
375        m_siteTableFilter.setIcon(FontOpenCms.FILTER);
376        m_siteTableFilter.setInputPrompt(
377            Messages.get().getBundle(UI.getCurrent().getLocale()).key(Messages.GUI_EXPLORER_FILTER_0));
378        m_siteTableFilter.addStyleName(ValoTheme.TEXTFIELD_INLINE_ICON);
379        m_siteTableFilter.setWidth("200px");
380        m_siteTableFilter.addTextChangeListener(new TextChangeListener() {
381
382            private static final long serialVersionUID = 1L;
383
384            public void textChange(TextChangeEvent event) {
385
386                table.filterTable(event.getText());
387            }
388        });
389        m_infoLayout.addComponent(m_siteTableFilter);
390
391        m_uiContext.addToolbarButton(getImageStatisticButton());
392        m_uiContext.addToolbarButton(CmsFlushCache.getFlushToolButton());
393        table.setSizeFull();
394        sp.setSecondComponent(secC);
395        sp.setSplitPosition(CmsFileExplorer.LAYOUT_SPLIT_POSITION, Unit.PIXELS);
396
397        table.setVisible(false);
398        nullResult.setVisible(false);
399        m_siteTableFilter.setVisible(false);
400
401        return sp;
402    }
403}