001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/portal/portlet/enterprise/WMCUpdateServlet.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.enterprise;
037    
038    import java.io.IOException;
039    import java.net.URL;
040    import java.util.List;
041    import java.util.Map;
042    
043    import javax.servlet.ServletException;
044    import javax.servlet.http.HttpServlet;
045    import javax.servlet.http.HttpServletRequest;
046    import javax.servlet.http.HttpServletResponse;
047    
048    import org.deegree.framework.log.ILogger;
049    import org.deegree.framework.log.LoggerFactory;
050    import org.deegree.framework.util.KVP2Map;
051    import org.deegree.framework.util.StringPair;
052    import org.deegree.framework.util.StringTools;
053    import org.deegree.model.crs.CoordinateSystem;
054    import org.deegree.model.spatialschema.Envelope;
055    import org.deegree.model.spatialschema.GeometryFactory;
056    import org.deegree.model.spatialschema.Point;
057    import org.deegree.portal.PortalUtils;
058    import org.deegree.portal.context.ContextException;
059    import org.deegree.portal.context.Layer;
060    import org.deegree.portal.context.LayerList;
061    import org.deegree.portal.context.Node;
062    import org.deegree.portal.context.ViewContext;
063    import org.deegree.portal.portlet.modules.actions.IGeoPortalPortletPerform;
064    
065    /**
066     * The servlet will be used to update a map model (Web Map Context) in the background (using an
067     * invisible iframe) from an iGeoPortal Portlet Edition
068     *
069     *
070     * @version $Revision: 18195 $
071     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
072     * @author last edited by: $Author: mschneider $
073     *
074     * @version 1.0. $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
075     *
076     * @since 2.0
077     */
078    public class WMCUpdateServlet extends HttpServlet {
079    
080        private static final long serialVersionUID = 2927537039728672671L;
081    
082        private static final ILogger LOG = LoggerFactory.getLogger( WMCUpdateServlet.class );
083    
084        @Override
085        public void init()
086                                throws ServletException {
087            super.init();
088        }
089    
090        @Override
091        protected void doGet( HttpServletRequest request, HttpServletResponse response )
092                                throws ServletException, IOException {
093    
094            doPost( request, response );
095        }
096    
097        @Override
098        protected void doPost( HttpServletRequest request, HttpServletResponse response )
099                                throws ServletException, IOException {
100    
101            Map<String, String> parameter = KVP2Map.toMap( request );
102    
103            String mm = parameter.get( "MAPPORTLET" );
104            String nodeValue = parameter.get( "NODE" );
105            String action = parameter.get( "ACTION" );
106    
107            LOG.logDebug( "parameter: " + parameter );
108    
109            IGeoPortalPortletPerform igeo = new IGeoPortalPortletPerform( request, null, getServletContext() );
110    
111            try {
112                // Handle ajax/iframe requests.
113                // All arguments are passed via a parameter map, see the handelXxxXxx
114                // static methods for the accepted parameters.
115    
116                // The parameter NODE identifies the affected node/layer:
117                // nodes are passed as 'node-xx' where xx is the number/id
118                // layers are passed as 'layername|serveraddress'
119                if ( action != null && mm != null && nodeValue != null ) {
120                    ViewContext vc = igeo.getCurrentViewContext( mm );
121    
122                    // send parts of a layer tree
123                    if ( action.equalsIgnoreCase( "TREEDATA" ) ) {
124                        // writes into response stream
125                        handleTreeData( parameter, vc, response );
126                        return;
127                    }
128    
129                    // handle actions on nodes/layers
130                    boolean repaintMap = true;
131                    if ( action.equalsIgnoreCase( "MOVENODE" ) ) {
132                        repaintMap = handleMoveNode( parameter, vc );
133                    } else if ( action.equalsIgnoreCase( "ADDNODE" ) ) {
134                        String nextNodeID = handleAddNode( parameter, vc );
135                        request.setAttribute( "NEXTNODEID", nextNodeID );
136                        repaintMap = false;
137                    } else if ( action.equalsIgnoreCase( "REMOVENODE" ) ) {
138                        handleRemoveNode( parameter, vc );
139                    } else if ( action.equalsIgnoreCase( "RENAMENODE" ) ) {
140                        handleRenameNode( parameter, vc );
141                        repaintMap = false;
142                    } else if ( action.equalsIgnoreCase( "SETVISIBILITY" ) ) {
143                        handleSetVisibility( parameter, vc );
144                    }
145                    if ( repaintMap ) {
146                        // if the visible layers changed,
147                        // send new baserequests to client ( see igeoportal/enterprise.jsp)
148                        request.setAttribute( "UPDATE_BASEREQUESTS", true );
149                        request.setAttribute( "VIEWCONTEXT", vc );
150                    }
151                } else if ( mm != null ) {
152                    // update bbox
153                    ViewContext vc = igeo.getCurrentViewContext( mm );
154    
155                    String bbox = parameter.get( IGeoPortalPortletPerform.PARAM_BBOX );
156    
157                    if ( bbox != null && vc != null ) {
158                        double[] coords = StringTools.toArrayDouble( bbox, "," );
159                        CoordinateSystem crs = vc.getGeneral().getBoundingBox()[0].getCoordinateSystem();
160                        Point[] pt = new Point[2];
161                        pt[0] = GeometryFactory.createPoint( coords[0], coords[1], crs );
162                        pt[1] = GeometryFactory.createPoint( coords[2], coords[3], crs );
163                        try {
164                            vc.getGeneral().setBoundingBox( pt );
165                        } catch ( ContextException should_never_happen ) {
166                            //nottin
167                        }
168                        List<Envelope> history = (List<Envelope>) request.getSession().getAttribute(
169                                                                                                     IGeoPortalPortletPerform.SESSION_HISTORY );
170                        int p = (Integer) request.getSession().getAttribute(
171                                                                             IGeoPortalPortletPerform.SESSION_HISTORYPOSITION );
172                        Envelope current = history.get( p );
173                        Envelope env = GeometryFactory.createEnvelope( coords[0], coords[1], coords[2], coords[3], null );
174                        if ( current == null || !current.equals( env ) ) {
175                            p++;
176                            history.add( p, env );
177                            request.getSession().setAttribute( IGeoPortalPortletPerform.SESSION_HISTORYPOSITION, p );
178                        }
179                    }
180    
181                    if ( bbox != null && parameter.get( "LAYERS" ) != null ) {
182                        // just change layerlist if the request contains a BBOX parameter
183                        // and at least one layer because other wise it will be the initial call;
184                        Layer[] layers = vc.getLayerList().getLayers();
185                        String ly =  parameter.get( "LAYERS" );
186                        StringBuffer sb = new StringBuffer( 100 );
187                        for ( int i = 0; i < layers.length; i++ ) {
188                            sb.append( layers[i].getName() ).append( '|' );
189                            sb.append( layers[i].getServer().getOnlineResource() );
190                            if ( ly.indexOf( sb.toString() ) > -1 ) {
191                                layers[i].setHidden( false );
192                            } else {
193                                layers[i].setHidden( true );
194                            }
195                            sb.delete( 0, sb.length() );
196                        }
197                        igeo.setCurrentMapContext( vc, mm );
198                    }
199                } else {
200                    System.out.println( "no mapmodel defined in request; ensure that parameter 'MAPPORTLET' is defined!" );
201                }
202            } catch ( Exception e ) {
203                e.printStackTrace();
204                LOG.logError( "could not update WMC: " + e.getMessage(), e );
205            }
206            request.setAttribute( "ACTION", getInitParameter( "ACTION" ) );
207    
208            request.getRequestDispatcher( "igeoportal/enterprise.jsp" ).forward( request, response );
209        }
210    
211        /**
212         * moves a node/layer to a new position used parameters: NODE, BEFORENODE, PARENTNODE
213         *
214         * @return true if moved layer/node was visible and a repaint is necessary
215         */
216        private static boolean handleMoveNode( Map<String, String> parameter, ViewContext vc ) {
217            int nodeID = getNodeID( parameter, "NODE" );
218            boolean visibleLayer;
219            if ( nodeID != -1 ) { // move a node
220                int beforeNodeID = getNodeID( parameter, "BEFORENODE" );
221                int parentNodeID = getNodeID( parameter, "PARENTNODE" );
222                vc.getLayerList().moveNodes( nodeID, beforeNodeID );
223                Node node = vc.getGeneral().getExtension().getLayerTreeRoot().getNode( nodeID );
224                Node parent;
225                if ( parentNodeID == -1 ) {
226                    parent = vc.getGeneral().getExtension().getLayerTreeRoot();
227                } else {
228                    parent = vc.getGeneral().getExtension().getLayerTreeRoot().getNode( parentNodeID );
229                }
230                node.getParent().removeNode( nodeID );
231                node.setParent( parent );
232                int beforeNodeIndex = parent.getIndex( beforeNodeID );
233                if ( beforeNodeIndex == -1 ) {
234                    parent.appendNode( node );
235                } else {
236                    parent.insertNode( node, beforeNodeIndex );
237                }
238                visibleLayer = PortalUtils.hasNodeVisibleLayers( vc, nodeID );
239            } else { // move a layer with layer/servername
240                StringPair layerName = getLayer( parameter, "NODE" );
241                StringPair beforeLayerName = getLayer( parameter, "BEFORENODE" );
242                int parentNodeId = getNodeID( parameter, "PARENTNODE" );
243                Layer layer = vc.getLayerList().getLayer( layerName.first, layerName.second );
244                layer.getExtension().setParentNodeId( parentNodeId );
245                Layer beforeLayer = null;
246                if ( beforeLayerName.second != "" ) {
247                    beforeLayer = vc.getLayerList().getLayer( beforeLayerName.first, beforeLayerName.second );
248                }
249                vc.getLayerList().move( layer, beforeLayer );
250                visibleLayer = !layer.isHidden();
251            }
252            return visibleLayer;
253        }
254    
255        /**
256         * rename a node used parameters: NODE and NODETITLE
257         */
258        private static void handleRenameNode( Map<String, String> parameter, ViewContext vc )
259                                throws ContextException {
260            int nodeID = getNodeID( parameter, "NODE" );
261            String title = parameter.get( "NODETITLE" );
262            if ( title != null ) {
263                if ( nodeID != -1 ) {
264                    vc.getGeneral().getExtension().getLayerTreeRoot().getNode( nodeID ).setTitle( title );
265                }
266            }
267        }
268    
269        /**
270         * removes a layer/node. when a node is removed, all child nodes/layers will be removed too used
271         * parameters: NODE
272         */
273        private static void handleRemoveNode( Map<String, String> parameter, ViewContext vc ) {
274            int nodeID = getNodeID( parameter, "NODE" );
275            if ( nodeID != -1 ) {
276                vc.getLayerList().removeLayers( nodeID );
277                vc.getGeneral().getExtension().getLayerTreeRoot().removeNode( nodeID );
278            } else {
279                StringPair layer = getLayer( parameter, "NODE" );
280                vc.getLayerList().removeLayer( layer.first, layer.second );
281            }
282        }
283    
284        /**
285         * adds a new node to the context used parameters: NODE, NODETITLE, PARENTNODE
286         *
287         * @return id for the next new node
288         */
289        private static String handleAddNode( Map<String, String> parameter, ViewContext vc )
290                                throws ContextException {
291            int nodeID = getNodeID( parameter, "NODE" );
292            int parentNodeID = getNodeID( parameter, "PARENTNODE" );
293            String nodeTitle = parameter.get( "NODETITLE" );
294            if ( nodeTitle == null ) {
295                nodeTitle = "";
296            }
297            Node root = vc.getGeneral().getExtension().getLayerTreeRoot();
298            if ( parentNodeID != -1 ) {
299                root = root.getNode( parentNodeID );
300            }
301            Node newNode = new Node( nodeID, root, nodeTitle, true, true );
302            root.insertNode( newNode, 0 );
303            String nextNodeID = "node-" + ( root.getMaxNodeId() + 1 );
304            return nextNodeID;
305        }
306    
307        /**
308         * sets visibility for layers or complete nodes used parameters: HIDDEN (boolean) and NODE
309         */
310        private static void handleSetVisibility( Map<String, String> parameter, ViewContext vc ) {
311            boolean hidden = Boolean.parseBoolean( parameter.get( "HIDDEN" ) );
312            int nodeID = getNodeID( parameter, "NODE" );
313            if ( nodeID != -1 ) { // change complete node
314                PortalUtils.setVisibility( vc, nodeID, hidden );
315            } else { // change single layer
316                StringPair layerName = getLayer( parameter, "NODE" );
317                Layer layer = vc.getLayerList().getLayer( layerName.first, layerName.second );
318                layer.setHidden( hidden );
319            }
320        }
321    
322        /**
323         * sends layer/node data for requested node as JSON to response stream used parameters: NODE
324         */
325        private static void handleTreeData( Map<String, String> parameter, ViewContext vc, HttpServletResponse response )
326                                throws IOException {
327            StringBuilder sb = new StringBuilder();
328            sb.append( '[' );
329    
330            LayerList layerList = vc.getLayerList();
331            String nodeValue = parameter.get( "NODE" );
332            // root node
333            if ( nodeValue.equals( "/" ) ) {
334                // return all nodes for root
335                Node[] nodes = vc.getGeneral().getExtension().getLayerTreeRoot().getNodes();
336                for ( Node n : nodes ) {
337                    boolean hidden = !PortalUtils.hasNodeVisibleLayers( vc, n.getId() );
338                    sb.append( String.format( "{'text': '%s', 'iconCls': 'folder', 'id':'node-%s', "
339                                              + "'leaf': false, 'checked': %s, 'expanded': true},", n.getTitle(),
340                                              n.getId(), hidden ? "false" : "true" ) );
341                }
342            } else {
343                int nodeID = getNodeID( parameter, "NODE" );
344                if ( nodeID != -1 ) {
345                    // first add the nodes...
346                    Node[] nodes = vc.getGeneral().getExtension().getLayerTreeRoot().getNode( nodeID ).getNodes();
347                    for ( Node n : nodes ) {
348                        boolean hidden = !PortalUtils.hasNodeVisibleLayers( vc, n.getId() );
349                        sb.append( String.format( "{'text': '%s', 'iconCls': 'folder', 'id':'node-%s', "
350                                                  + "'leaf': false, 'checked': %s},", n.getTitle(), n.getId(),
351                                                  hidden ? "false" : "true" ) );
352                    }
353                    // ...then all layers for this node
354                    for ( Layer layer : layerList.getLayersByNodeId( nodeID ) ) {
355                        if ( layer.getServer().getCapabilities() != null ) {
356                            URL s = null;
357                            if ( layer.getStyleList().getCurrentStyle().getLegendURL() != null ) {
358                                s = layer.getStyleList().getCurrentStyle().getLegendURL().getOnlineResource();
359                            }
360                            sb.append( String.format(
361                                                      "{'text': '%s', 'id':'%s', 'leaf': true, 'checked': %s, 'img': '%s'},",
362                                                      layer.getTitle(), layer.getName() + '|'
363                                                                        + layer.getServer().getOnlineResource(),
364                                                      layer.isHidden() ? "false" : "true", s ) );
365                        }
366                    }
367                }
368            }
369            sb.append( ']' );
370            String fixedDemoOutput = sb.toString();
371            response.setContentType( "application/json" );
372            response.setCharacterEncoding( "utf-8" );
373            response.getWriter().write( fixedDemoOutput );
374        }
375    
376        /**
377         * extracts the numeric node id from the node id sting (eg. (String)node-42 -> (int)42)
378         *
379         * @param parameter
380         *            the parameter map
381         * @param paramName
382         *            key of the parameter
383         * @return the node id or -1, if not found
384         */
385        private static int getNodeID( Map<String, String> parameter, String paramName ) {
386            String paramValue = parameter.get( paramName.toUpperCase() );
387            int nodeID = -1;
388            if ( paramValue != null && paramValue.startsWith( "node-" ) ) {
389                nodeID = Integer.parseInt( paramValue.substring( paramValue.lastIndexOf( '-' ) + 1 ) );
390            }
391            return nodeID;
392        }
393    
394        /**
395         * extracts the layer and servername from a node id
396         *
397         * @param parameter
398         *            the parameter map
399         * @param paramName
400         *            key of the parameter
401         * @return the layer (first) and servername (second)
402         */
403        private static StringPair getLayer( Map<String, String> parameter, String paramName ) {
404            String paramValue = parameter.get( paramName.toUpperCase() );
405            StringPair result = new StringPair();
406            if ( paramValue != null ) {
407                String[] values = paramValue.split( "\\|", 2 );
408                result.first = values[0];
409                if ( values.length == 2 ) {
410                    result.second = values[1];
411                } else {
412                    result.second = "";
413                }
414            }
415            return result;
416        }
417    
418    }