org.artifactory.webapp.wicket.page.logs.SystemLogsViewPanel.java Source code

Java tutorial

Introduction

Here is the source code for org.artifactory.webapp.wicket.page.logs.SystemLogsViewPanel.java

Source

/*
 * Artifactory is a binaries repository manager.
 * Copyright (C) 2012 JFrog Ltd.
 *
 * Artifactory is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Artifactory is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Artifactory.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.artifactory.webapp.wicket.page.logs;

import org.apache.commons.io.FileUtils;
import org.apache.wicket.ajax.AbstractAjaxTimerBehavior;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.link.DownloadLink;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.request.IRequestCycle;
import org.apache.wicket.request.Response;
import org.apache.wicket.request.handler.resource.ResourceStreamRequestHandler;
import org.apache.wicket.request.http.WebResponse;
import org.apache.wicket.request.resource.ContentDisposition;
import org.apache.wicket.request.resource.IResource;
import org.apache.wicket.request.resource.ResourceStreamResource;
import org.apache.wicket.util.resource.FileResourceStream;
import org.apache.wicket.util.resource.IResourceStream;
import org.apache.wicket.util.string.Strings;
import org.apache.wicket.util.time.Duration;
import org.artifactory.api.context.ContextHelper;
import org.artifactory.common.ConstantValues;
import org.artifactory.common.wicket.component.template.HtmlTemplate;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Date;
import java.util.List;

import static java.util.Arrays.asList;
import static org.artifactory.common.wicket.util.JavaScriptUtils.jsFunctionCall;

/**
 * This panel serves as as auto display of the information and content in the system log file, with an option of
 * downloading the file
 *
 * @author Noam Tenne
 */
public class SystemLogsViewPanel extends Panel {
    private static final int FIRST_READ_BLOCK_SIZE = 100 * 1024;

    private File logDir = ContextHelper.get().getArtifactoryHome().getLogDir();

    /**
     * File object of the system log
     */
    private File systemLogFile = new File(logDir, "artifactory.log");

    /**
     * Link object for downloading the system log file
     */
    private DownloadLink downloadLink;

    /**
     * Label to represent the download link caption
     */
    Label linkLabel = new Label("linkLabel", "Download");

    /**
     * Label to display the size of the system log file
     */
    private Label sizeLabel = new Label("systemLogsSize", "");

    /**
     * Label to display the content of the system log file (100K at a time)
     */
    private Label contentLabel = new Label("systemLogsContent", "");

    /**
     * Label to display file last modified and view last updated times
     */
    private Label lastUpdateLabel = new Label("lastUpdate", "");

    /**
     * Pointer to indicate the last position the log file was read from
     */
    private long lastPointer;

    /**
     * List containing log file names
     */
    private static final List<String> LOGS = asList("artifactory.log", "access.log", "import.export.log",
            "request.log");

    /**
     * Main constructor
     *
     * @param id The verbal ID of the panel
     */
    public SystemLogsViewPanel(String id) {
        super(id);
        addLogComboBox();
        addSystemLogsSize();
        addSystemLogsLink();
        addSystemLogsContent();
        addLastUpdate();

        // add the timer behavior to the page and make it update both components
        add(new AbstractAjaxTimerBehavior(Duration.seconds(ConstantValues.logsViewRefreshRateSecs.getInt())) {
            @Override
            protected void onTimer(AjaxRequestTarget target) {
                updateComponents(target, (!systemLogFile.exists()));
            }
        });
    }

    /**
     * Adds a combo box with log file choices
     */
    private void addLogComboBox() {
        final DropDownChoice logsDropDownChoice = new DropDownChoice<String>("logs", Model.of(LOGS.get(0)), LOGS) {

            @Override
            protected boolean wantOnSelectionChangedNotifications() {
                return true;
            }
        };
        /**
         * Add behavior for when the combo box selection is changed
         */
        logsDropDownChoice.add(new AjaxFormComponentUpdatingBehavior("onchange") {

            @Override
            protected void onUpdate(AjaxRequestTarget target) {
                int choice = Integer.parseInt(logsDropDownChoice.getValue());
                List choices = logsDropDownChoice.getChoices();
                String selectedLog = choices.get(choice).toString();
                systemLogFile = new File(logDir, selectedLog);
                updateComponents(target, true);
            }
        });

        add(logsDropDownChoice);
    }

    /**
     * Update the different display components
     *
     * @param target     The AjaxRequestTarget from our action
     * @param cleanPanel True if the text container should be cleaned of content. false if not
     */
    private void updateComponents(AjaxRequestTarget target, boolean cleanPanel) {
        //Make sure the bottom of the text area will be displayed after every update
        target.appendJavaScript(jsFunctionCall("ArtifactoryLog.log", contentLabel.getMarkupId(),
                readLogAndUpdateSize(cleanPanel), cleanPanel));
        target.add(downloadLink);
        target.add(sizeLabel);
        target.add(linkLabel);
        target.add(lastUpdateLabel);
    }

    /**
     * Add a label with the size of the system log file
     */
    private void addSystemLogsSize() {
        add(sizeLabel);
        sizeLabel.setOutputMarkupId(true);
    }

    /**
     * Add a link to enable the download of the system log file
     */
    private void addSystemLogsLink() {
        // This is very ugly and should be removed when we upgrade wicket, see RTFACT-5470 for explanation
        downloadLink = new DownloadLink("systemLogsLink", systemLogFile) {
            @Override
            public void onClick() {
                final File file = getModelObject();
                IResourceStream resourceStream = new FileResourceStream(new org.apache.wicket.util.file.File(file));
                ResourceStreamRequestHandler handler = new ResourceStreamRequestHandler(resourceStream) {
                    @Override
                    public void respond(IRequestCycle requestCycle) {
                        IResource.Attributes attributes = new IResource.Attributes(requestCycle.getRequest(),
                                requestCycle.getResponse());

                        ResourceStreamResource resource = new ResourceStreamResource(this.getResourceStream()) {
                            @Override
                            protected void configureCache(ResourceResponse data, Attributes attributes) {
                                Response response = attributes.getResponse();
                                ((WebResponse) response).disableCaching();
                            }
                        };
                        resource.setFileName(file.getName());
                        resource.setContentDisposition(ContentDisposition.ATTACHMENT);
                        resource.respond(attributes);
                    }
                };
                getRequestCycle().scheduleRequestHandlerAfterCurrent(handler);
            }
        };

        add(downloadLink);
        downloadLink.add(linkLabel);
        downloadLink.setOutputMarkupId(true);
        linkLabel.setOutputMarkupId(true);
    }

    /**
     * Add a label with the log file and view update times
     */
    private void addLastUpdate() {
        add(lastUpdateLabel);
        lastUpdateLabel.setOutputMarkupId(true);
    }

    /**
     * A a label to display the content of the system log file (last 100K)
     */
    private void addSystemLogsContent() {
        add(contentLabel);
        contentLabel.setOutputMarkupId(true);
        contentLabel.setEscapeModelStrings(false);
        contentLabel.setDefaultModelObject(readLogAndUpdateSize(false));

        HtmlTemplate initScript = new HtmlTemplate("initScript");
        initScript.setParameter("logDivId", new PropertyModel(contentLabel, "markupId"));
        add(initScript);
    }

    /**
     * Attemps to continue reading the log file from the last position, and the updates the log path, size and link
     * According to the outcome.
     *
     * @param cleanPanel True if the text container should be cleaned of content. false if not
     * @return String - The newly read content
     */
    protected String readLogAndUpdateSize(boolean cleanPanel) {
        if ((lastPointer > systemLogFile.length()) || cleanPanel) {
            lastPointer = 0;
        }
        long size = systemLogFile.length();
        setLogInfo();
        if (lastPointer == size) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        RandomAccessFile logRandomAccessFile = null;
        try {
            logRandomAccessFile = new RandomAccessFile(systemLogFile, "r");

            //If the log file is larger than 100K
            if (lastPointer == 0 && logRandomAccessFile.length() > FIRST_READ_BLOCK_SIZE) {
                //Point to the begining of the last 100K
                lastPointer = logRandomAccessFile.length() - FIRST_READ_BLOCK_SIZE;
            }
            logRandomAccessFile.seek(lastPointer);

            String line;
            while ((line = logRandomAccessFile.readLine()) != null) {
                CharSequence escapedLine = Strings.escapeMarkup(line, false, false);
                sb.append("<div>").append(escapedLine).append("<br/></div>");
            }
            lastPointer = logRandomAccessFile.getFilePointer();

        } catch (IOException e) {
            throw new RuntimeException(e.getMessage());
        } finally {
            try {
                if (logRandomAccessFile != null) {
                    logRandomAccessFile.close();
                }
            } catch (IOException ignore) {
            }
        }
        return sb.toString();
    }

    /**
     * Sets the attributes of the log size, download links and update times according to the log availability
     */
    private void setLogInfo() {
        if (!systemLogFile.exists()) {
            sizeLabel.setDefaultModelObject("");
            linkLabel.setDefaultModelObject("");
            lastUpdateLabel.setDefaultModelObject("");
        } else {
            sizeLabel.setDefaultModelObject("(" + FileUtils.byteCountToDisplaySize(systemLogFile.length()) + ")");
            linkLabel.setDefaultModelObject("Download");
            StringBuilder sb = new StringBuilder();
            Date logLastModified = new Date(systemLogFile.lastModified());
            Date viewLastUpdate = new Date(System.currentTimeMillis());
            sb.append("File last modified: ").append(logLastModified).append(". ");
            sb.append("View last updated: ").append(viewLastUpdate).append(".");
            lastUpdateLabel.setDefaultModelObject(sb.toString());
            downloadLink.setDefaultModel(new Model<>(systemLogFile));
        }
    }
}