Package org.geoserver.web.data.layer

Source Code of org.geoserver.web.data.layer.SQLViewAbstractPage

/* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.web.data.layer;

import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Level;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import org.apache.wicket.Component;
import org.apache.wicket.PageParameters;
import org.apache.wicket.ajax.AjaxRequestTarget;
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.IChoiceRenderer;
import org.apache.wicket.markup.html.form.SubmitLink;
import org.apache.wicket.markup.html.form.TextArea;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.panel.Fragment;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.validation.IValidatable;
import org.apache.wicket.validation.validator.AbstractValidator;
import org.geoserver.catalog.DataStoreInfo;
import org.geoserver.catalog.FeatureTypeInfo;
import org.geoserver.web.GeoServerSecuredPage;
import org.geoserver.web.data.store.StorePage;
import org.geoserver.web.wicket.GeoServerAjaxFormLink;
import org.geoserver.web.wicket.GeoServerTablePanel;
import org.geoserver.web.wicket.ParamResourceModel;
import org.geoserver.web.wicket.GeoServerDataProvider.Property;
import org.geotools.data.DataAccess;
import org.geotools.data.Query;
import org.geotools.data.Transaction;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.jdbc.JDBCDataStore;
import org.geotools.jdbc.VirtualTable;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.GeometryDescriptor;
import org.opengis.referencing.crs.CoordinateReferenceSystem;

import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryCollection;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.MultiLineString;
import com.vividsolutions.jts.geom.MultiPoint;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;

/**
* Base page for SQL view creation/editing
*
* @author Andrea Aime - OpenGeo
*/
@SuppressWarnings("serial")
public abstract class SQLViewAbstractPage extends GeoServerSecuredPage {

    public static final String DATASTORE = "storeName";

    public static final String WORKSPACE = "wsName";

    String storeId;
   
    String typeInfoId;

    String sql;

    String name;

    boolean newView;

    SQLViewAttributeProvider attProvider;

    private TextArea sqlEditor;

    private GeoServerTablePanel<SQLViewAttribute> attributes;
   
    private GeoServerTablePanel<Parameter> parameters;

    private SQLViewParamProvider paramProvider;
   
    boolean guessGeometrySrid = false;

    private CheckBox guessCheckbox;

    private static final List GEOMETRY_TYPES = Arrays.asList(Geometry.class,
            GeometryCollection.class, Point.class, MultiPoint.class, LineString.class,
            MultiLineString.class, Polygon.class, MultiPolygon.class);

    public SQLViewAbstractPage(PageParameters params) throws IOException {
        this(params.getString(WORKSPACE), params.getString(DATASTORE), null, null);
    }

    @SuppressWarnings("deprecation")
    public SQLViewAbstractPage(String workspaceName, String storeName, String typeName, VirtualTable virtualTable)
            throws IOException {
        storeId = getCatalog().getStoreByName(workspaceName, storeName, DataStoreInfo.class)
                .getId();
       
        // build the form and the text area
        Form form = new Form("form", new CompoundPropertyModel(this));
        add(form);
        final TextField nameField = new TextField("name");
        nameField.setRequired(true);
        nameField.add(new ViewNameValidator());
        form.add(nameField);
        sqlEditor = new TextArea("sql");
        form.add(sqlEditor);
       
        // the parameters and attributes provider
        attProvider = new SQLViewAttributeProvider();
        paramProvider = new SQLViewParamProvider();

        // setting up the providers
        if (typeName != null) {
            newView = false;

            // grab the virtual table
            DataStoreInfo store = getCatalog().getStore(storeId, DataStoreInfo.class);
            FeatureTypeInfo fti = getCatalog().getResourceByStore(store, typeName, FeatureTypeInfo.class);
            // the type can be still not saved
            if(fti != null) {
                typeInfoId = fti.getId();
            }
            if(virtualTable == null) {
                throw new IllegalArgumentException("The specified feature type does not have a sql view attached to it");
            }

            // get the store
            DataAccess da = store.getDataStore(null);
            if (!(da instanceof JDBCDataStore)) {
                error("Cannot create a SQL view if the store is not database based");
                setResponsePage(StorePage.class);
                return;
            }

            name = virtualTable.getName();
            sql = virtualTable.getSql();

            paramProvider.init(virtualTable);
            try {
                SimpleFeatureType ft = testViewDefinition(virtualTable, false);
                attProvider.setFeatureType(ft, virtualTable);
            } catch(Exception e) {
                LOGGER.log(Level.SEVERE, "Failed to build feature type for the sql view", e);
            }
        } else {
            newView = true;
        }
       
        // the links to refresh, add and remove a parameter
        form.add(new GeoServerAjaxFormLink("guessParams") {
           
            @Override
            protected void onClick(AjaxRequestTarget target, Form form) {
                sqlEditor.processInput();
                parameters.processInputs();
                if (sql != null && !"".equals(sql.trim())) {
                    paramProvider.refreshFromSql(sql);
                    target.addComponent(parameters);
                }
            }
        });
        form.add(new GeoServerAjaxFormLink("addNewParam") {
           
            @Override
            protected void onClick(AjaxRequestTarget target, Form form) {
                paramProvider.addParameter();
                target.addComponent(parameters);
            }
        });
        form.add(new GeoServerAjaxFormLink("removeParam") {
           
            @Override
            protected void onClick(AjaxRequestTarget target, Form form) {
                paramProvider.removeAll(parameters.getSelection());
                parameters.clearSelection();
                target.addComponent(parameters);
            }
        });
       
        // the parameters table
        parameters = new GeoServerTablePanel<Parameter>("parameters", paramProvider, true) {

            @Override
            protected Component getComponentForProperty(String id, IModel itemModel,
                    Property<Parameter> property) {
                Fragment f = new Fragment(id, "text", SQLViewAbstractPage.this);
                TextField text = new TextField("text", property.getModel(itemModel));
                text.setLabel(new ParamResourceModel("th." + property.getName(), SQLViewAbstractPage.this));
                if(property == SQLViewParamProvider.NAME) {
                    text.setRequired(true);
                } else if(property == SQLViewParamProvider.REGEXP) {
                    text.add(new RegexpValidator());
                }
                f.add(text);
                return f;
            }
           
        };
        parameters.setFilterVisible(false);
        parameters.setSortable(false);
        parameters.getTopPager().setVisible(false);
        parameters.getBottomPager().setVisible(false);
        parameters.setOutputMarkupId(true);
        form.add(parameters);
       
        // the "refresh attributes" link
        form.add(refreshLink());
        form.add(guessCheckbox = new CheckBox("guessGeometrySrid", new PropertyModel(this, "guessGeometrySrid")));

        // the editable attribute table
        attributes = new GeoServerTablePanel<SQLViewAttribute>("attributes", attProvider) {

            @Override
            protected Component getComponentForProperty(String id, IModel itemModel,
                    Property<SQLViewAttribute> property) {
                SQLViewAttribute att = (SQLViewAttribute) itemModel.getObject();
                boolean isGeometry = att.getType() != null
                        && Geometry.class.isAssignableFrom(att.getType());
                if (property == SQLViewAttributeProvider.PK) {
                    // editor for pk status
                    Fragment f = new Fragment(id, "checkbox", SQLViewAbstractPage.this);
                    f.add(new CheckBox("identifier", new PropertyModel(itemModel, "pk")));
                    return f;
                } else if (property == SQLViewAttributeProvider.TYPE && isGeometry) {
                    Fragment f = new Fragment(id, "geometry", SQLViewAbstractPage.this);
                    f.add(new DropDownChoice("geometry", new PropertyModel(itemModel, "type"),
                            GEOMETRY_TYPES, new GeometryTypeRenderer()));
                    return f;
                } else if(property == SQLViewAttributeProvider.SRID && isGeometry) {
                    Fragment f = new Fragment(id, "text", SQLViewAbstractPage.this);
                    f.add(new TextField("text", new PropertyModel(itemModel, "srid")));
                    return f;
                }
                return null;
            }
        };
        // just a plain table, no filters, no paging,
        attributes.setFilterVisible(false);
        attributes.setSortable(false);
        attributes.setPageable(false);
        attributes.setOutputMarkupId(true);
        form.add(attributes);

        // save and cancel at the bottom of the page
        form.add(new SubmitLink("save") {
            @Override
            public void onSubmit() {
                onSave();
            }
        });
        form.add(new Link("cancel") {

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

    private GeoServerAjaxFormLink refreshLink() {
        return new GeoServerAjaxFormLink("refresh") {

            @Override
            protected void onClick(AjaxRequestTarget target, Form form) {
                sqlEditor.processInput();
                parameters.processInputs();
                guessCheckbox.processInput();
                if (sql != null && !"".equals(sql.trim())) {
                    SimpleFeatureType newSchema = null;
                    try {
                        newSchema = testViewDefinition(guessGeometrySrid);

                        if (newSchema != null) {
                            attProvider.setFeatureType(newSchema, null);
                            target.addComponent(attributes);
                        }
                    } catch (IOException e) {
                        LOGGER.log(Level.INFO, "Error testing SQL query", e);
                        error(getFirstErrorMessage(e));
                    }
                }
            }
        };
    }

    /**
     * Checks the view definition works as expected and returns the feature type guessed solely by
     * looking at the sql and the first row of its output
     *
     * @param newSchema
     * @return
     * @throws IOException
     */
    protected SimpleFeatureType testViewDefinition(boolean guessGeometrySrid) throws IOException {
        // check out if the view can be used
        JDBCDataStore ds = (JDBCDataStore) getCatalog().getDataStore(storeId).getDataStore(null);
        String vtName = null;
        try {
            // use a highly random name
            do {
                vtName = UUID.randomUUID().toString();
            } while (Arrays.asList(ds.getTypeNames()).contains(vtName));

            // try adding the vt and see if that works
            VirtualTable vt = new VirtualTable(vtName, sql);
            paramProvider.updateVirtualTable(vt);
            ds.addVirtualTable(vt);
            return guessFeatureType(ds, vt.getName(), guessGeometrySrid);
        } finally {
            if(vtName != null) {
                ds.removeVirtualTable(vtName);
            }
           
        }
    }
   
    protected SimpleFeatureType getFeatureType(VirtualTable vt) throws IOException {
     // check out if the view can be used
        JDBCDataStore ds = (JDBCDataStore) getCatalog().getDataStore(storeId).getDataStore(null);
        String vtName = null;
        try {
            // use a highly random name
            do {
                vtName = UUID.randomUUID().toString();
            } while (Arrays.asList(ds.getTypeNames()).contains(vtName));

            // try adding the vt and see if that works
            ds.addVirtualTable(new VirtualTable(vtName, vt));
            return ds.getSchema(vtName);
        } finally {
            if(vtName != null) {
                ds.removeVirtualTable(vtName);
            }
           
        }
    }
   
    /**
     * Checks the view definition works as expected and returns the feature type guessed solely by
     * looking at the sql and the first row of its output
     *
     * @param newSchema
     * @return
     * @throws IOException
     */
    protected SimpleFeatureType testViewDefinition(VirtualTable virtualTable, boolean guessGeometrySrid) throws IOException {
        // check out if the view can be used
        JDBCDataStore ds = (JDBCDataStore) getCatalog().getDataStore(storeId).getDataStore(null);
        String vtName = null;
        try {
            // use a highly random name
            do {
                vtName = UUID.randomUUID().toString();
            } while (Arrays.asList(ds.getTypeNames()).contains(vtName));

            // try adding the vt and see if that works
            VirtualTable vt = new VirtualTable(vtName, virtualTable);
            // hide the primary key definitions or we'll loose some columns
            vt.setPrimaryKeyColumns(Collections.EMPTY_LIST);
            ds.addVirtualTable(vt);
            return guessFeatureType(ds, vt.getName(), guessGeometrySrid);
        } finally {
            if(vtName != null) {
                ds.removeVirtualTable(name);
            }
           
        }
    }

    /**
     * Grabs the feature type from the store, but takes a peek at figuring out the geoemtry type and
     * srids
     *
     * @param schema
     * @return
     */
    SimpleFeatureType guessFeatureType(JDBCDataStore store, String vtName, boolean guessGeometrySrid) throws IOException {
        SimpleFeatureType base = store.getSchema(vtName);
        List<String> geometries = new ArrayList<String>();
        for (AttributeDescriptor ad : base.getAttributeDescriptors()) {
            if (ad instanceof GeometryDescriptor) {
                geometries.add(ad.getLocalName());
            }
        }

        // no geometries? Or, shall we not try to guess the geometries type and srid?
        if (geometries.size() == 0 || !guessGeometrySrid) {
            return base;
        }

        // build a query to fetch the first rwo, we'll inspect the resulting
        // geometries
        Query q = new Query(vtName);
        q.setPropertyNames(geometries);
        q.setMaxFeatures(1);
        SimpleFeatureIterator it = null;
        SimpleFeature f = null;
        try {
            it = store.getFeatureSource(vtName).getFeatures(q).features();
            if (it.hasNext()) {
                f = it.next();
            }
        } finally {
            if (it != null) {
                it.close();
            }
        }

        // did we get more information?
        if (f == null) {
            return base;
        }

        // if so, try to build an override feature type
        Connection cx = null;
        try {
            store.getConnection(Transaction.AUTO_COMMIT);
            SimpleFeatureTypeBuilder tb = new SimpleFeatureTypeBuilder();
            tb.setName(base.getName());
            for (AttributeDescriptor ad : base.getAttributeDescriptors()) {
                if (ad instanceof GeometryDescriptor) {
                    GeometryDescriptor gd = (GeometryDescriptor) ad;
                    Geometry g = (Geometry) f.getAttribute(ad.getLocalName());
                    if (g == null) {
                        // nothing new we can learn
                        tb.add(ad);
                    } else {
                        Class binding = g.getClass();
                        CoordinateReferenceSystem crs = null;
                        if (g.getSRID() > 0) {
                            // see if the dialect can handle this one
                            crs = store.getSQLDialect().createCRS(g.getSRID(), cx);
                            tb.userData(JDBCDataStore.JDBC_NATIVE_SRID, g.getSRID());
                        }
                        if (crs == null) {
                            crs = gd.getCoordinateReferenceSystem();
                        }
                        tb.add(ad.getLocalName(), binding, crs);
                    }

                } else {
                    tb.add(ad);
                }
            }
            return tb.buildFeatureType();
        } catch (SQLException e) {
            throw (IOException) new IOException(e.getMessage()).initCause(e);
        } finally {
            store.closeSafe(cx);
        }
    }

    protected VirtualTable buildVirtualTable() {
        VirtualTable vt = new VirtualTable(name, sql);
        attProvider.fillVirtualTable(vt);
        paramProvider.updateVirtualTable(vt);
        return vt;
    }

    /**
     * Data stores tend to return IOExceptions with no explanation, and the actual error coming from
     * the db is in the cause. This method extracts the first not null message in the cause chain
     *
     * @param t
     * @return
     */
    protected String getFirstErrorMessage(Throwable t) {
        Throwable original = t;
       
        while (!(t instanceof SQLException)) {
            t = t.getCause();
            if (t == null) {
                break;
            }
        }
       
        if(t == null) {
            return original.getMessage();
        } else {
            return t.getMessage();
        }
    }
   
    protected abstract void onSave();

    protected abstract void onCancel();
   
    /**
     * Displays the geometry type in the geom type drop down
     * @author Andrea Aime - OpenGeo
     */
    static class GeometryTypeRenderer implements IChoiceRenderer {

        public Object getDisplayValue(Object object) {
            return ((Class) object).getSimpleName();
        }

        public String getIdValue(Object object, int index) {
            return (String) getDisplayValue(object);
        }
       
    }
   
    /**
     * Validaes the regular expression syntax
     */
    static class RegexpValidator extends AbstractValidator {

        @Override
        protected void onValidate(IValidatable validatable) {
            String value = (String) validatable.getValue();
            if(value != null) {
                try {
                    Pattern.compile(value);
                } catch(PatternSyntaxException e) {
                    Map<String, String> map = new HashMap<String, String>();
                    map.put("regexp", value);
                    map.put("error", e.getMessage().replaceAll("\\^?", ""));
                    error(validatable, "invalidRegexp", map);
                }
            }
        }
       
    }

    /**
     * Checks the sql view name is unique
     */
    class ViewNameValidator extends AbstractValidator {
        @Override
        protected void onValidate(IValidatable validatable) {
            String vtName = (String) validatable.getValue();
           
            final DataStoreInfo store = getCatalog().getStore(storeId, DataStoreInfo.class);
            List<FeatureTypeInfo> ftis = getCatalog().getResourcesByStore(store, FeatureTypeInfo.class);
            for (FeatureTypeInfo curr : ftis) {
                VirtualTable currvt = curr.getMetadata().get(FeatureTypeInfo.JDBC_VIRTUAL_TABLE, VirtualTable.class);
                if(currvt != null) {
                    if(typeInfoId == null || !typeInfoId.equals(curr.getId())) {
                        if(currvt.getName().equals(vtName)) {
                            Map<String, String> map = new HashMap<String, String>();
                            map.put("name", vtName);
                            map.put("typeName", curr.getName());
                            error(validatable, "duplicateSqlViewName", map);
                            return;
                        }
                    }
                }
            }
        }
    }
   
}
TOP

Related Classes of org.geoserver.web.data.layer.SQLViewAbstractPage

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.