    001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_testing/src/org/deegree/portal/portlet/modules/map/actions/portlets/FeatureInfoPortletPerform.java $
    002    /*----------------------------------------------------------------------------
    003     This file is part of deegree, http://deegree.org/
    004     Copyright (C) 2001-2009 by:
    005       Department of Geography, University of Bonn
    006     and
    007       lat/lon GmbH
    008
    009     This library is free software; you can redistribute it and/or modify it under
    010     the terms of the GNU Lesser General Public License as published by the Free
    011     Software Foundation; either version 2.1 of the License, or (at your option)
    012     any later version.
    013     This library is distributed in the hope that it will be useful, but WITHOUT
    014     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    015     FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
    016     details.
    017     You should have received a copy of the GNU Lesser General Public License
    018     along with this library; if not, write to the Free Software Foundation, Inc.,
    019     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    020
    021     Contact information:
    022
    023     lat/lon GmbH
    024     Aennchenstr. 19, 53177 Bonn
    025     Germany
    026     http://lat-lon.de/
    027
    028     Department of Geography, University of Bonn
    029     Prof. Dr. Klaus Greve
    030     Postfach 1147, 53001 Bonn
    031     Germany
    032     http://www.geographie.uni-bonn.de/deegree/
    033
    034     e-mail: info@deegree.org
    035    ----------------------------------------------------------------------------*/
    036    package org.deegree.portal.portlet.modules.map.actions.portlets;
    037
    038    import java.awt.Color;
    039    import java.io.File;
    040    import java.io.StringWriter;
    041    import java.net.URL;
    042    import java.util.ArrayList;
    043    import java.util.Arrays;
    044    import java.util.HashMap;
    045    import java.util.List;
    046    import java.util.Map;
    047
    048    import javax.servlet.ServletContext;
    049    import javax.servlet.http.HttpServletRequest;
    050    import javax.xml.transform.Source;
    051    import javax.xml.transform.dom.DOMSource;
    052    import javax.xml.transform.stream.StreamResult;
    053    import javax.xml.transform.stream.StreamSource;
    054
    055    import org.apache.jetspeed.portal.Portlet;
    056    import org.deegree.framework.log.ILogger;
    057    import org.deegree.framework.log.LoggerFactory;
    058    import org.deegree.framework.util.StringTools;
    059    import org.deegree.framework.xml.XMLFragment;
    060    import org.deegree.framework.xml.XSLTDocument;
    061    import org.deegree.model.spatialschema.Envelope;
    062    import org.deegree.model.spatialschema.GeometryFactory;
    063    import org.deegree.model.spatialschema.Point;
    064    import org.deegree.ogcwebservices.OGCWebServiceException;
    065    import org.deegree.ogcwebservices.OWSUtils;
    066    import org.deegree.ogcwebservices.wms.operation.GetFeatureInfo;
    067    import org.deegree.ogcwebservices.wms.operation.GetMap;
    068    import org.deegree.portal.PortalException;
    069    import org.deegree.portal.context.Layer;
    070    import org.deegree.portal.context.LayerList;
    071    import org.deegree.portal.context.ViewContext;
    072    import org.deegree.portal.portlet.modules.actions.IGeoPortalPortletPerform;
    073
    074    /**
    075     *
    076     *
    077     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
    078     * @author last edited by: $Author: mschneider $
    079     *
    080     * @version $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
    081     */
    082    public class FeatureInfoPortletPerform extends IGeoPortalPortletPerform {
    083
    084        private static final ILogger LOG = LoggerFactory.getLogger( FeatureInfoPortletPerform.class );
    085
    086        protected static String PARAM_FILAYERS = "FILAYERS";
    087
    088        protected static String PARAM_LAYERS = "LAYERS";
    089
    090        protected static String PARAM_X = "X";
    091
    092        protected static String PARAM_Y = "Y";
    093
    094        protected static String PARAM_FEATURECOUNT = "FEATURECOUNT";
    095
    096        private static String SESSION_INITPARAM = "FIINITPARAM";
    097
    098        protected static String INIT_PATHTOXSLT = "pathToXSLT";
    099
    100        private Map<?, ?> initParams = null;
    101
    102        /**
    103         * @param request
    104         * @param portlet
    105         * @param sc
    106         *            will be needed to evaluate the absolut path of the transform script(s)
    107         */
    108        public FeatureInfoPortletPerform( HttpServletRequest request, Portlet portlet, ServletContext sc ) {
    109            super( request, portlet, sc );
    110            initParams = (Map<?,?>) request.getSession().getAttribute( SESSION_INITPARAM );
    111        }
    112
    113        /**
    114         * initializes the portlet by putting the init parameters to the users session. Even if this is
    115         * not absolutly neccessary it simplifies a few things ...
    116         *
    117         */
    118        public void init() {
    119            if ( request.getSession().getAttribute( SESSION_INITPARAM ) == null ) {
    120                Map<?, ?> map = portlet.getPortletConfig().getInitParameters();
    121                request.getSession().setAttribute( SESSION_INITPARAM, map );
    122            }
    123        }
    124
    125        /**
    126         * performs a GetFeatureInfo/GetFeature/DescribeCoverage request depending on the layer typs the
    127         * request targets. If defiend the result will be transformed by a XSLT script to get a human
    128         * readable out put.
    129         *
    130         * @throws PortalException
    131         * @throws OGCWebServiceException
    132         */
    133        protected void doGetFeatureInfo()
    134                                throws PortalException, OGCWebServiceException {
    135
    136            StringBuffer sb = new StringBuffer( 20000 );
    137
    138            // a get feature info request only can be performed if a viewcontext
    139            // has been initialized before by the MapWindowPortletAction
    140            ViewContext vc = getCurrentViewContext( (String) initParams.get( INIT_MAPPORTLETID ) );
    141            if ( vc == null ) {
    142                throw new PortalException( "no valid view context available through users session" );
    143            }
    144
    145            if ( parameter.get( "FILAYERS_ALL" ) == null ) {
    146
    147                String tmp = parameter.get( PARAM_FILAYERS );
    148                if ( tmp == null || tmp.length() == 0 ) {
    149                    throw new PortalException( "at least one layer/featuretype/coverage must be set" );
    150                }
    151
    152                String[] layers = StringTools.toArray( tmp, ",", true );
    153
    154                setCurrentFILayer( layers );
    155
    156                // synchronize list of visible layer and BBOX with the users view context
    157                // because maybe the user had changed the visible layers before performing
    158                // a GetFeatureInfo request
    159                updateContext();
    160
    161                Layer layer = null;
    162                Layer former = null;
    163                List<Layer> layerList = new ArrayList<Layer>( 50 );
    164
    165                // performe a feature info request every time the hosting
    166                // server changes. This is required because maybe a user has
    167                // selected two or more layers hosted by different OWS
    168                for ( int i = 0; i < layers.length; i++ ) {
    169                    former = layer;
    170                    layer = vc.getLayerList().getLayer( layers[i], null );
    171                    if ( i > 0 ) {
    172                        if ( layer.getServer().equals( former.getServer() ) ) {
    173                            layerList.add( layer );
    174                        } else {
    175                            sb.append( perform( layerList, vc ) );
    176                            layerList.clear();
    177                            layerList.add( layer );
    178                        }
    179                    } else {
    180                        layerList.add( layer );
    181                    }
    182                }
    183                sb.append( perform( layerList, vc ) );
    184            } else {
    185                handleFeatureInfoForAllLayers( vc, sb );
    186            }
    187
    188            vc.getGeneral().getExtension().setMode( "FEATUREINFO" );
    189
    190            setCurrentMapContext( vc, (String) initParams.get( INIT_MAPPORTLETID ) );
    191
    192            // the result will be available through the forwarded request as well as
    193            // through the users session (the portal frontend deciceds what behavior
    194            // it should have)
    195            request.setAttribute( "HTML", sb.toString() );
    196            request.getSession().setAttribute( "HTML", sb.toString() );
    197
    198        }
    199
    200        /**
    201         *
    202         * @param vc
    203         * @param sb
    204         * @throws OGCWebServiceException
    205         * @throws PortalException
    206         */
    207        private void handleFeatureInfoForAllLayers( ViewContext vc, StringBuffer sb )
    208                                throws OGCWebServiceException, PortalException {
    209            String[] layers = findCurrentFILayers();
    210            // synchronize list of visible layer and BBOX with the users view context
    211            // because maybe the user had changed the visible layers before performing
    212            // a GetFeatureInfo request
    213            updateContext();
    214
    215            Layer layer = null;
    216            Layer former = null;
    217            List<Layer> layerList = new ArrayList<Layer>( 50 );
    218
    219            // performe a feature info request every time the hosting
    220            // server changes. This is required because maybe a user has
    221            // selected two or more layers hosted by different OWS
    222            for ( int i = 0; i < layers.length; i++ ) {
    223                former = layer;
    224                layer = vc.getLayerList().getLayer( layers[i], null );
    225                if ( i > 0 ) {
    226                    if ( layer.getServer().equals( former.getServer() ) ) {
    227                        layerList.add( layer );
    228                    } else {
    229                        String result = perform( layerList, vc );
    230                        if ( result != null ) {
    231                            sb.append( perform( layerList, vc ) );
    232                        }
    233                        layerList.clear();
    234                        layerList.add( layer );
    235                    }
    236                } else {
    237                    layerList.add( layer );
    238                }
    239            }
    240            String result = perform( layerList, vc );
    241            if ( result != null ) {
    242                sb.append( result );
    243            }
    244        }
    245
    246        /**
    247         * sets the name of the the layers that are activated for feature info requests in the uses WMC
    248         *
    249         * @param fiLayer
    250         */
    251        void setCurrentFILayer( String[] fiLayer ) {
    252
    253            List<String> list = Arrays.asList( fiLayer );
    254            list = new ArrayList<String>( list );
    255
    256            ViewContext vc = getCurrentViewContext( (String) initParams.get( INIT_MAPPORTLETID ) );
    257            LayerList layerList = vc.getLayerList();
    258            Layer[] layers = layerList.getLayers();
    259            for ( int i = 0; i < layers.length; i++ ) {
    260                if ( list.contains( layers[i].getName() ) ) {
    261                    layers[i].getExtension().setSelectedForQuery( true );
    262                } else {
    263                    layers[i].getExtension().setSelectedForQuery( false );
    264                }
    265            }
    266
    267        }
    268
    269        /**
    270         * distributes the performance of the feature info requests depending on the requested service
    271         * to a specialized method
    272         *
    273         * @param layerList
    274         *            list of context layers provided by the same OWS
    275         * @param vc
    276         * @return formated feature info result
    277         * @throws OGCWebServiceException
    278         * @throws PortalException
    279         */
    280        protected String perform( List<Layer> layerList, ViewContext vc )
    281                                throws OGCWebServiceException, PortalException {
    282
    283            if ( layerList.size() > 0 ) {
    284                Layer layer = layerList.get( 0 );
    285                if ( layer.getServer().getService().indexOf( "WMS" ) > -1 ) {
    286                    return performWMS( layerList, vc );
    287                } else if ( layer.getServer().getService().indexOf( "WFS" ) > -1 ) {
    288                    throw new PortalException( "WFS is not supported as feature info target yet!" );
    289                } else if ( layer.getServer().getService().indexOf( "WCS" ) > -1 ) {
    290                    throw new PortalException( "WCS is not supported as feature info target yet!" );
    291                } else {
    292                    throw new PortalException( "not supported service: " + layer.getServer().getService()
    293                                               + " as feature info target!" );
    294                }
    295            }
    296            return "";
    297
    298        }
    299
    300        /**
    301         * performes a GetFeatureInfo request on a WMS and transforms the result using a XSLT script
    302         * defined in the portlets init-parameters
    303         *
    304         * @param layerList
    305         *            list of context layers provided by the same WMS
    306         * @param vc
    307         * @return formated get feature info result
    308         */
    309        private String performWMS( List<Layer> layerList, ViewContext vc )
    310                                throws OGCWebServiceException, PortalException {
    311
    312            GetFeatureInfo gfi = createGetFeatureInfoRequest( layerList, vc );
    313            Layer layer = layerList.get( 0 );
    314
    315            URL url = OWSUtils.getHTTPGetOperationURL( layer.getServer().getCapabilities(), GetFeatureInfo.class );
    316            String href = OWSUtils.validateHTTPGetBaseURL( url.toExternalForm() );
    317            StringBuffer sb = new StringBuffer( 1000 );
    318            sb.append( href ).append( gfi.getRequestParameter() );
    319            // If a user is registered to the portal use his name and password to
    320            // perform GetFeatureInfo request because maybe the connected server
    321            // is hidden behind a owsProxy
    322            // TODO
    323            // read informations too which server user name and password shall be send
    324            // TODO
    325            // replace sending user name and password by a sessionID
    326            if ( !"anon".equals( request.getAttribute( "$U$" ) ) ) {
    327                sb.append( "&user=" ).append( request.getAttribute( "$U$" ) );
    328                request.removeAttribute( "$U$" );
    329            }
    330            if ( request.getAttribute( "$P$" ) != null ) {
    331                sb.append( "&password=" ).append( request.getAttribute( "$P$" ) );
    332                request.removeAttribute( "$P$" );
    333            }
    334            LOG.logDebug( "info request: ", sb );
    335
    336            XMLFragment frag = null;
    337            try {
    338                frag = new XMLFragment( new URL( sb.toString() ) );
    339                if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
    340                    LOG.logDebug( "GetFeatureInfo result: \n", frag.getAsPrettyString() );
    341                }
    342            } catch ( Exception e ) {
    343                e.printStackTrace();
    344                throw new OGCWebServiceException( "could not perform GetFeatureInfo request " + e.getMessage() );
    345            }
    346
    347            LOG.logDebug( "get xslt for:", href );
    348            String path = getPathToXSLTScript( layer, href );
    349            LOG.logDebug( "selected path to xslt: ", path );
    350
    351            path = sc.getRealPath( "/WEB-INF/" + path );
    352
    353            Source xmlSource = new DOMSource( frag.getRootElement() );
    354
    355            Source xslSource = new StreamSource( new File( path ) );
    356            StringWriter sw = new StringWriter( 20000 );
    357
    358            Map<String, String> param = new HashMap<String, String>();
    359            param.put( "SERVER", layer.getServer().getTitle() );
    360            String s = null;
    361            try {
    362                XSLTDocument.transform( xmlSource, xslSource, new StreamResult( sw ), null, param );
    363                s = sw.getBuffer().toString();
    364                sw.close();
    365            } catch ( Exception e ) {
    366                throw new PortalException( "could not transform GetFeatureInfo result of request: " + sb + " - "
    367                                           + StringTools.stackTraceToString( e ) );
    368            }
    369
    370            return s;
    371
    372        }
    373
    374        /**
    375         * returns the path to the xslt script to be used for transforming info data
    376         *
    377         * @param layer
    378         * @param server
    379         * @return the path to the xslt script to be used for transforming info data
    380         * @throws PortalException
    381         */
    382        private String getPathToXSLTScript( Layer layer, String server )
    383                                throws PortalException {
    384            StringBuffer sbb = new StringBuffer( 1000 );
    385            sbb.append( INIT_PATHTOXSLT ).append( ';' ).append( server ).append( ';' );
    386            sbb.append( layer.getServer().getService() );
    387            String path = (String) initParams.get( sbb.toString() );
    388            if ( path == null ) {
    389                sbb.delete( 0, sbb.length() );
    390                sbb.append( INIT_PATHTOXSLT ).append( ';' );
    391                sbb.append( layer.getServer().getService() );
    392                path = (String) initParams.get( sbb.toString() );
    393            }
    394            if ( path == null ) {
    395                path = (String) initParams.get( INIT_PATHTOXSLT );
    396            }
    397            if ( path == null ) {
    398                // for being compliant with an error in init-param that
    399                // has been used in several instances
    400                sbb = new StringBuffer( 1000 );
    401                sbb.append( "pathToXLST" ).append( ';' ).append( server ).append( ';' );
    402                sbb.append( layer.getServer().getService() );
    403                path = (String) initParams.get( sbb.toString() );
    404                if ( path == null ) {
    405                    sbb.delete( 0, sbb.length() );
    406                    sbb.append( "pathToXLST" ).append( ';' );
    407                    sbb.append( layer.getServer().getService() );
    408                    path = (String) initParams.get( sbb.toString() );
    409                }
    410                if ( path == null ) {
    411                    path = (String) initParams.get( "pathToXLST" );
    412                }
    413            }
    414
    415            if ( path == null ) {
    416                LOG.logDebug( "initParams: ", initParams );
    417                throw new PortalException( "no XSLT script defined for processing GetFeatureInfo " + "response of:"
    418                                           + INIT_PATHTOXSLT + ';' + server + ';' + layer.getServer().getService() );
    419            }
    420
    421            return path;
    422        }
    423
    424        /**
    425         * creates a GetFeatureInfo request from the requested target layers depending on the current
    426         * view context
    427         *
    428         * @param layerList
    429         * @param vc
    430         * @return GetFeatureInfo request
    431         */
    432        private GetFeatureInfo createGetFeatureInfoRequest( List<Layer> layerList, ViewContext vc ) {
    433
    434            String[] fiLayers = new String[layerList.size()];
    435            GetMap.Layer[] gmLayers = new GetMap.Layer[layerList.size()];
    436            for ( int i = 0; i < gmLayers.length; i++ ) {
    437                Layer layer = layerList.get( i );
    438                fiLayers[i] = layer.getName();
    439                gmLayers[i] = new GetMap.Layer( layer.getName(), layer.getStyleList().getCurrentStyle().getName() );
    440            }
    441
    442            Layer layer = layerList.get( 0 );
    443            Point[] pt = vc.getGeneral().getBoundingBox();
    444            String srs = pt[0].getCoordinateSystem().getIdentifier();
    445            int width = vc.getGeneral().getWindow().width;
    446            int height = vc.getGeneral().getWindow().height;
    447            Envelope bbox = GeometryFactory.createEnvelope( pt[0].getX(), pt[0].getY(), pt[1].getX(), pt[1].getY(),
    448                                                            pt[0].getCoordinateSystem() );
    449
    450            // create GetMap request being mandatory part of the GetFeatureInfo request
    451            GetMap gm = GetMap.create( layer.getServer().getVersion(), "id", gmLayers, null, null,
    452                                       layer.getFormatList().getCurrentFormat().getName(), width, height, srs, bbox, false,
    453                                       Color.WHITE, null, null, null, null, null );
    454
    455            int x = Integer.parseInt( parameter.get( PARAM_X ) );
    456            int y = Integer.parseInt( parameter.get( PARAM_Y ) );
    457            java.awt.Point point = new java.awt.Point( x, y );
    458            int featureCount = Integer.parseInt( parameter.get( PARAM_FEATURECOUNT ) );
    459            GetFeatureInfo gfi = GetFeatureInfo.create( layer.getServer().getVersion(), "id", fiLayers, gm,
    460                                                        "application/vnd.ogc.gml", featureCount, point, null, null, null );
    461
    462            return gfi;
    463        }
    464
    465        /**
    466         * sets the name of the the layers that are activated for feature info requests in the uses WMC
    467         *
    468         * @return layers involved in feature info
    469         */
    470        String[] findCurrentFILayers() {
    471
    472            List<String> list = new ArrayList<String>();
    473
    474            ViewContext vc = getCurrentViewContext( (String) initParams.get( INIT_MAPPORTLETID ) );
    475
    476            LayerList layerList = vc.getLayerList();
    477            Layer[] layers = layerList.getLayers();
    478            for ( int i = 0; i < layers.length; i++ ) {
    479
    480                if ( !layers[i].isHidden() && layers[i].isQueryable() ) {
    481                    layers[i].getExtension().setSelectedForQuery( true );
    482                    list.add( layers[i].getName() );
    483                }
    484            }
    485            return list.toArray( new String[list.size()] );
    486        }
    487
    488    }