001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/portal/standard/context/control/AbstractContextListener.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.standard.context.control;
037    
038    import java.io.IOException;
039    import java.io.StringWriter;
040    import java.net.MalformedURLException;
041    import java.net.URL;
042    import java.util.ArrayList;
043    
044    import javax.servlet.http.HttpServletRequest;
045    import javax.servlet.http.HttpSession;
046    import javax.xml.transform.TransformerException;
047    
048    import org.apache.commons.httpclient.HttpClient;
049    import org.apache.commons.httpclient.methods.GetMethod;
050    import org.deegree.enterprise.WebUtils;
051    import org.deegree.enterprise.control.AbstractListener;
052    import org.deegree.enterprise.control.RPCMember;
053    import org.deegree.enterprise.control.RPCMethodCall;
054    import org.deegree.enterprise.control.RPCParameter;
055    import org.deegree.enterprise.control.RPCStruct;
056    import org.deegree.enterprise.control.RPCUtils;
057    import org.deegree.enterprise.control.RPCWebEvent;
058    import org.deegree.framework.log.ILogger;
059    import org.deegree.framework.log.LoggerFactory;
060    import org.deegree.framework.util.StringTools;
061    import org.deegree.framework.xml.NamespaceContext;
062    import org.deegree.framework.xml.XMLFragment;
063    import org.deegree.framework.xml.XMLParsingException;
064    import org.deegree.framework.xml.XMLTools;
065    import org.deegree.framework.xml.XSLTDocument;
066    import org.deegree.i18n.Messages;
067    import org.deegree.model.crs.CoordinateSystem;
068    import org.deegree.model.spatialschema.Envelope;
069    import org.deegree.model.spatialschema.GeometryFactory;
070    import org.deegree.model.spatialschema.Point;
071    import org.deegree.ogcbase.BaseURL;
072    import org.deegree.ogcbase.CommonNamespaces;
073    import org.deegree.ogcwebservices.OWSUtils;
074    import org.deegree.ogcwebservices.getcapabilities.OGCCapabilities;
075    import org.deegree.ogcwebservices.wms.capabilities.WMSCapabilitiesDocument;
076    import org.deegree.ogcwebservices.wms.capabilities.WMSCapabilitiesDocumentFactory;
077    import org.deegree.portal.Constants;
078    import org.deegree.portal.PortalException;
079    import org.deegree.portal.context.ContextException;
080    import org.deegree.portal.context.General;
081    import org.deegree.portal.context.GeneralExtension;
082    import org.deegree.portal.context.Layer;
083    import org.deegree.portal.context.LayerList;
084    import org.deegree.portal.context.Server;
085    import org.deegree.portal.context.ViewContext;
086    import org.xml.sax.SAXException;
087    
088    /**
089     * This exception shall be thrown when a session(ID) will be used that has been expired.
090     *
091     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth </a>
092     * @author last edited by: $Author: jmays $
093     *
094     * @version $Revision: 19116 $, $Date: 2009-08-14 11:48:50 +0200 (Fr, 14. Aug 2009) $
095     */
096    abstract public class AbstractContextListener extends AbstractListener {
097    
098        private static ILogger LOG = LoggerFactory.getLogger( AbstractContextListener.class );
099    
100        private static final NamespaceContext nsContext = CommonNamespaces.getNamespaceContext();
101    
102        /**
103         * gets the user name assigned to the passed session ID from an authentication service. If no user is assigned to
104         * the session ID <tt>null</tt> will be returned. If the session is closed or expired an exception will be thrown
105         *
106         * @param sessionId
107         * @return name of the user assigned to the passed session ID
108         * @throws XMLParsingException
109         * @throws SAXException
110         * @throws IOException
111         */
112        protected String getUserName( String sessionId )
113                                throws XMLParsingException, IOException, SAXException {
114    
115            HttpSession session = ( (HttpServletRequest) getRequest() ).getSession( true );
116            ViewContext vc = (ViewContext) session.getAttribute( Constants.CURRENTMAPCONTEXT );
117            if ( vc == null ) {
118                return null;
119            }
120            GeneralExtension ge = vc.getGeneral().getExtension();
121            String userName = null;
122            if ( sessionId != null && ge.getAuthentificationSettings() != null ) {
123                LOG.logDebug( "try getting user from WAS/sessionID" );
124                BaseURL baseUrl = ge.getAuthentificationSettings().getAuthentificationURL();
125                String url = OWSUtils.validateHTTPGetBaseURL( baseUrl.getOnlineResource().toExternalForm() );
126                StringBuffer sb = new StringBuffer( url );
127                sb.append( "request=DescribeUser&SESSIONID=" ).append( sessionId );
128    
129                XMLFragment xml = new XMLFragment();
130                xml.load( new URL( sb.toString() ) );
131    
132                userName = XMLTools.getRequiredNodeAsString( xml.getRootElement(), "/User/UserName", nsContext );
133            } else {
134                LOG.logDebug( "try getting user from getUserPrincipal()" );
135                if ( ( (HttpServletRequest) getRequest() ).getUserPrincipal() != null ) {
136                    userName = ( (HttpServletRequest) getRequest() ).getUserPrincipal().getName();
137                    if ( userName.indexOf( "\\" ) > 1 ) {
138                        String[] us = StringTools.toArray( userName, "\\", false );
139                        userName = us[us.length - 1];
140                    }
141                }
142            }
143            LOG.logDebug( "userName: " + userName );
144            return userName;
145        }
146    
147        /**
148         * gets the user password assigned to the passed session ID from a authentification service. If no user is assigned
149         * to the session ID <tt>null</tt> will be returned. If the session is closed or expired an exception will be
150         * thrown
151         *
152         * @param sessionId
153         * @return password of the user assigned to the passed session ID
154         */
155        protected String getUserPassword( String sessionId )
156                                throws XMLParsingException, IOException, SAXException, PortalException {
157    
158            HttpSession session = ( (HttpServletRequest) getRequest() ).getSession( true );
159            ViewContext vc = (ViewContext) session.getAttribute( Constants.CURRENTMAPCONTEXT );
160            if ( vc == null ) {
161                return null;
162            }
163            GeneralExtension ge = vc.getGeneral().getExtension();
164            String userPassword = null;
165            if ( sessionId != null && ge.getAuthentificationSettings() != null ) {
166                LOG.logDebug( "try getting user from WAS/sessionID" );
167                BaseURL baseUrl = ge.getAuthentificationSettings().getAuthentificationURL();
168                String url = OWSUtils.validateHTTPGetBaseURL( baseUrl.getOnlineResource().toExternalForm() );
169                StringBuffer sb = new StringBuffer( url );
170                sb.append( "request=DescribeUser&SESSIONID=" ).append( sessionId );
171    
172                XMLFragment xml = new XMLFragment();
173                xml.load( new URL( sb.toString() ) );
174    
175                LOG.logDebug( xml.getAsPrettyString() );
176                userPassword = XMLTools.getRequiredNodeAsString( xml.getRootElement(), "/User/Password", nsContext );
177            } else {
178                throw new PortalException( "The session ID is null and thus the user password can not be extracted" );
179            }
180            LOG.logDebug( "Password: " + userPassword );
181            return userPassword;
182        }
183    
184        /**
185         * reads the users session ID.<br>
186         * first the PRC will be parsed for a 'sessionID' element. If not present the sessionID will be read from the users
187         * session. If even the user's HTTP session does not contain a sessionID, it will be tried to get it from the WAS
188         * registered to the current context. If no WAS available <code>null</code> will be returned.
189         *
190         * @param struct
191         * @return the users session id
192         * @throws IOException
193         * @throws SAXException
194         * @throws XMLParsingException
195         */
196        protected String readSessionID( RPCStruct struct )
197                                throws XMLParsingException, SAXException, IOException {
198            String sid = RPCUtils.getRpcPropertyAsString( struct, "sessionID" );
199            if ( sid == null ) {
200                LOG.logDebug( "try getting sessionID from HTTP session" );
201                HttpSession session = ( (HttpServletRequest) getRequest() ).getSession();
202                sid = (String) session.getAttribute( "SESSIONID" );
203            }
204            if ( sid == null ) {
205                // try get SessionID from WAS if user name is available
206                // in this case it is assumed that a user's name can be determined
207                // evaluating the requests userPrincipal that will be available if
208                // the user has been logged in to the the server (or network)
209                String userName = getUserName( null );
210                if ( userName != null ) {
211                    LOG.logDebug( "try getting sessionID by authorizing current user: " + userName );
212                    HttpSession session = ( (HttpServletRequest) getRequest() ).getSession( true );
213                    ViewContext vc = (ViewContext) session.getAttribute( Constants.CURRENTMAPCONTEXT );
214                    GeneralExtension ge = vc.getGeneral().getExtension();
215                    if ( ge.getAuthentificationSettings() != null ) {
216                        BaseURL baseUrl = ge.getAuthentificationSettings().getAuthentificationURL();
217                        StringBuffer sb = new StringBuffer( 500 );
218                        String addr = baseUrl.getOnlineResource().toExternalForm();
219                        sb.append( OWSUtils.validateHTTPGetBaseURL( addr ) );
220                        sb.append( "SERVICE=WAS&VERSION=1.0.0&REQUEST=GetSession&" );
221                        sb.append( "AUTHMETHOD=urn:x-gdi-nrw:authnMethod:1.0:password&CREDENTIALS=" );
222                        sb.append( userName );
223                        LOG.logDebug( "authenticat user: ", sb.toString() );
224                        HttpClient client = new HttpClient();
225                        client = WebUtils.enableProxyUsage( client, baseUrl.getOnlineResource() );
226                        GetMethod meth = new GetMethod( sb.toString() );
227                        client.executeMethod( meth );
228                        sid = meth.getResponseBodyAsString();
229                        session.setAttribute( "SESSIONID", sid );
230                    }
231                }
232            }
233            LOG.logDebug( "sessionID: " + sid );
234            return sid;
235        }
236    
237        /**
238         * Convenience method to extract the boundig box from an rpc fragment.
239         *
240         * @param bboxStruct
241         *            the <code>RPCStruct</code> containing the bounding box. For example,
242         *            <code>&lt;member&gt;&lt;name&gt;boundingBox&lt;/name&gt;etc...</code>.
243         * @param crs
244         *            a coordinate system value, may be null.
245         * @return an envelope with the boundaries defined in the rpc structure
246         */
247        protected Envelope extractBBox( RPCStruct bboxStruct, CoordinateSystem crs ) {
248    
249            Double minx = (Double) bboxStruct.getMember( Constants.RPC_BBOXMINX ).getValue();
250            Double miny = (Double) bboxStruct.getMember( Constants.RPC_BBOXMINY ).getValue();
251            Double maxx = (Double) bboxStruct.getMember( Constants.RPC_BBOXMAXX ).getValue();
252            Double maxy = (Double) bboxStruct.getMember( Constants.RPC_BBOXMAXY ).getValue();
253    
254            Envelope bbox = GeometryFactory.createEnvelope( minx.doubleValue(), miny.doubleValue(), maxx.doubleValue(),
255                                                            maxy.doubleValue(), crs );
256            return bbox;
257        }
258    
259        /**
260         * This method is kept for downward compatibility of the API. Do not use it any more!
261         *
262         * @deprecated use extractBBox( RPCStruct, CoordinateSystem ) instead.
263         * @param bboxStruct
264         *            the <code>RPCStruct</code> containing the bounding box. For example,
265         *            <code>&lt;member&gt;&lt;name&gt;boundingBox&lt;/name&gt;etc...</code>.
266         * @return an envelope with the boundaries defined in the rpc structure
267         */
268        @Deprecated
269        protected Envelope extractBBox( RPCStruct bboxStruct ) {
270            return extractBBox( bboxStruct, null );
271        }
272    
273        /**
274         * changes the bounding box of a given view context
275         *
276         * @param vc
277         *            the view context to be changed
278         * @param bbox
279         *            the new bounding box
280         * @throws PortalException
281         */
282        public static final void changeBBox( ViewContext vc, Envelope bbox )
283                                throws PortalException {
284            General gen = vc.getGeneral();
285    
286            CoordinateSystem cs = gen.getBoundingBox()[0].getCoordinateSystem();
287            Point[] p = new Point[] { GeometryFactory.createPoint( bbox.getMin(), cs ),
288                                     GeometryFactory.createPoint( bbox.getMax(), cs ) };
289            try {
290                gen.setBoundingBox( p );
291            } catch ( ContextException e ) {
292                LOG.logError( e.getMessage(), e );
293                throw new PortalException( Messages.getMessage( "IGEO_STD_CNTXT_ERROR_SET_BBOX" ) );
294            }
295        }
296    
297        /**
298         * changes the layer list of the ViewContext vc according to the information contained in the rpcLayerList
299         *
300         * @param vc
301         *            The original ViewContext where the changes will be applied to
302         * @param rpcLayerList
303         *            the current layerlist
304         * @throws PortalException
305         */
306        protected void changeLayerList( ViewContext vc, RPCMember[] rpcLayerList )
307                                throws PortalException {
308            LayerList layerList = vc.getLayerList();
309            ArrayList<Layer> nLayers = new ArrayList<Layer>( rpcLayerList.length );
310    
311            // this is needed to keep layer order
312            // order is correct in rpc call JavaScript) but get lost in translation...
313            for ( int i = 0; i < rpcLayerList.length; i++ ) {
314                String[] v = StringTools.toArray( (String) rpcLayerList[i].getValue(), "|", false );
315                String n = rpcLayerList[i].getName();
316    
317                String title = n;
318                if ( v.length > 5 ) {
319                    // this check is necessary, in order to not break running iGeoPortal instances
320                    title = v[5];
321                }
322                boolean isQueryable = false;
323                if ( v.length > 6 ) {
324                    // JM: this check is necessary, in order to not break running iGeoPortal instances
325                    isQueryable = v[6].equalsIgnoreCase( "true" );
326                }
327    
328                boolean isVisible = Boolean.valueOf( v[0] ).booleanValue();
329                Layer l = layerList.getLayer( n, null );
330                if ( l != null ) {
331                    // needed to reconstruct new layer order
332                    // otherwise layer order is still from original context
333                    l.setHidden( !isVisible );
334                } else {
335    
336                    if ( layerList.getLayers().length == 0 ) {
337                        // FIXME is this Exception Correct
338                        throw new PortalException( Messages.getMessage( "IGEO_STD_CNTXT_ERROR_EMPTY_LAYERLIST" ) );
339                    }
340    
341                    Layer p = layerList.getLayers()[0];
342                    // a new layer must be created because it is not prsent
343                    // in the current context. This is the case if the client
344                    // has loaded an additional WMS
345                    String[] tmp = StringTools.toArray( v[2], " ", false );
346                    try {
347                        v[4] = OWSUtils.validateHTTPGetBaseURL( v[4] );
348                        WMSCapabilitiesDocument doc = WMSCapabilitiesDocumentFactory.getWMSCapabilitiesDocument( new URL(
349                                                                                                                          v[4]
350                                                                                                                                                  + "request=GetCapabilities&service=WMS" ) );
351                        OGCCapabilities capa = doc.parseCapabilities();
352                        Server server = new Server( v[3], tmp[1], tmp[0], new URL( v[4] ), capa );
353    //                    l = new Layer( server, n, title, "", p.getSrs(), null, null, p.getFormatList(), p.getStyleList(),
354    //                                   isQueryable, !isVisible, null );
355                        l = new Layer( server, n, title, "", p.getSrs(), p.getDataURL(), p.getMetadataURL(),
356                                       p.getFormatList(), p.getStyleList(), isQueryable, !isVisible, p.getExtension() );
357                    } catch ( Exception e ) {
358                        throw new PortalException( StringTools.stackTraceToString( e ) );
359                    }
360                }
361                nLayers.add( l );
362            }
363            try {
364                nLayers.trimToSize();
365                Layer[] ls = new Layer[nLayers.size()];
366                ls = nLayers.toArray( ls );
367                vc.setLayerList( new LayerList( ls ) );
368            } catch ( ContextException e ) {
369                throw new PortalException( Messages.getMessage( "IGEO_STD_CNTXT_ERROR_SET_LAYERLIST",
370                                                                StringTools.stackTraceToString( e.getStackTrace() ) ) );
371            }
372        }
373    
374        /**
375         * This function takes in a XmlFragment and transforms it to a html map context
376         *
377         * @param xml
378         *            xmlFragment to transform
379         * @param xsl
380         *            xsl file used to transform
381         * @return html representing the mapContext
382         * @throws TransformerException
383         * @throws SAXException
384         * @throws IOException
385         * @throws MalformedURLException
386         */
387        protected String transformToHtmlMapContext( XMLFragment xml, String xsl )
388                                throws TransformerException, MalformedURLException, IOException, SAXException {
389    
390            XSLTDocument xslt = new XSLTDocument();
391            StringWriter sw = new StringWriter( 30000 );
392    
393            xslt.load( new URL( xsl ) );
394            xml = xslt.transform( xml );
395            xml.write( sw );
396    
397            return sw.toString();
398        }
399    
400        /**
401         * Extracts the parameters from the method call element within the passed rpcEvent.
402         *
403         * @param rpcEvent
404         * @return Returns the parameters as array of <code>RPCParameter</code>.
405         * @throws PortalException
406         */
407        protected RPCParameter[] extractRPCParameters( RPCWebEvent rpcEvent )
408                                throws PortalException {
409            RPCParameter[] params;
410            try {
411                RPCMethodCall mc = rpcEvent.getRPCMethodCall();
412                params = mc.getParameters();
413            } catch ( Exception e ) {
414                throw new PortalException( Messages.getMessage( "IGEO_STD_CNTXT_ERROR_EXTRACT_PARAM_RPC", e.getMessage() ) );
415            }
416            return params;
417        }
418    
419        /**
420         * Extracts the <code>RPCStruct</code> from the indicated parameter in the params element of the passed
421         * <code>RPCWebEvent</code>.
422         *
423         * @param rpcEvent
424         *            The RPCWebEvent, that contains the RPCStruct to extract.
425         * @param index
426         *            The index of the parameter from which to extract the RPCStruct (starting with 0).
427         * @return Returns the <code>RPCStruct</code> from the indicated params element.
428         * @throws PortalException
429         */
430        protected RPCStruct extractRPCStruct( RPCWebEvent rpcEvent, int index )
431                                throws PortalException {
432            RPCStruct rpcStruct;
433            try {
434                RPCParameter[] params = extractRPCParameters( rpcEvent );
435                rpcStruct = (RPCStruct) params[index].getValue();
436            } catch ( Exception e ) {
437                throw new PortalException( Messages.getMessage( "IGEO_STD_CNTXT_ERROR_EXTRACT_STRUCT_RPC", e.getMessage() ) );
438            }
439            return rpcStruct;
440        }
441    
442    }