/*---------------- FILE HEADER ------------------------------------------ This file is part of deegree. Copyright (C) 2001-2006 by: EXSE, Department of Geography, University of Bonn http://www.giub.uni-bonn.de/deegree/ lat/lon GmbH http://www.lat-lon.de This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Contact: Andreas Poth lat/lon GmbH Aennchenstr. 19 53115 Bonn Germany E-Mail: poth@lat-lon.de Klaus Greve Department of Geography University of Bonn Meckenheimer Allee 166 53115 Bonn Germany E-Mail: klaus.greve@uni-bonn.de ---------------------------------------------------------------------------*/ package org.deegree.portal.standard.context.control; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.deegree.enterprise.control.FormEvent; import org.deegree.enterprise.control.RPCMember; import org.deegree.enterprise.control.RPCMethodCall; import org.deegree.enterprise.control.RPCParameter; import org.deegree.enterprise.control.RPCStruct; import org.deegree.enterprise.control.RPCUtils; import org.deegree.enterprise.control.RPCWebEvent; import org.deegree.framework.log.ILogger; import org.deegree.framework.log.LoggerFactory; import org.deegree.framework.util.StringTools; import org.deegree.framework.xml.XMLFragment; import org.deegree.model.spatialschema.Envelope; import org.deegree.ogcwebservices.getcapabilities.OGCCapabilities; import org.deegree.ogcwebservices.wms.capabilities.WMSCapabilitiesDocument; import org.deegree.portal.Constants; import org.deegree.portal.PortalException; import org.deegree.portal.context.ContextException; import org.deegree.portal.context.Layer; import org.deegree.portal.context.LayerList; import org.deegree.portal.context.Server; import org.deegree.portal.context.ViewContext; import org.deegree.portal.context.XMLFactory; import org.w3c.dom.Document; /** * This class saves a new context based on changes made by the * user (on the client) and based on the original context xml. * <br/> * Files are saved under .../WEB-INF/xml/users/some_user, where * some_user is passed as an RPC parameter. Files should be saved with * .xml extension becuase the default load context listener class looks * up those files. * <br/> * Currently this class is only channing the bounding box and the layers * visibility. * * @author <a href="mailto:taddei@lat-lon.de">Ugo Taddei</a> * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> * */ public class ContextSaveListener extends AbstractContextListener { private static final ILogger LOG = LoggerFactory.getLogger( ContextSaveListener.class ); private static String userDir = "WEB-INF/conf/igeoportal/users/"; private static String contextDir = "WEB-INF/conf/igeoportal/"; /** * @see org.deegree.enterprise.control.WebListener#actionPerformed(org.deegree.enterprise.control.FormEvent) */ public void actionPerformed( FormEvent event ) { RPCWebEvent rpc = (RPCWebEvent) event; try { validate( rpc ); } catch ( PortalException e ) { gotoErrorPage( "Not a valid RPC for ContextSave <br/>" + e.getMessage() ); return; } String newContext = null; try { newContext = storeContext( rpc ); } catch ( Exception e ) { e.printStackTrace(); gotoErrorPage( "Context could not be saved <br/>" + e.getMessage() ); return; } // forward to new page this.getRequest().setAttribute( Constants.MESSAGE, "Saved context: " + newContext ); } /** * stores the current context of the user with a defined name * * @param event * @return name of the context that has been stored */ private String storeContext( RPCWebEvent event ) throws Exception { RPCMethodCall mc = event.getRPCMethodCall(); RPCParameter[] pars = mc.getParameters(); RPCStruct struct = (RPCStruct) pars[0].getValue(); // read base context StringBuffer path2Dir = new StringBuffer( getHomePath() ); path2Dir.append( contextDir ); // access base context HttpSession session = ( (HttpServletRequest) getRequest() ).getSession(); ViewContext vc = (ViewContext) session.getAttribute( Constants.CURRENTMAPCONTEXT ); // change values: BBOX and Layer List Envelope bbox = extractBBox( (RPCStruct) struct.getMember( Constants.RPC_BBOX ).getValue() ); changeBBox( vc, bbox ); RPCMember[] layerList = createLayerList( (RPCStruct) struct.getMember( "layerList" ).getValue() ); changeLayerList( vc, layerList ); // save new context // get map context value String username = "default"; try { String sid = RPCUtils.getRpcPropertyAsString( struct, "sessionID" ); LOG.logDebug( "sessionID ", sid ); username = getUserName( sid ); if ( username == null ) { username = "default"; } LOG.logDebug( "username ", username ); } catch ( Exception e ) { e.printStackTrace(); } String newContext = RPCUtils.getRpcPropertyAsString( struct, "newContext" ); path2Dir = new StringBuffer( getHomePath() ); path2Dir.append( userDir ); path2Dir.append( username ); File file = new File( path2Dir.toString() ); if ( !file.exists() ) { // create directory if not exists file.mkdir(); } path2Dir.append( "/" ); path2Dir.append( newContext ); saveDocument( vc, path2Dir.toString() ); return newContext; } /** * saves the new context as xml */ public static final void saveDocument( ViewContext vc, String filename ) throws PortalException { try { XMLFragment xml = XMLFactory.export( vc ); FileOutputStream fos = new FileOutputStream( filename ); xml.write( fos ); fos.close(); } catch ( FileNotFoundException e ) { e.printStackTrace(); throw new PortalException( "could not save file '" + filename + "'\n" + StringTools.stackTraceToString( e.getStackTrace() ) ); } catch ( IOException e ) { e.printStackTrace(); throw new PortalException( "could not save file '" + filename + "'\n" + StringTools.stackTraceToString( e.getStackTrace() ) ); } catch ( ParserConfigurationException e ) { e.printStackTrace(); throw new PortalException( "could not save file '" + filename + "'\n" + StringTools.stackTraceToString( e.getStackTrace() ) ); } catch ( Exception e ) { e.printStackTrace(); } } /** * * validates the incoming RPC event */ private void validate( RPCWebEvent rpc ) throws PortalException { RPCMethodCall mc = rpc.getRPCMethodCall(); RPCParameter param = mc.getParameters()[0]; RPCStruct struct = (RPCStruct) param.getValue(); RPCMember username = struct.getMember( "sessionID" ); if ( username == null ) { throw new PortalException( "missing parameter 'sessionID' in RPC for ContextSave" ); } RPCMember newContext = struct.getMember( "newContext" ); if ( newContext == null ) { throw new PortalException( "missing parameter 'newContext' in RPC for ContextSave" ); } RPCMember layerList = struct.getMember( "layerList" ); if ( layerList == null ) { throw new PortalException( "missing parameter 'layerList' in RPC for ContextSave" ); } // TODO validate box: should do this in a common (static) method // for many listeners that need a bbox } /** * changes the layer list of the ViewContext vc according to the information * contined in the rpcLayerList */ private void changeLayerList( ViewContext vc, RPCMember[] rpcLayerList ) throws PortalException { LayerList layerList = vc.getLayerList(); ArrayList nLayers = new ArrayList( rpcLayerList.length ); // stores visibility vals HashMap layersMap = new HashMap( rpcLayerList.length ); // stores order vals String[] ordered = new String[rpcLayerList.length]; String[] types = new String[rpcLayerList.length]; String[] names = new String[rpcLayerList.length]; String[] addr = new String[rpcLayerList.length]; // this is needed to keep layer order // order is correct in rpc call JavaScript) but get lost in translation... for ( int i = 0; i < rpcLayerList.length; i++ ) { String[] v = StringTools.toArray( (String) rpcLayerList[i].getValue(), "|", false ); String n = rpcLayerList[i].getName(); layersMap.put( n, v[0] ); int cc = Integer.valueOf( v[1] ).intValue(); ordered[cc] = n; types[cc] = v[2]; names[cc] = v[3]; addr[cc] = v[4]; } for ( int i = 0; i < rpcLayerList.length; i++ ) { String n = ordered[i]; boolean isVisible = Boolean.valueOf( ( (String) layersMap.get( n ) ) ).booleanValue(); Layer l = layerList.getLayer( n ); if ( l != null ) { // needed to reconstruct new layer order // otherwise layer order is still from original context l.setHidden( !isVisible ); } else { if ( layerList.getLayers().length == 0 ) { //FIXME is this Exception Correct throw new PortalException( "LayerList is empty for required mapcontext" ); } Layer p = layerList.getLayers()[0]; // a new layer must be created because it is not prsent // in the current context. This is the case if the client // has loaded an additional WMS String[] tmp = StringTools.toArray( types[i], " ", false ); try { WMSCapabilitiesDocument doc = new WMSCapabilitiesDocument(); doc.load( new URL( addr[i] ) ); OGCCapabilities capa = doc.parseCapabilities(); Server server = new Server( names[i], tmp[1], tmp[0], new URL( addr[i] ), capa ); l = new Layer( server, n, n, "", p.getSrs(), null, null, p.getFormatList(), p.getStyleList(), false, false, null ); } catch ( Exception e1 ) { throw new PortalException( StringTools.stackTraceToString( e1 ) ); } } nLayers.add( l ); } try { nLayers.trimToSize(); Layer[] ls = new Layer[nLayers.size()]; ls = (Layer[]) nLayers.toArray( ls ); vc.setLayerList( new LayerList( ls ) ); } catch ( ContextException e ) { throw new PortalException( "Error setting new layer list \n" + StringTools.stackTraceToString( e.getStackTrace() ) ); } } /** * common method to save xml */ protected static void internalSave( OutputStream os, Document doc ) throws PortalException { try { Source source = new DOMSource( doc ); Transformer transformer = TransformerFactory.newInstance().newTransformer(); transformer.transform( source, new StreamResult( os ) ); } catch ( Exception e ) { throw new PortalException( "Error saving context xml \n" + StringTools.stackTraceToString( e.getStackTrace() ) ); } } /** * creates a layer list as RPCMember[] from an RPC struct. * this method might change to accomodate for others layer props */ private RPCMember[] createLayerList( RPCStruct rpcStruc ) { RPCMember[] ls = rpcStruc.getMembers(); return ls; } } /* ******************************************************************** Changes to this class. What the people have been up to: $Log: ContextSaveListener.java,v $ Revision 1.13 2006/10/17 20:31:18 poth *** empty log message *** Revision 1.12 2006/08/29 19:54:14 poth footer corrected Revision 1.11 2006/08/24 16:30:44 poth method extracted to RPCUtil Revision 1.10 2006/08/20 20:53:54 poth changes rquired as a consequence of bug fix in wmc implementation/handling. Instead of determining the correct URL as given in a services capabilities deegree always has used the base URL which is just guarenteed to be valid for GetCapabilities requests. Revision 1.9 2006/06/29 10:29:37 poth *** empty log message *** Revision 1.8 2006/05/23 14:27:49 poth support for UserPrincipal added if no sessionId is set Revision 1.7 2006/05/16 15:56:36 poth code cleanup ********************************************************************** */