001    //$HeadURL: svn+ssh://mschneider@svn.wald.intevation.org/deegree/base/trunk/resources/eclipse/svn_classfile_header_template.xml $
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.ogcwebservices.csw.iso_profile.ebrim;
038    
039    import java.io.BufferedReader;
040    import java.io.ByteArrayOutputStream;
041    import java.io.IOException;
042    import java.io.InputStream;
043    import java.io.InputStreamReader;
044    import java.io.OutputStream;
045    import java.io.OutputStreamWriter;
046    import java.io.PrintWriter;
047    import java.io.UnsupportedEncodingException;
048    import java.net.MalformedURLException;
049    import java.net.URI;
050    import java.net.URISyntaxException;
051    import java.net.URL;
052    import java.security.InvalidParameterException;
053    import java.util.ArrayList;
054    import java.util.HashMap;
055    import java.util.List;
056    import java.util.Map;
057    import java.util.Set;
058    
059    import javax.mail.MessagingException;
060    import javax.mail.internet.ContentDisposition;
061    import javax.mail.internet.MimeBodyPart;
062    import javax.mail.internet.MimeMultipart;
063    import javax.mail.internet.MimeUtility;
064    import javax.mail.util.ByteArrayDataSource;
065    import javax.servlet.Filter;
066    import javax.servlet.FilterChain;
067    import javax.servlet.FilterConfig;
068    import javax.servlet.ServletException;
069    import javax.servlet.ServletRequest;
070    import javax.servlet.ServletResponse;
071    import javax.servlet.http.HttpServletRequest;
072    import javax.servlet.http.HttpServletResponse;
073    
074    import org.deegree.enterprise.servlet.ServletRequestWrapper;
075    import org.deegree.enterprise.servlet.ServletResponseWrapper;
076    import org.deegree.framework.log.ILogger;
077    import org.deegree.framework.log.LoggerFactory;
078    import org.deegree.framework.util.CharsetUtils;
079    import org.deegree.framework.util.IDGenerator;
080    import org.deegree.framework.util.WebappResourceResolver;
081    import org.deegree.framework.xml.InvalidConfigurationException;
082    import org.deegree.framework.xml.XMLException;
083    import org.deegree.framework.xml.XMLFragment;
084    import org.deegree.framework.xml.XMLTools;
085    import org.deegree.ogcbase.CommonNamespaces;
086    import org.deegree.ogcbase.ExceptionCode;
087    import org.deegree.ogcwebservices.OGCRequestFactory;
088    import org.deegree.ogcwebservices.OGCWebServiceException;
089    import org.deegree.ogcwebservices.OGCWebServiceRequest;
090    import org.deegree.ogcwebservices.csw.CSWExceptionCode;
091    import org.deegree.ogcwebservices.csw.CatalogueService;
092    import org.deegree.ogcwebservices.csw.configuration.CatalogueConfiguration;
093    import org.deegree.ogcwebservices.csw.manager.Insert;
094    import org.deegree.ogcwebservices.csw.manager.InsertResults;
095    import org.deegree.ogcwebservices.csw.manager.Manager;
096    import org.deegree.ogcwebservices.csw.manager.Operation;
097    import org.deegree.ogcwebservices.csw.manager.Transaction;
098    import org.deegree.ogcwebservices.csw.manager.TransactionResult;
099    import org.deegree.ogcwebservices.csw.manager.TransactionResultDocument;
100    import org.deegree.ogcwebservices.csw.manager.XMLFactory;
101    import org.deegree.security.GeneralSecurityException;
102    import org.deegree.security.drm.model.User;
103    import org.w3c.dom.CDATASection;
104    import org.w3c.dom.Document;
105    import org.w3c.dom.Element;
106    import org.w3c.dom.Node;
107    import org.xml.sax.SAXException;
108    
109    /**
110     * The <code>CSWEbRIMFilter</code> class is able to handle an incoming csw/wrs ebrim Transaction. This extra filter is
111     * necessary because the insertion of an ebrim object is handled different then the original csw:Insert.
112     * <p>
113     * The first difference is the usage of the form/multipart header which allows for ExtrinsicObjects to be defined
114     * outside the message Body and thus must be handled seperatly.
115     * </p>
116     * <p>
117     * A second difference results after the insertion of an Object for every update and insertion leads to the extra
118     * insertion of an auditableEvent. The AuditableEvents result in an audit-trail (a version coontrol history ) for each
119     * inserted or updated app:RegistryObject.
120     * </p>
121     *
122     * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
123     *
124     * @author last edited by: $Author: poth $
125     *
126     * @version $Revision: 1.13 $, $Date: 2007-11-27 12:50:25 $
127     *
128     */
129    
130    public class CSWEbRIMFilter implements Filter {
131    
132        private static ILogger LOG = LoggerFactory.getLogger( CSWEbRIMFilter.class );
133    
134        private Manager transactionManager = null;
135    
136        private URI appURI;
137    
138        private OWSProxyHandler proxyHandler = null;
139    
140        private CatalogueService cswService;
141    
142        public void init( FilterConfig config )
143                                throws ServletException {
144            String configLocation = config.getInitParameter( "csw.config" );
145            try {
146                URL configURL = WebappResourceResolver.resolveFileLocation( configLocation, config.getServletContext(), LOG );
147                CatalogueConfiguration cswConfig = CatalogueConfiguration.createConfiguration( configURL );
148                cswService = CatalogueService.create( cswConfig );
149                transactionManager = cswService.getManager( "2.0.1" );
150                if ( transactionManager.getWFService() == null ) {
151                    LOG.logError( "CSW (Ebrim) Insert-Filter: The InserFilter has no access to the localWFS" );
152                }
153            } catch ( MalformedURLException e ) {
154                LOG.logError( "Could not initiate CSWInsertfilter: " + e.getMessage() );
155            } catch ( IOException e ) {
156                LOG.logError( "Could not initiate CSWInsertfilter: " + e.getMessage() );
157            } catch ( SAXException e ) {
158                LOG.logError( "Could not initiate CSWInsertfilter: " + e.getMessage() );
159            } catch ( InvalidConfigurationException e ) {
160                LOG.logError( "Could not initiate CSWInsertfilter: " + e.getMessage() );
161            } catch ( OGCWebServiceException e ) {
162                LOG.logError( "Could not initiate CSWInsertfilter: " + e.getMessage() );
163            }
164            if ( transactionManager != null ) {
165                try {
166                    appURI = new URI( "http://www.deegree.org/app" );
167                    proxyHandler = new OWSProxyHandler( config );
168                } catch ( URISyntaxException e ) {
169                    // This never happens but whatever
170                } catch ( InvalidParameterException ipe ) {
171                    LOG.logInfo( "CSW (Ebrim) Insert-Filter: couldn't create an OWSProxyHandler, so no user authentification available, because: "
172                                 + ipe.getMessage() );
173                }
174                LOG.logInfo( "CSW (Ebrim) Insert Servlet Filter successfully initialized" );
175            } else {
176                LOG.logInfo( "CSW (Ebrim) Insert Servlet Filter was not initialized" );
177            }
178        }
179    
180        public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain )
181                                throws IOException, ServletException {
182            if ( transactionManager == null ) {
183                LOG.logInfo( "CSW (Ebrim) Insert-Filter: Local Catalogue service is not instantiated, therefore the CSW-Insert filter can not be used" );
184                sendException( response, new OGCWebServiceException( "The transactionManager is not configured "
185                                                                     + "so not accepting any requests.",
186                                                                     CSWExceptionCode.WRS_NOTIMPLEMENTED ) );
187                // chain.doFilter( request, response );
188                return;
189            }
190            response.setCharacterEncoding( "UTF-8" );
191            // so no get request (or a get Request without parameters)
192            ServletRequestWrapper requestWrapper = null;
193            if ( request instanceof ServletRequestWrapper ) {
194                LOG.logDebug( "The incoming request is actually an org.deegree.enterprise.servlet.RequestWrapper, so not creating new instance." );
195                requestWrapper = (ServletRequestWrapper) request;
196            } else {
197                requestWrapper = new ServletRequestWrapper( (HttpServletRequest) request );
198            }
199    
200            Map<String, String[]> parameters = requestWrapper.getParameterMap();
201            ResponseObject responseObject = null;
202            try {
203                if ( parameters.size() != 0 ) {
204                    responseObject = handleParameterizedRequest( parameters, requestWrapper );
205                } else {
206                    responseObject = handlePostedRequest( requestWrapper, response );
207                }
208            } catch ( OGCWebServiceException e ) {
209                sendException( response, e );
210                return;
211            }
212            if ( responseObject != null && responseObject.doResponseValidation() ) {
213                // Try the user authorization of the response
214                ServletResponseWrapper responseWrapper = new ServletResponseWrapper( (HttpServletResponse) response );
215                responseWrapper.setCharacterEncoding( "UTF-8" );
216                response.setContentType( "application/xml" );
217                if ( responseObject.getOriginalRequest() instanceof Transaction ) {
218                    try {
219                        TransactionResult result = handleTransactions( (Transaction) responseObject.getOriginalRequest(),
220                                                                       responseObject.getUserName() );
221                        LOG.logDebug( "Creating xml representation of the csw-transaction " );
222                        TransactionResultDocument resultDoc = XMLFactory.export( result );
223                        // String prettyString = resultDoc.getAsPrettyString();
224                        if ( LOG.isDebug() ) {
225                            LOG.logDebug( "The result of the csw-transaction was not null, it has following xml-structure: \n"
226                                          + resultDoc.getAsPrettyString() );
227                        }
228                        PrintWriter writer = new PrintWriter( new OutputStreamWriter( responseWrapper.getOutputStream() ) );
229                        resultDoc.prettyPrint( writer );
230                        writer.flush();
231                    } catch ( OGCWebServiceException e ) {
232                        sendException( response, e );
233                        return;
234                    } catch ( Exception e ) {
235                        sendException( response,
236                                       new OGCWebServiceException(
237                                                                   "Error while writing transaction response to the strea: "
238                                                                                           + e.getLocalizedMessage(),
239                                                                   CSWExceptionCode.WRS_TRANSACTIONFAILED ) );
240                        return;
241                    }
242                } else {
243                    // Get a response which must be evaluated
244                    chain.doFilter( requestWrapper, responseWrapper );
245                }
246                if ( this.proxyHandler != null ) {
247                    try {
248                        authorizeResponse( responseObject.getUserName(), responseObject.getPassword(),
249                                           responseObject.getOriginalRequest(), responseWrapper );
250                    } catch ( OGCWebServiceException e ) {
251                        sendException( response, e );
252                        return;
253                    }
254                }
255                try {
256                    afterResponseEvaluation( responseObject.getOriginalRequest(), responseWrapper );
257                } catch ( OGCWebServiceException e ) {
258                    sendException( response, e );
259                    return;
260                }
261                OutputStream os = responseWrapper.getOutputStream();
262                String encoding = requestWrapper.getCharacterEncoding();
263                if ( !"UTF-8".equals( encoding ) ) {
264                    LOG.logDebug( "The request uses following character encoding: " + encoding + " setting to UTF-8." );
265                    encoding = "UTF-8";
266                }
267    
268                String responseString = ( (ServletResponseWrapper.ProxyServletOutputStream) os ).toString( encoding );
269                if ( LOG.isDebug() ) {
270                    LOG.logDebug( "got the string: " + responseString );
271                }
272                os.close();
273                PrintWriter writer = response.getWriter();
274                writer.write( responseString );
275                writer.flush();
276                writer.close();
277                // responseWrapper.flushBuffer();
278            } else {
279                chain.doFilter( requestWrapper, response );
280            }
281        }
282    
283        /**
284         * This method is called after the CSWEbRIMFilter has successfully processed the incoming request, but did not
285         * actually write it to the Stream. THis method could be overwritten to do postprocessing the stream without
286         * actually having to register a new filter.
287         *
288         * @param originalRequest
289         *            containing deegree java bean which is represents the request.
290         * @param responsewrapper
291         *            which should be used.
292         */
293        public void afterResponseEvaluation( OGCWebServiceRequest originalRequest, ServletResponseWrapper responsewrapper )
294                                throws OGCWebServiceException {
295            // This method is called after the CSWEbRIMFilter has successfully processed the incoming request, but did not
296            // write it to the Stream.
297        }
298    
299        /**
300         * @param requestWrapper
301         * @param response
302         * @throws IOException
303         *             if an exception occurred while getting the inputstream
304         */
305        private ResponseObject handlePostedRequest( ServletRequestWrapper requestWrapper, ServletResponse response )
306                                throws OGCWebServiceException, IOException {
307    
308            Transaction transaction = null;
309            BufferedReader reader = new BufferedReader( new InputStreamReader( requestWrapper.getInputStream() ) );
310            String firstLine = reader.readLine();
311            LOG.logDebug( "first line of request: " + firstLine );
312            if ( firstLine == null ) {
313                LOG.logInfo( "CSW (Ebrim) Insert-Filter: no request characters found, not handling request" );
314                // chain.doFilter( requestWrapper, response );
315                throw new OGCWebServiceException( "no request characters found, not handling request",
316                                                  CSWExceptionCode.WRS_INVALIDREQUEST );
317            }
318            if ( LOG.isDebug() ) {
319                LOG.logDebug( "OUTPUTING as Strings" );
320                LOG.logDebug( firstLine );
321                try {
322                    while ( reader.ready() ) {
323                        LOG.logDebug( reader.readLine() );
324                    }
325                } catch ( IOException e ) {
326                    // because of debug, just do anythin.
327                }
328            }
329    
330            LOG.logDebug( "GetContentype(): " + requestWrapper.getContentType() );
331    
332            // These variables will be set according to the request properties
333            CSWSOAPHandler soapHandler = new CSWSOAPHandler();
334            if ( requestWrapper.getContentType() != null
335                 && requestWrapper.getContentType().contains( "multipart/form-data" ) ) {
336                // because we have some multiparts, the soap/owsproxy handling is done in the
337                // #handleMultiparts method
338                transaction = handleMultiparts( requestWrapper, soapHandler );
339                if ( transaction == null ) {
340                    LOG.logDebug( "Could not generate a Transaction object out of the multiparts, giving request to the chain" );
341                    throw new OGCWebServiceException( "Could not generate a Transaction object out of the multiparts",
342                                                      CSWExceptionCode.WRS_INVALIDREQUEST );
343                }
344            } else {
345                // Not a multipart, please continue
346                XMLFragment doc = null;
347                try {
348                    doc = new XMLFragment( new InputStreamReader( requestWrapper.getInputStream() ),
349                                           "http://some_server.org" );
350                } catch ( SAXException e ) {
351                    LOG.logDebug( "couldn't create an xml Fragment of the incoming request, so not handling. "
352                                  + e.getMessage() );
353                    throw new OGCWebServiceException( "Couldn't create an xml Fragment of the incoming request",
354                                                      CSWExceptionCode.WRS_INVALIDREQUEST );
355                } catch ( IOException e ) {
356                    throw new OGCWebServiceException( "Couldn't create an xml Fragment of the incoming request",
357                                                      CSWExceptionCode.WRS_INVALIDREQUEST );
358                }
359                Element rootElement = doc.getRootElement();
360                String ns = rootElement.getNamespaceURI();
361                if ( LOG.isDebug() ) {
362                    LOG.logDebug( "Decoded request as xml:\n " + doc.getAsPrettyString() );
363                }
364    
365                if ( !CommonNamespaces.CSWNS.toASCIIString().equals( ns )
366                     && !CommonNamespaces.W3SOAP_ENVELOPE.toASCIIString().equals( ns ) ) {
367                    LOG.logDebug( "The namespace of the root element (" + ns + ") is neither: "
368                                  + CommonNamespaces.W3SOAP_ENVELOPE + " nor: " + CommonNamespaces.CSWNS.toASCIIString()
369                                  + ", so not using the servlet filter " );
370                    throw new OGCWebServiceException( "Service not known", CSWExceptionCode.WRS_INVALIDREQUEST );
371                }
372    
373                soapHandler.setIncomingRequest( doc );
374                doc = soapHandler.createCSWRequestFromSOAP();
375                if ( doc == null ) {
376                    LOG.logDebug( "The soap handler returned a null valued XMLFragment, cannot be!" );
377                    throw new OGCWebServiceException( "The body of your request could not be parsed.",
378                                                      CSWExceptionCode.WRS_INVALIDREQUEST );
379                }
380                rootElement = doc.getRootElement();
381                if ( LOG.isDebug() ) {
382                    LOG.logDebug( "The body of the soap request:\n" + doc.getAsPrettyString() );
383                }
384    
385                // reset the stream to handle the request without the soap
386                if ( soapHandler.isSOAPRequest() ) {
387                    ByteArrayOutputStream bos = new ByteArrayOutputStream( 50000 );
388                    try {
389                        OutputStreamWriter osw = new OutputStreamWriter( bos, CharsetUtils.getSystemCharset() );
390                        doc.write( osw );
391                    } catch ( UnsupportedEncodingException e ) {
392                        throw new OGCWebServiceException( "The charset is not supported: " + e.getLocalizedMessage(),
393                                                          CSWExceptionCode.WRS_NOTSUPPORTED );
394                    }
395                    requestWrapper.setInputStreamAsByteArray( bos.toByteArray() );
396                }
397    
398                // checking the authorization of the user.
399                OGCWebServiceRequest owsProxyRequest = null;
400                if ( this.proxyHandler != null ) {
401                    owsProxyRequest = proxyHandler.createOWSRequest( requestWrapper );
402                    authorizeRequest( soapHandler.getUserName(), soapHandler.getPassword(), owsProxyRequest, requestWrapper );
403                } else {
404                    LOG.logDebug( "The proxyHandler is not defined so no user Authentification." );
405                    throw new OGCWebServiceException( "The proxyHandler is not defined so not accepting any requests.",
406                                                      CSWExceptionCode.WRS_NOTIMPLEMENTED );
407                }
408    
409                if ( !"Transaction".equals( rootElement.getLocalName() ) ) {
410                    LOG.logDebug( "The localname of the root element is not: 'Transaction', so put request onto the chain. " );
411                    return new ResponseObject( soapHandler.getUserName(), soapHandler.getPassword(), true, owsProxyRequest );
412                }
413    
414                // create the transaction
415                transaction = Transaction.create( Long.toString( IDGenerator.getInstance().generateUniqueID() ),
416                                                  rootElement );
417            }
418            LOG.logDebug( "The request is a csw:Transaction parsed and handled by the CSWEbRIMFilter" );
419            if ( transaction == null ) {
420                LOG.logDebug( "the transaction is null, this cannot be" );
421                throw new OGCWebServiceException( "Failed to handle your transaction, please check your request.",
422                                                  CSWExceptionCode.WRS_INVALIDREQUEST );
423            }
424            return new ResponseObject( soapHandler.getUserName(), soapHandler.getPassword(), true, transaction );
425        }
426    
427        private ResponseObject handleParameterizedRequest( Map<String, String[]> parameters,
428                                                           ServletRequestWrapper requestWrapper )
429                                throws OGCWebServiceException {
430            Map<String, String> params = new HashMap<String, String>();
431            for ( String key : parameters.keySet() ) {
432                String[] tmp = parameters.get( key );
433                for ( int i = 0; i < tmp.length; i++ ) {
434                    tmp[i] = tmp[i].trim();
435                    LOG.logDebug( "for key: " + key + " found param: " + tmp[i] );
436                }
437                // params.put( key.toUpperCase(), StringTools.arrayToString( tmp, ',' ) );
438                if ( tmp.length > 0 ) {
439                    params.put( key.toUpperCase(), tmp[0] );
440                }
441            }
442            String service = params.get( "SERVICE" );
443            if ( service == null ) {
444                // a profile of a service will be treated as a service
445                service = params.get( "PROFILE" );
446                if ( service == null ) {
447                    throw new OGCWebServiceException( "The SERVICE or PROFILE keyword isn't set.",
448                                                      CSWExceptionCode.WRS_INVALIDREQUEST );
449                } else if ( !( OGCRequestFactory.CSW_SERVICE_NAME_EBRIM.equals( service ) || "CSW".equals( service ) ) ) {
450                    LOG.logDebug( "The Service parameter of the request (" + service + ") is neither: "
451                                  + OGCRequestFactory.CSW_SERVICE_NAME_EBRIM + " nor: CSW"
452                                  + ", so not using the servlet filter " );
453                    throw new OGCWebServiceException( "Service not known: " + service, CSWExceptionCode.WRS_INVALIDREQUEST );
454                }
455            }
456    
457            OGCWebServiceRequest owsProxyRequest = null;
458            // put them to uppercase *sigh*
459            String userName = params.get( "USER" );
460            String password = params.get( "PASSWORD" );
461            LOG.logDebug( "Trying to get authentication for user: " + userName + " with password: " + password );
462            if ( this.proxyHandler != null ) {
463                owsProxyRequest = proxyHandler.createOWSRequest( requestWrapper );
464                authorizeRequest( userName, password, owsProxyRequest, requestWrapper );
465            } else {
466                LOG.logDebug( "CSW (Ebrim) Insert-Filter: the proxyHandler is not defined so no user Authentification." );
467                new OGCWebServiceException( "The proxyHandler is not defined so not accepting any requests.",
468                                            CSWExceptionCode.WRS_NOTIMPLEMENTED );
469            }
470            return new ResponseObject( userName, password, true, owsProxyRequest );
471        }
472    
473        private void authorizeRequest( String userName, String password, OGCWebServiceRequest owsProxyRequest,
474                                       ServletRequestWrapper requestWrapper )
475                                throws OGCWebServiceException {
476            // checking the authorization of the user.
477            try {
478                User user = proxyHandler.authentificateFromUserPw( userName, password );
479                proxyHandler.doRequestValidation( requestWrapper, user, owsProxyRequest );
480            } catch ( GeneralSecurityException e ) {
481                LOG.logDebug( "User: " + userName + " with password: " + password
482                              + " couldn't get authentification because: " + e.getMessage() );
483                throw new OGCWebServiceException( e.getMessage(), CSWExceptionCode.WRS_INVALIDREQUEST );
484            }
485        }
486    
487        private void authorizeResponse( String userName, String password, OGCWebServiceRequest owsProxyRequest,
488                                        ServletResponseWrapper responseWrapper )
489                                throws OGCWebServiceException {
490            // checking the authorization of the user.
491            if ( this.proxyHandler != null ) {
492                try {
493                    User user = proxyHandler.authentificateFromUserPw( userName, password );
494                    proxyHandler.doResponseValidation( responseWrapper, user, owsProxyRequest );
495                } catch ( GeneralSecurityException e ) {
496                    LOG.logDebug( "User: " + userName + " with password: " + password
497                                  + " couldn't get authentification because: " + e.getMessage() );
498                    throw new OGCWebServiceException( e.getMessage(), CSWExceptionCode.WRS_INVALIDREQUEST );
499                } catch ( IOException e ) {
500                    LOG.logDebug( "couldn't get an outputstream for the repsponse because: " + e.getMessage() );
501                    throw new OGCWebServiceException( e.getMessage(), CSWExceptionCode.WRS_INVALIDREQUEST );
502                }
503            }
504        }
505    
506        public void destroy() {
507            // implements nottin.
508        }
509    
510        /**
511         * Sends the passed <tt>OGCWebServiceException</tt> to the calling client and flushes/closes the writer.
512         *
513         * @param responseWriter
514         *            to write the message of the exception to
515         * @param e
516         *            the exception to 'send' e.g. write to the stream.
517         * @throws IOException
518         *             if an error occurred while getting the writer of the response.
519         */
520        private void sendException( ServletResponse response, OGCWebServiceException e )
521                                throws IOException {
522            if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
523                Thread.dumpStack();
524            }
525            if ( response instanceof ServletResponseWrapper ) {
526                ( (ServletResponseWrapper) response ).reset();
527            }
528            response.setContentType( "application/xml" );
529            PrintWriter writer = response.getWriter();
530            LOG.logInfo( "CSW (Ebrim) Insert-Filter: Sending OGCWebServiceException to client with message: ."
531                         + e.getMessage() );
532            Document doc = XMLTools.create();
533    
534            XMLFragment frag = new XMLFragment( doc.createElementNS( CommonNamespaces.OWSNS.toASCIIString(),
535                                                                     "ows:ExceptionReport" ) );
536            ExceptionCode code = e.getCode();
537            String exceptionCode = CSWExceptionCode.WRS_INVALIDREQUEST.value;
538            if ( code != null && code.value != null ) {
539                exceptionCode = code.value;
540            }
541            Element soapFailed = XMLTools.appendElement( frag.getRootElement(), CommonNamespaces.WRS_EBRIMNS, exceptionCode );
542            XMLTools.setNodeValue( soapFailed, e.getMessage() );
543            writer.write( frag.getAsPrettyString() );
544            writer.flush();
545            writer.close();
546        }
547    
548        /**
549         * This method handles the operations in the given Transaction that is, it splits the Transaction into update/delete
550         * and insert operations. The update and delete operations are gathered as long as there are no insert operations.
551         * If an Insert opertation is encountered the pending delete/update operations are first send to the csw after which
552         * all records in the insert operation are sequently inserted into the registry, using the
553         * {@link InsertTransactionHandler}. Note this method guarantees the sequential processing of the operations in
554         * their defined order.
555         *
556         * @param transaction
557         *            bean representation of the incoming request.
558         * @param username
559         *            of the user which inserts the registryObjects.
560         * @throws OGCWebServiceException
561         *             if an error occurred while hanlding an operations.
562         */
563        private TransactionResult handleTransactions( Transaction transaction, String username )
564                                throws OGCWebServiceException {
565            List<Operation> ops = transaction.getOperations();
566            // find all the ids which were referenced by the transaction.
567            List<Operation> pendingOps = new ArrayList<Operation>();
568            List<Node> briefInsertedRecords = new ArrayList<Node>();
569            // 0 = insert
570            // 1 = delete
571            // 2 = update
572            int[] resultValues = new int[3];
573            for ( Operation op : ops ) {
574                if ( "Insert".equalsIgnoreCase( op.getName() ) ) {
575                    // First do all transactions which were not insert(s)
576                    if ( pendingOps.size() > 0 ) {
577                        TransactionResult tr = sendPendingOperations( transaction, pendingOps );
578                        resultValues[0] += tr.getTotalInserted();
579                        resultValues[1] += tr.getTotalDeleted();
580                        resultValues[2] += tr.getTotalUpdated();
581                    }
582                    // handle all records inside the insert operation, e.g. set the
583                    // app:RegistryObject/app:status to invalid
584                    // if the id of the object allready exists.
585                    InsertTransactionHandler handler = new InsertTransactionHandler( transaction, (Insert) op, appURI,
586                                                                                     username );
587                    briefInsertedRecords.addAll( handler.handleInsertTransaction( this.transactionManager, resultValues ) );
588    
589                } else {
590                    pendingOps.add( op );
591                }
592            }
593            if ( pendingOps.size() > 0 ) {
594                TransactionResult tr = sendPendingOperations( transaction, pendingOps );
595                resultValues[0] += tr.getTotalInserted();
596                resultValues[1] += tr.getTotalDeleted();
597                resultValues[2] += tr.getTotalUpdated();
598            }
599            LOG.logDebug( "Number of brief inserted records: " + briefInsertedRecords.size() );
600            InsertResults iresults = new InsertResults( briefInsertedRecords );
601            return new TransactionResult( transaction, resultValues[0], resultValues[1], resultValues[2], iresults );
602        }
603    
604        /**
605         * Sends all operations in the list to the csw.
606         *
607         * @param originalTransaction
608         *            used to get the version, id and vendorspecific params from.
609         * @param pendingOps
610         *            the list of delete and update operations.
611         * @return the Result of the csw.
612         * @throws OGCWebServiceException
613         */
614        private TransactionResult sendPendingOperations( Transaction originalTransaction, List<Operation> pendingOps )
615                                throws OGCWebServiceException {
616            Transaction transaction = new Transaction( originalTransaction.getVersion(), originalTransaction.getId(),
617                                                       originalTransaction.getVendorSpecificParameters(), pendingOps, false );
618    
619            return transactionManager.transaction( transaction );
620        }
621    
622        /**
623         * This method handles the multiparts of a ServletRequest. This method is called when the
624         * content-type:form/multipart header is set. Inside the multiparts different extrinsice object representations (as
625         * xml) may reside. They will be added inside the appropriate InsertOperation (found by the extrinsicObject's/@id)
626         * and inserted in the xml representation of the referenced (again while looking at the /@id attribute)
627         * extrinicObject. The definition of the extrinsicObject (e.g. inside the multipart) will be put inside the
628         * following xml-Element: 'deegreecsw:DescribedObject', at the end of the nodelist of the 'rim:ExtrinsicOjbects'.
629         *
630         * @param request
631         *            the actual HttpServletRequest.
632         * @param soapHandler
633         * @return the Transaction Representation of the incoming request
634         * @throws OGCWebServiceException
635         *             if an exception occurred while processing the mime parts.
636         */
637        private Transaction handleMultiparts( HttpServletRequest request, CSWSOAPHandler soapHandler )
638                                throws OGCWebServiceException {
639            Transaction trans = null;
640            // StreamDataSource sds = new StreamDataSource( request );
641            try {
642                ByteArrayDataSource bads = new ByteArrayDataSource( request.getInputStream(), "application/xml" );
643                LOG.logInfo( "CSW (Ebrim) Insert-Filter: Setting the 'mail.mime.multipart.ignoremissingendboundary' System property to false." );
644                System.setProperty( "mail.mime.multipart.ignoremissingendboundary", "false" );
645                MimeMultipart multi = new MimeMultipart( bads );
646                Map<String, Insert> insertOpContainingExtObj = new HashMap<String, Insert>();
647    
648                for ( int i = 0; i < multi.getCount(); i++ ) {
649                    MimeBodyPart content = (MimeBodyPart) multi.getBodyPart( i );
650                    LOG.logDebug( "multipart (" + ( i + 1 ) + " of " + multi.getCount() + ") content id: "
651                                  + content.getContentID() );
652                    LOG.logDebug( "multipart (" + ( i + 1 ) + " of " + multi.getCount() + ") content type: "
653                                  + content.getContentType() );
654                    String contentType = content.getContentType();
655                    if ( !( contentType.contains( "application/xml" ) || contentType.contains( "text/xml" ) ) ) {
656                        throw new OGCWebServiceException(
657                                                          "Other than xml-encoded data can not be handled in the multiparts",
658                                                          CSWExceptionCode.WRS_INVALIDREQUEST );
659                    }
660                    String[] names = content.getHeader( "Content-Disposition" );
661                    String nameID = null;
662                    if ( names != null ) {
663                        for ( String name : names ) {
664                            ContentDisposition cd = new ContentDisposition( name );
665                            String nm = cd.getParameter( "name" );
666                            if ( nm != null ) {
667                                nameID = nm;
668                                break;
669                            }
670                        }
671                    }
672    
673                    if ( nameID == null ) {
674                        nameID = content.getContentID();
675                        if ( nameID == null ) {
676                            throw new OGCWebServiceException( "Exactly one 'name' parameter must be set in the header.",
677                                                              CSWExceptionCode.WRS_INVALIDREQUEST );
678                        }
679                    }
680                    LOG.logDebug( "Working with multipart (" + ( i + 1 ) + " of " + multi.getCount() + ") content name:"
681                                  + nameID );
682                    LOG.logDebug( "multipart (" + ( i + 1 ) + " of " + multi.getCount() + ") lineCount: "
683                                  + content.getLineCount() );
684                    LOG.logDebug( "multipart (" + ( i + 1 ) + " of " + multi.getCount() + ") encoding: "
685                                  + content.getEncoding() );
686                    if ( "Transaction".equalsIgnoreCase( nameID ) ) {
687                        InputStream contentIS = null;
688                        if ( !"UTF-8".equalsIgnoreCase( content.getEncoding() ) ) {
689                            contentIS = MimeUtility.decode( content.getInputStream(), content.getEncoding() );
690                        } else {
691                            contentIS = content.getInputStream();
692                        }
693    
694                        // first get an xml fragment of the request and check if it is a soap.
695                        XMLFragment doc = new XMLFragment( new InputStreamReader( contentIS ), XMLFragment.DEFAULT_URL );
696                        LOG.logDebug( "Decoded first multiPart:\n " + doc.getAsPrettyString() );
697                        soapHandler.setIncomingRequest( doc );
698    
699                        doc = soapHandler.createCSWRequestFromSOAP();
700                        Element rootElement = doc.getRootElement();
701    
702                        String ns = rootElement.getNamespaceURI();
703                        if ( !CommonNamespaces.CSWNS.toASCIIString().equals( ns )
704                             || !"Transaction".equals( rootElement.getLocalName() ) ) {
705                            // create the transaction
706                            LOG.logDebug( "The namespace of the root element (" + ns + ") is not"
707                                          + CommonNamespaces.CSWNS.toASCIIString()
708                                          + ", or the request isn't a Transaction so not creating a Transaction object. " );
709                            // set the first multipart to the multipart without the soap.
710                            content.setContent( doc.getAsPrettyString(), "application/xml" );
711                            return null;
712                        }
713                        trans = Transaction.create( "0", rootElement );
714    
715                        if ( this.proxyHandler != null ) {
716                            ServletRequestWrapper wrapper = new ServletRequestWrapper( request );
717                            ByteArrayOutputStream bos = new ByteArrayOutputStream( 50000 );
718                            OutputStreamWriter osw = new OutputStreamWriter( bos, CharsetUtils.getSystemCharset() );
719                            doc.write( osw );
720                            wrapper.setInputStreamAsByteArray( bos.toByteArray() );
721                            authorizeRequest( soapHandler.getUserName(), soapHandler.getPassword(), trans, wrapper );
722                        } else {
723                            LOG.logDebug( "the proxyHandler is not defined so no user Authentification." );
724                        }
725    
726                        List<Operation> ops = trans.getOperations();
727                        // find all the ids which were referenced by the transaction.
728                        int countInserts = 0;
729                        for ( Operation op : ops ) {
730    
731                            if ( "Insert".equalsIgnoreCase( op.getName() ) ) {
732    
733                                Map<String, Element> objects = ( (Insert) op ).getExtrinsicObjects();
734                                Set<String> keys = objects.keySet();
735                                LOG.logDebug( "This insert operation (" + ++countInserts
736                                              + ") contains the following keys: " + keys );
737                                // set the keys to the operations they were referenced in.
738                                for ( String key : keys ) {
739                                    if ( insertOpContainingExtObj.containsKey( key ) ) {
740                                        throw new OGCWebServiceException( "The following key is not unique, " + key
741                                                                          + ", the transaction therefore failed.",
742                                                                          CSWExceptionCode.WRS_INVALIDREQUEST );
743                                    }
744                                    insertOpContainingExtObj.put( key, (Insert) op );
745                                }
746                            }
747                        }
748                    } else {
749                        if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
750                            InputStream contentIS = null;
751    
752                            if ( !"UTF-8".equalsIgnoreCase( content.getEncoding() ) ) {
753                                contentIS = MimeUtility.decode( content.getInputStream(), content.getEncoding() );
754                            } else {
755                                contentIS = content.getInputStream();
756                            }
757    
758                            BufferedReader reader = new BufferedReader( new InputStreamReader( contentIS ) );
759                            String firstLine = reader.readLine();
760                            if ( !reader.ready() || firstLine == null ) {
761                                LOG.logInfo( "CSW (Ebrim) Insert-Filter: no characters found in multipart, is this an error?" );
762                            }
763                            LOG.logDebug( "first line of multipart: " + firstLine );
764                        }
765                        LOG.logDebug( "found Keys: " + insertOpContainingExtObj.keySet() );
766                        if ( insertOpContainingExtObj.size() > 0 ) {
767                            if ( insertOpContainingExtObj.containsKey( nameID ) ) {
768                                LOG.logDebug( "found the key to insert (" + nameID
769                                              + ") trying to find associated multipart." );
770                                Insert insertOperation = insertOpContainingExtObj.remove( nameID );
771                                Map<String, Element> extrinsicObjects = insertOperation.getExtrinsicObjects();
772                                // find the extrinsicobject element and add the multipartmime type
773                                // containing it's id.
774                                if ( extrinsicObjects.containsKey( nameID ) ) {
775                                    List<Element> records = insertOperation.getRecords();
776                                    Element extrinsicObject = extrinsicObjects.get( nameID );
777                                    if ( records.contains( extrinsicObject ) ) {
778                                        int objectIndex = records.indexOf( extrinsicObject );
779                                        LOG.logDebug( "removing extrinsicObject at index: " + objectIndex
780                                                      + " from the records list" );
781                                        records.remove( extrinsicObject );
782                                        Element handledExtrinsicObject = addDescribedExtrinsicObject( content,
783                                                                                                      extrinsicObject );
784                                        if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
785                                            XMLFragment testFrag = new XMLFragment( handledExtrinsicObject );
786                                            LOG.logDebug( "The extrinsicObject after the insertion of the multipart: "
787                                                          + testFrag.getAsPrettyString() );
788                                        }
789                                        records.add( objectIndex, handledExtrinsicObject );
790                                        // records.add( handledExtrinsicObject );
791                                    } else {
792                                        LOG.logDebug( "Following extrinsicObject was not found in the records list, this cannot be!: "
793                                                      + extrinsicObject.toString() );
794                                    }
795                                } else {
796                                    LOG.logDebug( "The given id: "
797                                                  + nameID
798                                                  + " was not found in the list of extrinsicObjects in the given InsertOperation, this cannot be!" );
799                                }
800                            }
801                        } else {
802                            throw new OGCWebServiceException( "Some mime multiparts remain, but there are no more "
803                                                              + "referenced ids, something is wrong with the transaction",
804                                                              CSWExceptionCode.WRS_INVALIDREQUEST );
805                        }
806                    }
807                }
808            } catch ( IOException ioe ) {
809                throw new OGCWebServiceException( "Can't handle your insert request, because an IOException "
810                                                  + "(with following message occured) while handling the mime multiparts:"
811                                                  + ioe.getMessage(), CSWExceptionCode.WRS_INVALIDREQUEST );
812            } catch ( MessagingException me ) {
813                throw new OGCWebServiceException( "Can't handle your insert request, because a MessagingException "
814                                                  + "(with following message occured) while handling the mime multiparts:"
815                                                  + me.getMessage(), CSWExceptionCode.WRS_INVALIDREQUEST );
816            } catch ( XMLException xmle ) {
817                throw new OGCWebServiceException( "Can't handle your insert request, because an XMLException (with "
818                                                  + "following message occured) while handling the mime multiparts:"
819                                                  + xmle.getMessage(), CSWExceptionCode.WRS_INVALIDREQUEST );
820            } catch ( SAXException saxe ) {
821                throw new OGCWebServiceException( "Can't handle your insert request, because an SAXException (with "
822                                                  + "following message) occured while handling the mime multiparts:"
823                                                  + saxe.getMessage(), CSWExceptionCode.WRS_INVALIDREQUEST );
824            }
825            // finished so lets give back the transaction as a result.
826            return trans;
827        }
828    
829        /**
830         * Adds an deegreecsw:DescribedObject to the rim:EextrinsicObject, which will contain the xml encoded object given
831         * in the MimeBodyPart.
832         *
833         * @param content
834         *            holding the described object
835         * @param extrinsicObject
836         *            which will receive the deegreecsw:DescribedObject
837         * @return the modified extrinsicObject xml-Element.
838         * @throws OGCWebServiceException
839         * @throws MessagingException
840         */
841        private Element addDescribedExtrinsicObject( MimeBodyPart content, Element extrinsicObject )
842                                throws OGCWebServiceException, MessagingException {
843    
844            String contentType = content.getContentType();
845            LOG.logDebug( "in the multipart, we found the contentType: " + contentType );
846            if ( contentType == null || !( contentType.contains( "application/xml" ) || contentType.contains( "text/xml" ) ) ) {
847                throw new OGCWebServiceException( "Other than xml-encoded data can not be handled in the multiparts",
848                                                  CSWExceptionCode.WRS_INVALIDREQUEST );
849            }
850            try {
851                InputStream contentIS = null;
852                if ( !"UTF-8".equalsIgnoreCase( content.getEncoding() ) ) {
853                    contentIS = MimeUtility.decode( content.getInputStream(), content.getEncoding() );
854                } else {
855                    contentIS = content.getInputStream();
856                }
857    
858                BufferedReader reader = new BufferedReader( new InputStreamReader( contentIS ) );
859                String firstLine = reader.readLine();
860                if ( !reader.ready() || firstLine == null ) {
861                    LOG.logInfo( "CSW (Ebrim) Insert-Filter: no request characters found, not handling request" );
862                }
863                StringBuffer sb = new StringBuffer();
864                while ( firstLine != null ) {
865                    sb.append( firstLine );
866                    firstLine = reader.readLine();
867                }
868                String resultString = sb.toString();
869                LOG.logDebug( "content of multipart: " + resultString );
870    
871                // first delete the old element of the records list.
872                // XMLFragment doc = new XMLFragment( new InputStreamReader( contentIS ),
873                // XMLFragment.DEFAULT_URL );
874                Element describedObject = XMLTools.appendElement( extrinsicObject, CommonNamespaces.DEEGREECSW,
875                                                                  CommonNamespaces.DEEGREECSW_PREFIX + ":DescribedObject" );
876                CDATASection dataSection = describedObject.getOwnerDocument().createCDATASection( resultString );
877                LOG.logDebug( "content of multipart(after cdata encoding): " + dataSection.getWholeText() );
878                describedObject.appendChild( dataSection );
879                // Document ownerDoc = extrinsicObject.getOwnerDocument();
880                // Node newInsertNode = ownerDoc.importNode( doc.getRootElement(), true );
881                // describedObject.appendChild( newInsertNode );
882    
883            } catch ( IOException e ) {
884                throw new OGCWebServiceException( "An error occurred while processing a multipart, discarding",
885                                                  CSWExceptionCode.WRS_INVALIDREQUEST );
886            }
887            return extrinsicObject;
888        }
889    
890        private class ResponseObject {
891    
892            private final String userName;
893    
894            private final String password;
895    
896            private final boolean doResponseValidation;
897    
898            private final OGCWebServiceRequest originalRequest;
899    
900            ResponseObject( String userName, String password, boolean doResponseValidation,
901                            OGCWebServiceRequest originalRequest ) {
902                this.userName = userName;
903                this.password = password;
904                this.doResponseValidation = doResponseValidation;
905                this.originalRequest = originalRequest;
906            }
907    
908            /**
909             * @return the userName
910             */
911            public final String getUserName() {
912                return userName;
913            }
914    
915            /**
916             * @return the password
917             */
918            public final String getPassword() {
919                return password;
920            }
921    
922            /**
923             * @return the doResponseValidation
924             */
925            public final boolean doResponseValidation() {
926                return doResponseValidation;
927            }
928    
929            /**
930             * @return the originalRequest
931             */
932            public final OGCWebServiceRequest getOriginalRequest() {
933                return originalRequest;
934            }
935        }
936    
937        /**
938         * @return an instance of the catalogue service intiliazed in the init method.
939         */
940        public CatalogueService getCatalogueService() {
941            return cswService;
942        }
943    }