org.geoserver.web.publish.PublishedConfigurationPage.java Source code

Java tutorial

Introduction

Here is the source code for org.geoserver.web.publish.PublishedConfigurationPage.java

Source

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

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry;
import java.util.logging.Level;

import org.apache.wicket.Component;
import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
import org.apache.wicket.extensions.markup.html.tabs.ITab;
import org.apache.wicket.extensions.markup.html.tabs.TabbedPanel;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.SubmitLink;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.geoserver.catalog.PublishedInfo;
import org.geoserver.web.ComponentAuthorizer;
import org.geoserver.web.GeoServerSecuredPage;
import org.geoserver.web.data.resource.ResourceConfigurationPanel;

/**
 * Page allowing to configure a layer(group) (and its resource).
 * <p>
 * The page is completely pluggable, the UI will be made up by scanning the Spring context for
 * implementations of {@link ResourceConfigurationPanel} and {@link PublishedConfigurationPanel}.
 * <p>
 * WARNING: one crucial aspect of this page is its ability to not loose edits when one switches from
 * one tab to the other. I did not find any effective way to unit test this, so _please_, if you do
 * modify anything in this class (especially the models), manually retest that the edits are not
 * lost on tab switch.
 * 
 * @author Niels Charlier
 */
public abstract class PublishedConfigurationPage<T extends PublishedInfo> extends GeoServerSecuredPage {

    private static final long serialVersionUID = 7870938096047218989L;

    public static final String NAME = "name";

    public static final String WORKSPACE = "wsName";

    protected IModel<T> myModel;

    protected boolean isNew;

    protected TabbedPanel tabbedPanel;

    /**
     * {@link PublishedEditTabPanel} contributions may need to edit something different than the
     * LayerInfo and ResourceInfo this page holds models to. In such cases
     * {@link PublishedEditTabPanelInfo#createOwnModel} will return a non null model and well pass it
     * back to the concrete LayerEditTabPanel constructor. This is so because LayerEditTabPanel are
     * re-created everytime the user switches tabs.
     */
    private LinkedHashMap<Class<? extends PublishedEditTabPanel<T>>, IModel<?>> tabPanelCustomModels;

    private boolean inputEnabled = true;

    protected PublishedConfigurationPage(boolean isNew) {
        this.isNew = isNew;
    }

    protected PublishedConfigurationPage(T info, boolean isNew) {
        this.isNew = isNew;
        setupPublished(info);
    }

    protected void setupPublished(T info) {
        setupPublished(new Model<T>(info));
    }

    protected void setupPublished(IModel<T> infoModel) {
        myModel = new CompoundPropertyModel<T>(infoModel);
        initComponents();
    }

    /**
     * 
     */
    @SuppressWarnings("rawtypes")
    private void initComponents() {
        this.tabPanelCustomModels = new LinkedHashMap<Class<? extends PublishedEditTabPanel<T>>, IModel<?>>();

        add(new Label("publishedinfoname", getPublishedInfo().prefixedName()));
        Form<T> theForm = new Form<T>("publishedinfo", myModel);
        add(theForm);

        List<ITab> tabs = new ArrayList<ITab>();

        // add the "well known" tabs
        tabs.add(new AbstractTab(new org.apache.wicket.model.ResourceModel("ResourceConfigurationPage.Data")) {
            private static final long serialVersionUID = 1L;

            public Panel getPanel(String panelID) {
                return createMainTab(panelID).setInputEnabled(inputEnabled);
            }
        });

        tabs.add(
                new AbstractTab(new org.apache.wicket.model.ResourceModel("ResourceConfigurationPage.Publishing")) {
                    private static final long serialVersionUID = 1L;

                    public Panel getPanel(String panelID) {
                        return new PublishingEditTabPanel(panelID).setInputEnabled(inputEnabled);
                    }
                });

        // add the tabs contributed via extension point
        List<PublishedEditTabPanelInfo> tabPanels = getGeoServerApplication()
                .getBeansOfType(PublishedEditTabPanelInfo.class);

        // sort the tabs based on order
        Collections.sort(tabPanels, new Comparator<PublishedEditTabPanelInfo>() {
            public int compare(PublishedEditTabPanelInfo o1, PublishedEditTabPanelInfo o2) {
                Integer order1 = o1.getOrder() >= 0 ? o1.getOrder() : Integer.MAX_VALUE;
                Integer order2 = o2.getOrder() >= 0 ? o2.getOrder() : Integer.MAX_VALUE;

                return order1.compareTo(order2);
            }
        });

        for (PublishedEditTabPanelInfo ttabPanelInfo : tabPanels) {
            if (ttabPanelInfo.supports(getPublishedInfo())) {

                @SuppressWarnings("unchecked")
                PublishedEditTabPanelInfo<T> tabPanelInfo = (PublishedEditTabPanelInfo<T>) ttabPanelInfo;

                String titleKey = tabPanelInfo.getTitleKey();
                IModel<String> titleModel = null;
                if (titleKey != null) {
                    titleModel = new org.apache.wicket.model.ResourceModel(titleKey);
                } else {
                    titleModel = new Model<String>(tabPanelInfo.getComponentClass().getSimpleName());
                }

                final Class<PublishedEditTabPanel<T>> panelClass = tabPanelInfo.getComponentClass();
                IModel<?> panelCustomModel = tabPanelInfo.createOwnModel(myModel, isNew);
                tabPanelCustomModels.put(panelClass, panelCustomModel);

                tabs.add(new AbstractTab(titleModel) {
                    private static final long serialVersionUID = -6637277497986497791L;
                    private final Class<PublishedEditTabPanel<T>> panelType = panelClass;

                    @Override
                    public Panel getPanel(String panelId) {
                        PublishedEditTabPanel<?> tabPanel;
                        final IModel<?> panelCustomModel = tabPanelCustomModels.get(panelType);
                        try {
                            // if this tab needs a custom model instead of just our layer model, then
                            // let it create it once
                            if (panelCustomModel == null) {
                                tabPanel = panelClass.getConstructor(String.class, IModel.class)
                                        .newInstance(panelId, myModel);
                            } else {
                                tabPanel = panelClass.getConstructor(String.class, IModel.class, IModel.class)
                                        .newInstance(panelId, myModel, panelCustomModel);
                            }
                        } catch (Exception e) {
                            throw new WicketRuntimeException(e);
                            // LOGGER.log(Level.WARNING, "Error creating resource panel", e);
                        }
                        return tabPanel;
                    }
                });
            }
        }

        // we need to override with submit links so that the various form
        // element
        // will validate and write down into their
        tabbedPanel = new TabbedPanel("tabs", tabs) {
            private static final long serialVersionUID = 1L;

            @Override
            protected WebMarkupContainer newLink(String linkId, final int index) {
                return new SubmitLink(linkId) {
                    private static final long serialVersionUID = 1L;

                    @Override
                    public void onSubmit() {
                        setSelectedTab(index);
                    }
                };
            }
        };
        theForm.add(tabbedPanel);
        theForm.add(saveLink());
        theForm.add(cancelLink());
    }

    protected abstract PublishedEditTabPanel<T> createMainTab(String panelID);

    protected abstract void doSaveInternal() throws IOException;

    public void setSelectedTab(Class<? extends PublishedEditTabPanel<?>> selectedTabClass) {
        int selectedTabIndex;
        //relying on LinkedHashMap here
        selectedTabIndex = new ArrayList<Class<? extends PublishedEditTabPanel<T>>>(tabPanelCustomModels.keySet())
                .indexOf(selectedTabClass);
        if (selectedTabIndex > -1) {
            tabbedPanel.setSelectedTab(selectedTabIndex);
        }
    }

    public void selectDataTab() {
        tabbedPanel.setSelectedTab(0);
    }

    public void selectPublishingTab() {
        tabbedPanel.setSelectedTab(1);
    }

    protected void disableForm() {
        get("publishedinfo").setEnabled(false);
    }

    private SubmitLink saveLink() {
        return new SubmitLink("save") {
            private static final long serialVersionUID = 1839992481355433705L;

            @Override
            public void onSubmit() {
                doSave();
            }
        };
    }

    /**
     * Performs the necessary operation on save.
     * <p>
     * This implementation adds the necessary objects to the catalog, respecting the isNew flag, and
     * calls {@link #onSuccessfulSave()} upon success.
     * </p>
     */
    protected void doSave() {
        try {
            doSaveInternal();
            if (hasErrorMessage()) {
                return;
            }

            for (Entry<Class<? extends PublishedEditTabPanel<T>>, IModel<?>> e : tabPanelCustomModels.entrySet()) {
                Class<? extends PublishedEditTabPanel<T>> panelClass = e.getKey();
                IModel<?> customModel = e.getValue();
                if (customModel == null) {
                    continue;
                }
                PublishedEditTabPanel<?> tabPanel = panelClass
                        .getConstructor(String.class, IModel.class, IModel.class)
                        .newInstance("temp", myModel, customModel);
                tabPanel.save();
            }

            onSuccessfulSave();
        } catch (Exception e) {
            LOGGER.log(Level.INFO, "Error saving layer", e);
            error(e.getMessage() == null ? e.toString() : e.getMessage());
        }
    }

    @SuppressWarnings("rawtypes")
    private Link<?> cancelLink() {
        return new Link("cancel") {
            private static final long serialVersionUID = -9007727127569731882L;

            @Override
            public void onClick() {
                onCancel();
            }
        };
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    private List<PublishedConfigurationPanelInfo<T>> filterPublishedPanels(
            List<PublishedConfigurationPanelInfo> list) {
        List<PublishedConfigurationPanelInfo<T>> result = new ArrayList<PublishedConfigurationPanelInfo<T>>();
        for (PublishedConfigurationPanelInfo info : list) {
            if (info.canHandle(getPublishedInfo())) {
                result.add((PublishedConfigurationPanelInfo<T>) info);
            }
        }
        return result;
    }

    /**
     * Returns the {@link PublishedInfo} contained in this page
     * 
     *
     */
    public T getPublishedInfo() {
        return (T) myModel.getObject();
    }

    /**
     * By default brings back the user to LayerPage, subclasses can override this behavior
     */
    protected void onSuccessfulSave() {
        doReturn();
    }

    /**
     * By default brings back the user to LayerPage, subclasses can override this behavior
     */
    protected void onCancel() {
        doReturn();
    }

    /**
     * Allows collaborating pages to update the published info object
     * 
     * @param info
     */
    public void updatePublishedInfo(T info) {
        myModel.setObject(info);
    }

    protected abstract class ListEditTabPanel extends PublishedEditTabPanel<T> {

        private static final long serialVersionUID = -7279044666531992361L;

        public ListEditTabPanel(String id) {
            super(id, myModel);

            ListView<?> list = createList("theList");

            // do this or die on validation (the form element contents will
            // reset, the edit will be lost)
            list.setReuseItems(true);
            add(list);
        }

        protected abstract ListView<?> createList(String id);

    }

    protected class PublishingEditTabPanel extends ListEditTabPanel {
        private static final long serialVersionUID = -6575960326680386479L;

        public PublishingEditTabPanel(String id) {
            super(id);
        }

        @Override
        public ListView<PublishedConfigurationPanelInfo<T>> createList(String id) {
            List<PublishedConfigurationPanelInfo<T>> pubPanels = filterPublishedPanels(
                    getGeoServerApplication().getBeansOfType(PublishedConfigurationPanelInfo.class));
            ListView<PublishedConfigurationPanelInfo<T>> pubPanelList = new ListView<PublishedConfigurationPanelInfo<T>>(
                    id, pubPanels) {
                private static final long serialVersionUID = 1L;

                @Override
                protected void populateItem(ListItem<PublishedConfigurationPanelInfo<T>> item) {
                    PublishedConfigurationPanelInfo<T> panelInfo = (PublishedConfigurationPanelInfo<T>) item
                            .getModelObject();
                    try {
                        PublishedConfigurationPanel<T> panel = panelInfo.getComponentClass()
                                .getConstructor(String.class, IModel.class).newInstance("content", myModel);
                        item.add((Component) panel);
                    } catch (Exception e) {
                        throw new WicketRuntimeException("Failed to add pluggable layer configuration panels", e);
                    }
                }
            };
            return pubPanelList;
        }

    }

    @Override
    protected ComponentAuthorizer getPageAuthorizer() {
        return ComponentAuthorizer.WORKSPACE_ADMIN;
    }

    public void setInputEnabled(boolean inputEnabled) {
        this.inputEnabled = inputEnabled;
        get("publishedinfo:save").setVisible(inputEnabled);
    }
}