org.geoserver.backuprestore.web.BackupRestorePage.java Source code

Java tutorial

Introduction

Here is the source code for org.geoserver.backuprestore.web.BackupRestorePage.java

Source

/* (c) 2016 Open Source Geospatial Foundation - all rights reserved
 * This code is licensed under the GPL 2.0 license, available at the root
 * application directory.
 */
package org.geoserver.backuprestore.web;

import static org.geoserver.backuprestore.web.BackupRestoreWebUtils.backupFacade;
import static org.geoserver.backuprestore.web.BackupRestoreWebUtils.humanReadableByteCount;

import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;

import org.apache.commons.io.FileUtils;
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.NumberTextField;
import org.apache.wicket.markup.html.form.SubmitLink;
import org.apache.wicket.markup.html.form.TextArea;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.LoadableDetachableModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.request.handler.resource.ResourceStreamRequestHandler;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.request.resource.ContentDisposition;
import org.apache.wicket.request.resource.PackageResourceReference;
import org.apache.wicket.util.resource.FileResourceStream;
import org.apache.wicket.util.resource.IResourceStream;
import org.apache.wicket.validation.validator.RangeValidator;
import org.geoserver.backuprestore.AbstractExecutionAdapter;
import org.geoserver.backuprestore.BackupExecutionAdapter;
import org.geoserver.backuprestore.RestoreExecutionAdapter;
import org.geoserver.config.GeoServerDataDirectory;
import org.geoserver.platform.resource.Paths;
import org.geoserver.web.GeoServerSecuredPage;
import org.geoserver.web.GeoServerUnlockablePage;
import org.geoserver.web.wicket.GeoServerDialog;
import org.geoserver.web.wicket.Icon;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.launch.JobExecutionNotRunningException;
import org.springframework.batch.core.launch.NoSuchJobException;
import org.springframework.batch.core.launch.NoSuchJobExecutionException;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.batch.core.repository.JobRestartException;

/**
 * @author Alessio Fabiani, GeoSolutions S.A.S.
 *
 */
public class BackupRestorePage<T extends AbstractExecutionAdapter> extends GeoServerSecuredPage
        implements GeoServerUnlockablePage {

    public static final PackageResourceReference COMPRESS_ICON = new PackageResourceReference(
            BackupRestorePage.class, "compress.png");

    static final String DETAILS_LEVEL = "expand";

    int expand = 0;

    File backupFile;

    GeoServerDialog dialog;

    private PageParameters params;

    private Class<T> clazz;

    public BackupRestorePage(PageParameters pp) {
        this(new BackupRestoreExecutionModel(pp.get("id").toLong(), getType(pp.get("clazz").toString())), pp,
                getType(pp.get("clazz").toString()));
    }

    public BackupRestorePage(T bkp, PageParameters pp) {
        this(new BackupRestoreExecutionModel(bkp, getType(pp.get("clazz").toString())), pp,
                getType(pp.get("clazz").toString()));
    }

    /**
     * @param string
     * @return
     */
    private static Class getType(String simpleName) {
        if (BackupExecutionAdapter.class.getSimpleName().equals(simpleName)) {
            return BackupExecutionAdapter.class;
        } else if (RestoreExecutionAdapter.class.getSimpleName().equals(simpleName)) {
            return RestoreExecutionAdapter.class;
        }
        return null;
    }

    public BackupRestorePage(IModel<T> model, PageParameters pp, Class<T> clazz) {
        this.params = pp;
        this.clazz = clazz;

        initComponents(model);
    }

    public Class<T> getType() {
        return this.clazz;
    }

    void initComponents(final IModel<T> model) {
        add(new Label("id", new PropertyModel(model, "id")));
        add(new Label("clazz", new Model(
                this.clazz.getSimpleName().substring(0, this.clazz.getSimpleName().indexOf("Execution")))));

        BackupRestoreExecutionsProvider provider = new BackupRestoreExecutionsProvider(getType()) {
            @Override
            protected List<Property<AbstractExecutionAdapter>> getProperties() {
                return Arrays.asList(ID, STATE, STARTED, PROGRESS, ARCHIVEFILE, OPTIONS);
            }

            @Override
            protected List<T> getItems() {
                return Collections.singletonList(model.getObject());
            }
        };

        final BackupRestoreExecutionsTable headerTable = new BackupRestoreExecutionsTable("header", provider,
                getType());

        headerTable.setOutputMarkupId(true);
        headerTable.setFilterable(false);
        headerTable.setPageable(false);
        add(headerTable);

        final T bkp = model.getObject();
        boolean selectable = bkp.getStatus() != BatchStatus.COMPLETED;

        add(new Icon("icon", COMPRESS_ICON));
        add(new Label("title", new DataTitleModel(bkp))
                .add(new AttributeModifier("title", new DataTitleModel(bkp, false))));

        @SuppressWarnings("rawtypes")
        Form<?> form = new Form("form");
        add(form);

        try {
            if (params != null && params.getNamedKeys().contains(DETAILS_LEVEL)) {
                if (params.get(DETAILS_LEVEL).toInt() > 0) {
                    expand = params.get(DETAILS_LEVEL).toInt();
                }
            }
        } catch (Exception e) {
            LOGGER.log(Level.WARNING, "Error parsing the 'details level' parameter: ",
                    params.get(DETAILS_LEVEL).toString());
        }

        form.add(new SubmitLink("refresh") {
            @Override
            public void onSubmit() {
                setResponsePage(BackupRestorePage.class, new PageParameters().add("id", params.get("id").toLong())
                        .add("clazz", getType().getSimpleName()).add(DETAILS_LEVEL, expand));
            }
        });

        NumberTextField<Integer> expand = new NumberTextField<Integer>("expand",
                new PropertyModel<Integer>(this, "expand"));
        expand.add(RangeValidator.minimum(0));
        form.add(expand);

        TextArea<String> details = new TextArea<String>("details", new BKErrorDetailsModel(bkp));
        details.setOutputMarkupId(true);
        details.setMarkupId("details");
        add(details);

        String location = bkp.getArchiveFile().path();
        if (location == null) {
            location = getGeoServerApplication().getGeoServer().getLogging().getLocation();
        }
        backupFile = new File(location);
        if (!backupFile.isAbsolute()) {
            // locate the geoserver.log file
            GeoServerDataDirectory dd = getGeoServerApplication().getBeanOfType(GeoServerDataDirectory.class);
            backupFile = dd.get(Paths.convert(backupFile.getPath())).file();
        }

        if (!backupFile.exists()) {
            error("Could not find the Backup Archive file: " + backupFile.getAbsolutePath());
        }

        /***
         * DOWNLOAD LINK
         */
        final Link<Object> downLoadLink = new Link<Object>("download") {

            @Override
            public void onClick() {
                IResourceStream stream = new FileResourceStream(backupFile) {
                    public String getContentType() {
                        return "application/zip";
                    }
                };
                ResourceStreamRequestHandler handler = new ResourceStreamRequestHandler(stream,
                        backupFile.getName());
                handler.setContentDisposition(ContentDisposition.ATTACHMENT);

                RequestCycle.get().scheduleRequestHandlerAfterCurrent(handler);
            }
        };
        add(downLoadLink);

        /***
         * PAUSE LINK
         */
        final AjaxLink pauseLink = new AjaxLink("pause") {

            @Override
            protected void disableLink(ComponentTag tag) {
                super.disableLink(tag);
                tag.setName("a");
                tag.addBehavior(AttributeModifier.replace("class", "disabled"));
            }

            @Override
            public void onClick(AjaxRequestTarget target) {
                AbstractExecutionAdapter bkp = model.getObject();
                if (bkp.getStatus() == BatchStatus.STOPPED) {
                    setLinkEnabled((AjaxLink) downLoadLink.getParent().get("pause"), false, target);
                } else {
                    try {
                        backupFacade().stopExecution(bkp.getId());

                        setResponsePage(BackupRestoreDataPage.class);
                    } catch (NoSuchJobExecutionException | JobExecutionNotRunningException e) {
                        LOGGER.log(Level.WARNING, "", e);
                        getSession().error(e);
                        setResponsePage(BackupRestoreDataPage.class);
                    }
                }
            }

        };
        pauseLink.setEnabled(doSelectReady(bkp) && bkp.getStatus() != BatchStatus.STOPPED);
        add(pauseLink);

        /***
         * RESUME LINK
         */
        final AjaxLink resumeLink = new AjaxLink("resume") {

            @Override
            protected void disableLink(ComponentTag tag) {
                super.disableLink(tag);
                tag.setName("a");
                tag.addBehavior(AttributeModifier.replace("class", "disabled"));
            }

            @Override
            public void onClick(AjaxRequestTarget target) {
                AbstractExecutionAdapter bkp = model.getObject();
                if (bkp.getStatus() != BatchStatus.STOPPED) {
                    setLinkEnabled((AjaxLink) downLoadLink.getParent().get("pause"), false, target);
                } else {
                    try {
                        Long id = backupFacade().restartExecution(bkp.getId());

                        PageParameters pp = new PageParameters();
                        pp.add("id", id);
                        if (bkp instanceof BackupExecutionAdapter) {
                            pp.add("clazz", BackupExecutionAdapter.class.getSimpleName());
                        } else if (bkp instanceof RestoreExecutionAdapter) {
                            pp.add("clazz", RestoreExecutionAdapter.class.getSimpleName());
                        }

                        setResponsePage(BackupRestorePage.class, pp);
                    } catch (NoSuchJobExecutionException | JobInstanceAlreadyCompleteException | NoSuchJobException
                            | JobRestartException | JobParametersInvalidException e) {
                        LOGGER.log(Level.WARNING, "", e);
                        getSession().error(e);
                        setResponsePage(BackupRestoreDataPage.class);
                    }
                }
            }

        };
        resumeLink.setEnabled(bkp.getStatus() == BatchStatus.STOPPED);
        add(resumeLink);

        /***
         * ABANDON LINK
         */
        final AjaxLink cancelLink = new AjaxLink("cancel") {

            @Override
            protected void disableLink(ComponentTag tag) {
                super.disableLink(tag);
                tag.setName("a");
                tag.addBehavior(AttributeModifier.replace("class", "disabled"));
            }

            @Override
            public void onClick(AjaxRequestTarget target) {
                AbstractExecutionAdapter bkp = model.getObject();
                if (!doSelectReady(bkp)) {
                    setLinkEnabled((AjaxLink) downLoadLink.getParent().get("cancel"), false, target);
                } else {
                    try {
                        backupFacade().abandonExecution(bkp.getId());

                        PageParameters pp = new PageParameters();
                        pp.add("id", bkp.getId());
                        if (bkp instanceof BackupExecutionAdapter) {
                            pp.add("clazz", BackupExecutionAdapter.class.getSimpleName());
                        } else if (bkp instanceof RestoreExecutionAdapter) {
                            pp.add("clazz", RestoreExecutionAdapter.class.getSimpleName());
                        }

                        setResponsePage(BackupRestorePage.class, pp);
                    } catch (NoSuchJobExecutionException | JobExecutionAlreadyRunningException e) {
                        error(e);
                        LOGGER.log(Level.WARNING, "", e);
                    }
                }
            }

        };
        cancelLink.setEnabled(doSelectReady(bkp));
        add(cancelLink);

        /***
         * DONE LINK
         */
        final AjaxLink doneLink = new AjaxLink("done") {

            @Override
            protected void disableLink(ComponentTag tag) {
                super.disableLink(tag);
                tag.setName("a");
                tag.addBehavior(AttributeModifier.replace("class", "disabled"));
            }

            @Override
            public void onClick(AjaxRequestTarget target) {
                setResponsePage(BackupRestoreDataPage.class);
                return;
            }

        };
        add(doneLink);

        /**
         * FINALIZE
         */
        add(dialog = new GeoServerDialog("dialog"));
    }

    /**
     * @param bkp
     * @return
     */
    private boolean doSelectReady(AbstractExecutionAdapter bkp) {
        if (bkp.getStatus() == BatchStatus.COMPLETED || bkp.getStatus() == BatchStatus.FAILED
                || bkp.getStatus() == BatchStatus.ABANDONED) {
            return false;
        }

        return true;
    }

    @Override
    public String getAjaxIndicatorMarkupId() {
        return null;
    }

    class DataTitleModel<T extends AbstractExecutionAdapter> extends LoadableDetachableModel<String> {

        long contextId;

        boolean abbrev;

        DataTitleModel(T bkp) {
            this(bkp, true);
        }

        DataTitleModel(T bkp, boolean abbrev) {
            this.contextId = bkp.getId();
            this.abbrev = abbrev;
        }

        @Override
        protected String load() {
            AbstractExecutionAdapter ctx = null;

            if (getType() == BackupExecutionAdapter.class) {
                ctx = backupFacade().getBackupExecutions().get(contextId);
            } else if (getType() == RestoreExecutionAdapter.class) {
                ctx = backupFacade().getRestoreExecutions().get(contextId);
            }
            String title = ctx.getArchiveFile() != null ? ctx.getArchiveFile().path() : ctx.toString();

            if (abbrev && title.length() > 70) {
                // shorten it
                title = title.substring(0, 20) + "[...]" + title.substring(title.length() - 50);
            }

            title = title + " [" + humanReadableByteCount(FileUtils.sizeOf(ctx.getArchiveFile().file()), false)
                    + "]";

            return title;
        }

    }

    void setLinkEnabled(AjaxLink link, boolean enabled, AjaxRequestTarget target) {
        link.setEnabled(enabled);
        target.add(link);
    }

    class BKErrorDetailsModel<T extends AbstractExecutionAdapter> extends LoadableDetachableModel<String> {

        long contextId;

        public BKErrorDetailsModel(AbstractExecutionAdapter bkp) {
            this.contextId = bkp.getId();
        }

        @Override
        protected String load() {
            AbstractExecutionAdapter ctx = null;

            if (getType() == BackupExecutionAdapter.class) {
                ctx = backupFacade().getBackupExecutions().get(contextId);
            } else if (getType() == RestoreExecutionAdapter.class) {
                ctx = backupFacade().getRestoreExecutions().get(contextId);
            }

            StringBuilder buf = new StringBuilder();
            if (!ctx.getAllFailureExceptions().isEmpty()) {
                for (Throwable ex : ctx.getAllFailureExceptions()) {
                    ex = writeException(buf, ex, Level.SEVERE);
                }
            } else {
                buf.append("\nNO Exceptions Detected.\n");
            }

            if (!ctx.getAllWarningExceptions().isEmpty()) {
                for (Throwable ex : ctx.getAllWarningExceptions()) {
                    ex = writeException(buf, ex, Level.WARNING);
                }
            } else {
                buf.append("\nNO Warnings Detected.\n");
            }

            return buf.toString();
        }

        /**
         * @param buf
         * @param ex
         * @param severe
         * @return
         */
        private Throwable writeException(StringBuilder buf, Throwable ex, Level level) {
            int cnt = 0;
            while (ex != null) {
                if (buf.length() > 0) {
                    buf.append('\n');
                }
                if (ex.getMessage() != null) {
                    buf.append(level).append(":");
                    buf.append(ex.getMessage());
                    cnt++;
                }
                if (BackupRestorePage.this.expand > 0 && BackupRestorePage.this.expand >= cnt) {
                    StringWriter errors = new StringWriter();
                    ex.printStackTrace(new PrintWriter(errors));
                    buf.append('\n').append(errors.toString());
                }

                ex = ex.getCause();
            }
            return ex;
        }

    }
}