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

Java tutorial

Introduction

Here is the source code for org.geoserver.backuprestore.web.BackupRestoreDataPage.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 java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.io.FilenameUtils;
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.Component;
import org.apache.wicket.ajax.AbstractAjaxTimerBehavior;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.ajax.markup.html.form.AjaxSubmitLink;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.CheckBox;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.Model;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.util.time.Duration;
import org.geoserver.backuprestore.AbstractExecutionAdapter;
import org.geoserver.backuprestore.Backup;
import org.geoserver.backuprestore.BackupExecutionAdapter;
import org.geoserver.backuprestore.RestoreExecutionAdapter;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.WorkspaceInfo;
import org.geoserver.platform.resource.Resource;
import org.geoserver.platform.resource.Resource.Type;
import org.geoserver.platform.resource.Resources;
import org.geoserver.web.GeoServerApplication;
import org.geoserver.web.GeoServerSecuredPage;
import org.geoserver.web.GeoServerUnlockablePage;
import org.geoserver.web.data.workspace.WorkspaceChoiceRenderer;
import org.geoserver.web.data.workspace.WorkspaceDetachableModel;
import org.geoserver.web.data.workspace.WorkspacesModel;
import org.geoserver.web.wicket.GeoServerDialog;
import org.geotools.factory.Hints;
import org.geotools.filter.text.ecql.ECQL;
import org.geotools.util.logging.Logging;
import org.opengis.filter.Filter;
import org.springframework.batch.core.launch.JobExecutionNotRunningException;
import org.springframework.batch.core.launch.NoSuchJobExecutionException;

/**
 * First page of the backup wizard.
 * 
 * @author Andrea Aime - OpenGeo
 * @author Justin Deoliveira, OpenGeo
 */
@SuppressWarnings("serial")
public class BackupRestoreDataPage extends GeoServerSecuredPage implements GeoServerUnlockablePage {

    static Logger LOGGER = Logging.getLogger(BackupRestoreDataPage.class);

    WorkspaceDetachableModel workspace;
    DropDownChoice workspaceChoice;
    TextField workspaceNameTextField;

    Component statusLabel;

    BackupRestoreExecutionsTable backupRestoreExecutionsTable;
    BackupRestoreExecutionsTable restoreExecutionsTable;

    WebMarkupContainer newBackupRestorePanel;

    GeoServerDialog dialog;

    public BackupRestoreDataPage(PageParameters params) {

        newBackupRestorePanel = new WebMarkupContainer("newBackupRestorePanel");
        newBackupRestorePanel.setOutputMarkupId(true);

        resetResourcePanel();

        add(newBackupRestorePanel);

        Catalog catalog = GeoServerApplication.get().getCatalog();

        // workspace chooser
        workspace = new WorkspaceDetachableModel(null);
        workspaceChoice = new DropDownChoice("workspace", workspace, new WorkspacesModel(),
                new WorkspaceChoiceRenderer());
        workspaceChoice.setOutputMarkupId(true);
        workspaceChoice.add(new AjaxFormComponentUpdatingBehavior("change") {
            @Override
            protected void onUpdate(AjaxRequestTarget target) {
                updateTargetWorkspace(target);
            }
        });
        workspaceChoice.setNullValid(true);
        add(workspaceChoice);

        WebMarkupContainer workspaceNameContainer = new WebMarkupContainer("workspaceNameContainer");
        workspaceNameContainer.setOutputMarkupId(true);
        add(workspaceNameContainer);

        workspaceNameTextField = new TextField("workspaceName", new Model());
        workspaceNameTextField.setOutputMarkupId(true);
        boolean defaultWorkspace = catalog.getDefaultWorkspace() != null;
        workspaceNameTextField.setVisible(!defaultWorkspace);
        workspaceNameTextField.setRequired(!defaultWorkspace);
        workspaceNameContainer.add(workspaceNameTextField);

        /**
         * Backup Panel
         */
        Form backupForm = new Form("backupForm");
        add(backupForm);

        populateBackupForm(backupForm);

        /**
         * Restore Panel
         */
        Form restoreForm = new Form("restoreForm");
        add(restoreForm);

        populateRestoreForm(restoreForm);

        /**
         * 
         */
        add(dialog = new GeoServerDialog("dialog"));
        dialog.setInitialWidth(600);
        dialog.setInitialHeight(400);
        dialog.setMinimalHeight(150);
    }

    /**
     * @param target
     */
    protected void updateTargetWorkspace(AjaxRequestTarget target) {
        WorkspaceInfo ws = (WorkspaceInfo) workspace.getObject();

        //workspaceNameTextField.setVisible(ws == null);
        //workspaceNameTextField.setRequired(ws == null);

        /*if (target != null) {
        target.add(workspaceNameTextField.getParent());
        }*/
    }

    /**
     * 
     */
    private void resetResourcePanel() {
        if (newBackupRestorePanel.size() > 0) {
            newBackupRestorePanel.remove("backupResource");
        }

        Panel p = new ResourceFilePanel("backupResource");
        newBackupRestorePanel.add(p);
    }

    /**
     * @param form
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    private void populateBackupForm(Form form) {
        form.add(new CheckBox("backupOptOverwirte", new Model<Boolean>(false)));
        form.add(new CheckBox("backupOptBestEffort", new Model<Boolean>(false)));
        form.add(statusLabel = new Label("status", new Model()).setOutputMarkupId(true));
        form.add(new AjaxSubmitLink("newBackupStart", form) {
            @Override
            protected void disableLink(ComponentTag tag) {
                super.disableLink(tag);
                tag.setName("a");
                tag.addBehavior(AttributeModifier.replace("class", "disabled"));
            }

            protected void onError(AjaxRequestTarget target, Form<?> form) {
                target.add(feedbackPanel);
            }

            protected void onSubmit(AjaxRequestTarget target, final Form<?> form) {

                //update status to indicate we are working
                statusLabel.add(AttributeModifier.replace("class", "working-link"));
                statusLabel.setDefaultModelObject("Working");
                target.add(statusLabel);

                //enable cancel and disable this
                Component cancel = form.get("cancel");
                cancel.setEnabled(true);
                target.add(cancel);

                setEnabled(false);
                target.add(this);

                final AjaxSubmitLink self = this;

                final Long jobid;
                try {
                    jobid = launchBackupExecution(form);
                } catch (Exception e) {
                    error(e);
                    LOGGER.log(Level.WARNING, "Error starting a new Backup", e);
                    return;
                } finally {
                    //update the button back to original state
                    resetButtons(form, target, "newBackupStart");

                    target.add(feedbackPanel);
                }

                cancel.setDefaultModelObject(jobid);
                this.add(new AbstractAjaxTimerBehavior(Duration.milliseconds(100)) {
                    @Override
                    protected void onTimer(AjaxRequestTarget target) {
                        Backup backupFacade = BackupRestoreWebUtils.backupFacade();
                        BackupExecutionAdapter exec = backupFacade.getBackupExecutions().get(jobid);

                        if (!exec.isRunning()) {
                            try {
                                if (exec.getAllFailureExceptions() != null
                                        && !exec.getAllFailureExceptions().isEmpty()) {
                                    getSession().error(exec.getAllFailureExceptions().get(0));
                                    setResponsePage(BackupRestoreDataPage.class);
                                } else if (exec.isStopping()) {
                                    //do nothing
                                } else {
                                    PageParameters pp = new PageParameters();
                                    pp.add("id", exec.getId());
                                    pp.add("clazz", BackupExecutionAdapter.class.getSimpleName());

                                    setResponsePage(BackupRestorePage.class, pp);
                                }
                            } catch (Exception e) {
                                error(e);
                                LOGGER.log(Level.WARNING, "", e);
                            } finally {
                                stop(null);

                                //update the button back to original state
                                resetButtons(form, target, "newBackupStart");

                                target.add(feedbackPanel);
                            }
                            return;
                        }

                        String msg = exec != null ? exec.getStatus().toString() : "Working";

                        statusLabel.setDefaultModelObject(msg);
                        target.add(statusLabel);
                    };

                    @Override
                    public boolean canCallListenerInterface(Component component, Method method) {
                        if (self.equals(component)
                                && method.getDeclaringClass()
                                        .equals(org.apache.wicket.behavior.IBehaviorListener.class)
                                && method.getName().equals("onRequest")) {
                            return true;
                        }
                        return super.canCallListenerInterface(component, method);
                    }
                });
            }

            private Long launchBackupExecution(Form<?> form) throws Exception {
                ResourceFilePanel panel = (ResourceFilePanel) newBackupRestorePanel.get("backupResource");
                Resource archiveFile = null;
                try {
                    archiveFile = panel.getResource();
                } catch (NullPointerException e) {
                    throw new Exception("Backup Archive File is Mandatory!");
                }

                if (archiveFile == null || archiveFile.getType() == Type.DIRECTORY
                        || FilenameUtils.getExtension(archiveFile.name()).isEmpty()) {
                    throw new Exception("Backup Archive File is Mandatory and should not be a Directory or URI.");
                }

                Filter filter = null;
                WorkspaceInfo ws = (WorkspaceInfo) workspace.getObject();
                if (ws != null) {
                    filter = ECQL.toFilter("name = '" + ws.getName() + "'");
                }

                Hints hints = new Hints(new HashMap(2));

                Boolean backupOptOverwirte = ((CheckBox) form.get("backupOptOverwirte")).getModelObject();
                Boolean backupOptBestEffort = ((CheckBox) form.get("backupOptBestEffort")).getModelObject();

                if (backupOptBestEffort) {
                    hints.add(new Hints(new Hints.OptionKey(Backup.PARAM_BEST_EFFORT_MODE),
                            Backup.PARAM_BEST_EFFORT_MODE));
                }

                Backup backupFacade = BackupRestoreWebUtils.backupFacade();

                return backupFacade.runBackupAsync(archiveFile, backupOptOverwirte, filter, hints).getId();
            }
        });

        form.add(new AjaxLink<Long>("cancel", new Model<Long>()) {
            protected void disableLink(ComponentTag tag) {
                super.disableLink(tag);
                tag.setName("a");
                tag.addBehavior(AttributeModifier.replace("class", "disabled"));
            };

            @Override
            public void onClick(AjaxRequestTarget target) {
                Long jobid = getModelObject();

                if (jobid != null) {
                    try {
                        BackupRestoreWebUtils.backupFacade().stopExecution(jobid);
                        setResponsePage(BackupRestoreDataPage.class);
                    } catch (NoSuchJobExecutionException | JobExecutionNotRunningException e) {
                        LOGGER.log(Level.WARNING, "", e);
                    }
                }

                setEnabled(false);

                target.add(this);
            }
        }.setOutputMarkupId(true).setEnabled(false));

        backupRestoreExecutionsTable = new BackupRestoreExecutionsTable("backups",
                new BackupRestoreExecutionsProvider(true, BackupExecutionAdapter.class) {
                    @Override
                    protected List<org.geoserver.web.wicket.GeoServerDataProvider.Property<AbstractExecutionAdapter>> getProperties() {
                        return Arrays.asList(ID, STATE, STARTED, PROGRESS, ARCHIVEFILE, OPTIONS);
                    }
                }, true, BackupExecutionAdapter.class) {
            protected void onSelectionUpdate(AjaxRequestTarget target) {
            };
        };
        backupRestoreExecutionsTable.setOutputMarkupId(true);
        backupRestoreExecutionsTable.setFilterable(false);
        backupRestoreExecutionsTable.setSortable(false);
        form.add(backupRestoreExecutionsTable);
    }

    /**
     * @param form
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    private void populateRestoreForm(Form form) {
        form.add(new CheckBox("restoreOptDryRun", new Model<Boolean>(false)));
        form.add(new CheckBox("restoreOptBestEffort", new Model<Boolean>(false)));
        form.add(statusLabel = new Label("status", new Model()).setOutputMarkupId(true));
        form.add(new AjaxSubmitLink("newRestoreStart", form) {
            @Override
            protected void disableLink(ComponentTag tag) {
                super.disableLink(tag);
                tag.setName("a");
                tag.addBehavior(AttributeModifier.replace("class", "disabled"));
            }

            protected void onError(AjaxRequestTarget target, Form<?> form) {
                target.add(feedbackPanel);
            }

            protected void onSubmit(AjaxRequestTarget target, final Form<?> form) {

                //update status to indicate we are working
                statusLabel.add(AttributeModifier.replace("class", "working-link"));
                statusLabel.setDefaultModelObject("Working");
                target.add(statusLabel);

                //enable cancel and disable this
                Component cancel = form.get("cancel");
                cancel.setEnabled(true);
                target.add(cancel);

                setEnabled(false);
                target.add(this);

                final AjaxSubmitLink self = this;

                final Long jobid;
                try {
                    jobid = launchRestoreExecution(form);
                } catch (Exception e) {
                    error(e);
                    LOGGER.log(Level.WARNING, "Error starting a new Restore", e);
                    return;
                } finally {
                    //update the button back to original state
                    resetButtons(form, target, "newRestoreStart");

                    target.add(feedbackPanel);
                }

                cancel.setDefaultModelObject(jobid);
                this.add(new AbstractAjaxTimerBehavior(Duration.milliseconds(100)) {
                    @Override
                    protected void onTimer(AjaxRequestTarget target) {
                        Backup backupFacade = BackupRestoreWebUtils.backupFacade();
                        RestoreExecutionAdapter exec = backupFacade.getRestoreExecutions().get(jobid);

                        if (!exec.isRunning()) {
                            try {
                                if (exec.getAllFailureExceptions() != null
                                        && !exec.getAllFailureExceptions().isEmpty()) {
                                    getSession().error(exec.getAllFailureExceptions().get(0));
                                    setResponsePage(BackupRestoreDataPage.class);
                                } else if (exec.isStopping()) {
                                    //do nothing
                                } else {
                                    PageParameters pp = new PageParameters();
                                    pp.add("id", exec.getId());
                                    pp.add("clazz", RestoreExecutionAdapter.class.getSimpleName());

                                    setResponsePage(BackupRestorePage.class, pp);
                                }
                            } catch (Exception e) {
                                error(e);
                                LOGGER.log(Level.WARNING, "", e);
                            } finally {
                                stop(null);

                                //update the button back to original state
                                resetButtons(form, target, "newRestoreStart");

                                target.add(feedbackPanel);
                            }
                            return;
                        }

                        String msg = exec != null ? exec.getStatus().toString() : "Working";

                        statusLabel.setDefaultModelObject(msg);
                        target.add(statusLabel);
                    };

                    @Override
                    public boolean canCallListenerInterface(Component component, Method method) {
                        if (self.equals(component)
                                && method.getDeclaringClass()
                                        .equals(org.apache.wicket.behavior.IBehaviorListener.class)
                                && method.getName().equals("onRequest")) {
                            return true;
                        }
                        return super.canCallListenerInterface(component, method);
                    }
                });
            }

            private Long launchRestoreExecution(Form<?> form) throws Exception {
                ResourceFilePanel panel = (ResourceFilePanel) newBackupRestorePanel.get("backupResource");
                Resource archiveFile = null;
                try {
                    archiveFile = panel.getResource();
                } catch (NullPointerException e) {
                    throw new Exception("Restore Archive File is Mandatory!");
                }

                if (archiveFile == null || !Resources.exists(archiveFile) || archiveFile.getType() == Type.DIRECTORY
                        || FilenameUtils.getExtension(archiveFile.name()).isEmpty()) {
                    throw new Exception(
                            "Restore Archive File is Mandatory, must exists and should not be a Directory or URI.");
                }

                Filter filter = null;
                WorkspaceInfo ws = (WorkspaceInfo) workspace.getObject();
                if (ws != null) {
                    filter = ECQL.toFilter("name = '" + ws.getName() + "'");
                }

                Hints hints = new Hints(new HashMap(2));

                Boolean restoreOptDryRun = ((CheckBox) form.get("restoreOptDryRun")).getModelObject();

                if (restoreOptDryRun) {
                    hints.add(new Hints(new Hints.OptionKey(Backup.PARAM_DRY_RUN_MODE), Backup.PARAM_DRY_RUN_MODE));
                }

                Boolean restoreOptBestEffort = ((CheckBox) form.get("restoreOptBestEffort")).getModelObject();

                if (restoreOptBestEffort) {
                    hints.add(new Hints(new Hints.OptionKey(Backup.PARAM_BEST_EFFORT_MODE),
                            Backup.PARAM_BEST_EFFORT_MODE));
                }

                Backup backupFacade = BackupRestoreWebUtils.backupFacade();

                return backupFacade.runRestoreAsync(archiveFile, filter, hints).getId();
            }
        });

        form.add(new AjaxLink<Long>("cancel", new Model<Long>()) {
            protected void disableLink(ComponentTag tag) {
                super.disableLink(tag);
                tag.setName("a");
                tag.addBehavior(AttributeModifier.replace("class", "disabled"));
            };

            @Override
            public void onClick(AjaxRequestTarget target) {
                Long jobid = getModelObject();

                if (jobid != null) {
                    try {
                        BackupRestoreWebUtils.backupFacade().stopExecution(jobid);
                        setResponsePage(BackupRestoreDataPage.class);
                    } catch (NoSuchJobExecutionException | JobExecutionNotRunningException e) {
                        LOGGER.log(Level.WARNING, "", e);
                    }
                }

                setEnabled(false);

                target.add(this);
            }
        }.setOutputMarkupId(true).setEnabled(false));

        restoreExecutionsTable = new BackupRestoreExecutionsTable("restores",
                new BackupRestoreExecutionsProvider(true, RestoreExecutionAdapter.class) {
                    @Override
                    protected List<org.geoserver.web.wicket.GeoServerDataProvider.Property<AbstractExecutionAdapter>> getProperties() {
                        return Arrays.asList(ID, STATE, STARTED, PROGRESS, ARCHIVEFILE, OPTIONS);
                    }
                }, true, RestoreExecutionAdapter.class) {
            protected void onSelectionUpdate(AjaxRequestTarget target) {
            };
        };
        restoreExecutionsTable.setOutputMarkupId(true);
        restoreExecutionsTable.setFilterable(false);
        restoreExecutionsTable.setSortable(false);
        form.add(restoreExecutionsTable);
    }

    protected void resetButtons(Form<?> form, AjaxRequestTarget target, String buttonId) {
        form.get(buttonId).setEnabled(true);
        statusLabel.setDefaultModelObject("");
        statusLabel.add(AttributeModifier.replace("class", ""));

        target.add(form.get(buttonId));
        target.add(form.get("status"));
    }
}