    001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_testing/src/org/deegree/portal/standard/context/control/ContextSaveListener.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
    037    package org.deegree.portal.standard.context.control;
    038
    039    import java.io.File;
    040    import java.io.FileOutputStream;
    041    import java.io.OutputStream;
    042
    043    import javax.servlet.http.HttpServletRequest;
    044    import javax.servlet.http.HttpSession;
    045    import javax.xml.transform.Source;
    046    import javax.xml.transform.Transformer;
    047    import javax.xml.transform.TransformerFactory;
    048    import javax.xml.transform.dom.DOMSource;
    049    import javax.xml.transform.stream.StreamResult;
    050
    051    import org.deegree.enterprise.control.FormEvent;
    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.XMLFragment;
    062    import org.deegree.i18n.Messages;
    063    import org.deegree.model.spatialschema.Envelope;
    064    import org.deegree.portal.Constants;
    065    import org.deegree.portal.PortalException;
    066    import org.deegree.portal.context.ViewContext;
    067    import org.deegree.portal.context.XMLFactory;
    068    import org.w3c.dom.Document;
    069
    070    /**
    071     * This class saves a new context based on changes made by the user (on the client) and based on the original context
    072     * xml. <br/>
    073     * Files are saved under .../WEB-INF/xml/users/some_user, where some_user is passed as an RPC parameter. Files should be
    074     * saved with .xml extension because the default load context listener class looks up those files. <br/>
    075     *
    076     * @author <a href="mailto:taddei@lat-lon.de">Ugo Taddei</a>
    077     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
    078     * @author last edited by: $Author: jmays $
    079     *
    080     * @version $Revision: 21347 $, $Date: 2009-12-09 17:04:39 +0100 (Mi, 09. Dez 2009) $
    081     */
    082    public class ContextSaveListener extends AbstractContextListener {
    083
    084        private static final ILogger LOG = LoggerFactory.getLogger( ContextSaveListener.class );
    085
    086        private static String userDir = "WEB-INF/conf/igeoportal/users/";
    087
    088        private static String contextDir = "WEB-INF/conf/igeoportal/";
    089
    090        /*
    091         * (non-Javadoc)
    092         *
    093         * @see org.deegree.enterprise.control.WebListener#actionPerformed(org.deegree.enterprise.control.FormEvent)
    094         */
    095        @Override
    096        public void actionPerformed( FormEvent event ) {
    097
    098            RPCWebEvent rpc = (RPCWebEvent) event;
    099            try {
    100                validate( rpc );
    101            } catch ( PortalException e ) {
    102                LOG.logError( e.getMessage(), e );
    103                gotoErrorPage( Messages.getMessage( "IGEO_STD_CNTXT_INVALID_RPC", "ContextSave", e.getMessage() ) );
    104                return;
    105            }
    106
    107            String newContext = null;
    108            try {
    109                newContext = storeContext( rpc );
    110            } catch ( Exception e ) {
    111                LOG.logError( e.getMessage(), e );
    112                gotoErrorPage( Messages.getMessage( "IGEO_STD_CNTXT_ERROR_SAVE_CNTXT" ) );
    113                return;
    114            }
    115
    116            // forward to new page
    117            this.getRequest().setAttribute( Constants.MESSAGE,
    118                                            Messages.getMessage( "IGEO_STD_CNTXT_SUCCESS_SAVE_CNTXT", newContext ) );
    119        }
    120
    121        /**
    122         * stores the current context of the user with a defined name
    123         *
    124         * @param event
    125         * @return name of the context that has been stored
    126         * @throws PortalException
    127         */
    128        private String storeContext( RPCWebEvent event )
    129                                throws PortalException {
    130
    131            RPCMethodCall mc = event.getRPCMethodCall();
    132            RPCParameter[] pars = mc.getParameters();
    133            RPCStruct struct = (RPCStruct) pars[0].getValue();
    134
    135            // read base context
    136            StringBuffer path2Dir = new StringBuffer( getHomePath() );
    137            path2Dir.append( contextDir );
    138
    139            // access base context
    140            HttpSession session = ( (HttpServletRequest) getRequest() ).getSession();
    141            ViewContext vc = (ViewContext) session.getAttribute( Constants.CURRENTMAPCONTEXT );
    142            // change values: BBOX and Layer List
    143            Envelope bbox = extractBBox( (RPCStruct) struct.getMember( Constants.RPC_BBOX ).getValue(), null );
    144            changeBBox( vc, bbox );
    145            RPCMember[] layerList = ( (RPCStruct) struct.getMember( "layerList" ).getValue() ).getMembers();
    146            changeLayerList( vc, layerList );
    147
    148            // save new context
    149            // get map context value
    150            String username = "default";
    151            try {
    152                String sid = RPCUtils.getRpcPropertyAsString( struct, "sessionID" );
    153                LOG.logDebug( "sessionID ", sid );
    154                username = getUserName( sid );
    155                if ( username == null ) {
    156                    username = "default";
    157                }
    158                LOG.logDebug( "username ", username );
    159            } catch ( Exception e ) {
    160                LOG.logError( e.getMessage(), e );
    161            }
    162
    163            String newContext = RPCUtils.getRpcPropertyAsString( struct, "newContext" );
    164
    165            // check for init params, adjusting the file name input of the user
    166            if ( getInitParameter( "RESTRICT_CHARS" ) != null || getInitParameter( "ADD_XML_SUFFIX" ) != null ) {
    167                newContext = fixFileName( newContext, getInitParameter( "RESTRICT_CHARS" ),
    168                                          getInitParameter( "ADD_XML_SUFFIX" ) );
    169            }
    170
    171            path2Dir = new StringBuffer( getHomePath() );
    172            path2Dir.append( userDir );
    173            path2Dir.append( username );
    174            File file = new File( path2Dir.toString() );
    175            if ( !file.exists() ) {
    176                // create directory if not exists
    177                file.mkdir();
    178            }
    179            path2Dir.append( "/" );
    180            path2Dir.append( newContext );
    181
    182            saveDocument( vc, path2Dir.toString() );
    183
    184            return newContext;
    185        }
    186
    187        /**
    188         * This method replaces all forbidden characters with underscores ("_") and sets the file extension to ".xml".
    189         * Accepted characters are: [a-zA-Z_0-9-.]
    190         *
    191         * @param newContext
    192         * @param restrictedChars
    193         *            may be null
    194         * @param addXmlSuffix
    195         *            may be null
    196         * @return updated version of the passed String
    197         */
    198        private String fixFileName( String newContext, String restrictedChars, String addXmlSuffix ) {
    199
    200            if ( restrictedChars != null ) {
    201                String regex = null;
    202                if ( restrictedChars.length() == 0 ) {
    203                    // replace everything that is not a word character [a-zA-Z_0-9]
    204                    regex = "[^\\w]";
    205                } else {
    206                    // replace everything that is not a word character [a-zA-Z_0-9] or one of the restrictedChars
    207                    regex = "[^\\w" + restrictedChars + "]";
    208                }
    209
    210                // replace forbidden characters with underscore
    211                newContext = newContext.replaceAll( regex, "_" );
    212                // trimm double underscores
    213                while ( newContext.contains( "__" ) ) {
    214                    newContext = newContext.replace( "__", "_" );
    215                }
    216            }
    217
    218            if ( "true".equals( addXmlSuffix ) ) {
    219                // add proper file ending ".xml"
    220                if ( !newContext.endsWith( ".xml" ) ) {
    221                    newContext += ".xml";
    222                }
    223            }
    224            return newContext;
    225        }
    226
    227        /**
    228         * saves the new context as xml
    229         *
    230         * @param vc
    231         * @param filename
    232         * @throws PortalException
    233         */
    234        public static final void saveDocument( ViewContext vc, String filename )
    235                                throws PortalException {
    236            try {
    237                XMLFragment xml = XMLFactory.export( vc );
    238                FileOutputStream fos = new FileOutputStream( filename );
    239                xml.write( fos );
    240                fos.close();
    241            } catch ( Exception e ) {
    242                LOG.logError( e.getMessage(), e );
    243                throw new PortalException( Messages.getMessage( "IGEO_STD_CNTXT_ERROR_SAVE_FILE", filename ) );
    244            }
    245        }
    246
    247        /**
    248         * validates the incoming RPC event
    249         *
    250         * @param rpc
    251         * @throws PortalException
    252         */
    253        private void validate( RPCWebEvent rpc )
    254                                throws PortalException {
    255            RPCMethodCall mc = rpc.getRPCMethodCall();
    256            RPCParameter param = mc.getParameters()[0];
    257            RPCStruct struct = (RPCStruct) param.getValue();
    258            RPCMember username = struct.getMember( "sessionID" );
    259            if ( username == null ) {
    260                throw new PortalException( Messages.getMessage( "IGEO_STD_CNTXT_MISSING_PARAM", "sessionID", "ContextSave" ) );
    261            }
    262            RPCMember newContext = struct.getMember( "newContext" );
    263            if ( newContext == null ) {
    264                throw new PortalException(
    265                                           Messages.getMessage( "IGEO_STD_CNTXT_MISSING_PARAM", "newContext", "ContextSave" ) );
    266            }
    267            RPCMember layerList = struct.getMember( "layerList" );
    268            if ( layerList == null ) {
    269                throw new PortalException( Messages.getMessage( "IGEO_STD_CNTXT_MISSING_PARAM", "layerList", "ContextSave" ) );
    270            }
    271            // TODO validate box: should do this in a common (static) method
    272            // for many listeners that need a bbox
    273        }
    274
    275        /**
    276         * common method to save xml
    277         *
    278         * @param os
    279         * @param doc
    280         * @throws PortalException
    281         */
    282        protected static void internalSave( OutputStream os, Document doc )
    283                                throws PortalException {
    284            try {
    285                Source source = new DOMSource( doc );
    286                Transformer transformer = TransformerFactory.newInstance().newTransformer();
    287                transformer.transform( source, new StreamResult( os ) );
    288            } catch ( Exception e ) {
    289                throw new PortalException( Messages.getMessage( "IGEO_STD_CNTXT_ERROR_INTERNAL_SAVE",
    290                                                                StringTools.stackTraceToString( e.getStackTrace() ) ) );
    291            }
    292        }
    293
    294    }