001    //$HeadURL: svn+ssh://developername@svn.wald.intevation.org/deegree/base/trunk/resources/eclipse/files_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    package org.deegree.tools.metadata;
037    
038    import java.io.BufferedReader;
039    
040    import java.io.File;
041    import java.io.FileWriter;
042    import java.io.IOException;
043    import java.io.InputStream;
044    import java.io.PrintWriter;
045    import java.io.StringReader;
046    import java.net.MalformedURLException;
047    import java.text.MessageFormat;
048    import java.util.List;
049    import java.util.Properties;
050    import java.util.Random;
051    
052    import javax.swing.text.AttributeSet;
053    import javax.swing.text.Element;
054    import javax.swing.text.ElementIterator;
055    import javax.swing.text.StyleConstants;
056    import javax.swing.text.html.HTML;
057    import javax.swing.text.html.HTMLDocument;
058    import javax.swing.text.html.HTMLEditorKit;
059    import javax.swing.text.html.parser.ParserDelegator;
060    
061    import org.apache.commons.httpclient.HttpMethod;
062    import org.apache.http.HttpResponse;
063    import org.apache.http.client.HttpClient;
064    import org.apache.http.client.methods.HttpPost;
065    import org.apache.http.entity.mime.MultipartEntity;
066    import org.apache.http.entity.mime.content.FileBody;
067    import org.apache.http.impl.client.DefaultHttpClient;
068    import org.apache.http.message.BasicHttpResponse;
069    import org.deegree.framework.log.ILogger;
070    import org.deegree.framework.log.LoggerFactory;
071    import org.deegree.framework.util.BootLogger;
072    import org.deegree.framework.util.FileUtils;
073    import org.deegree.framework.util.HttpUtils;
074    import org.deegree.framework.util.StringTools;
075    import org.deegree.framework.xml.NamespaceContext;
076    import org.deegree.framework.xml.XMLFragment;
077    import org.deegree.framework.xml.XMLTools;
078    import org.deegree.ogcbase.CommonNamespaces;
079    import org.w3c.dom.Node;
080    import org.xml.sax.SAXException;
081    
082    /**
083     * TODO add class documentation here
084     * 
085     * @author <a href="mailto:name@deegree.org">Andreas Poth</a>
086     * @author last edited by: $Author: poth $
087     * 
088     * @version $Revision: 1.3 $, $Date: 2011-03-09 07:41:56 $
089     */
090    public class InspireValidator {
091    
092        private static final ILogger LOG = LoggerFactory.getLogger( InspireValidator.class );
093    
094        private static NamespaceContext nsc = CommonNamespaces.getNamespaceContext();
095    
096        private String cswAddress;
097    
098        private String outputFile;
099    
100        private PrintWriter outputWriter;
101    
102        private XMLFragment query;
103    
104        /**
105         * 
106         * @param cswAddress
107         * @param outputFile
108         * @throws SAXException
109         * @throws IOException
110         * @throws MalformedURLException
111         */
112        public InspireValidator( String cswAddress, String outputFile ) throws MalformedURLException, IOException,
113                                SAXException {
114            this.outputFile = outputFile;
115            this.cswAddress = cswAddress;
116        }
117    
118        /**
119         * 
120         * @param cswAddress
121         * @param query
122         * @param outputFile
123         */
124        public InspireValidator( String cswAddress, XMLFragment query, String outputFile ) {
125            this.query = query;
126        }
127    
128        /**
129         * reads a number of random records from a CSW and validates them against INSPIRE metadata validator
130         * 
131         * @param mr
132         *            number of records to be tested
133         */
134        public void runRandomQuery( String mr ) {
135            int maxQueries = 50;
136            if ( mr != null ) {
137                maxQueries = Integer.parseInt( mr );
138                LOG.logInfo( Messages.get( "m1", mr ) );
139            } else {
140                LOG.logInfo( Messages.get( "m2", mr ) );
141            }
142            try {
143                int max = getNumberOfAvailableRecords();
144                if ( maxQueries > max ) {
145                    maxQueries = max;
146                    LOG.logInfo( Messages.get( "m3", max ) );
147                }
148                // create base GetRecords request without constraint but with startPosition and maxRecords = 1
149                String s = Messages.get( "getRecordsWithoutFilter" );
150                XMLFragment request = new XMLFragment( new StringReader( s ), XMLFragment.DEFAULT_URL );
151                Random random = new Random();
152                outputWriter = new PrintWriter( new File( outputFile ) );
153                for ( int i = 0; i < maxQueries; i++ ) {
154                    // set index of record to read
155                    Node node = XMLTools.getNode( request.getRootElement(), Messages.get( "xPathStartPosition" ), nsc );
156                    node.setNodeValue( Integer.toString( random.nextInt( max ) + 1 ) );
157    
158                    HttpMethod m = HttpUtils.performHttpPost( cswAddress, request, 60000, null, null, null );
159                    XMLFragment xml = new XMLFragment();
160                    xml.load( m.getResponseBodyAsStream(), cswAddress );
161                    List<org.w3c.dom.Element> list = XMLTools.getElements( xml.getRootElement(),
162                                                                           Messages.get( "xPathMetadata" ), nsc );
163                    System.out.println( Messages.get( "m4", i + 1, maxQueries ) );
164                    outputWriter.println( "---------------------------------------------------------------------" );
165                    String id = XMLTools.getNodeAsString( list.get( 0 ), Messages.get( "xPathIdentifier" ), nsc, "none" );
166                    outputWriter.println( Messages.get( "m5", id ) );
167                    validateINSPIRE( list.get( 0 ) );
168                }
169                System.out.println();
170            } catch ( Exception e ) {
171                LOG.logError( e );
172            } finally {
173                if ( outputWriter != null ) {
174                    outputWriter.flush();
175                    outputWriter.close();
176                }
177            }
178            System.out.println( Messages.get( "m6", new File( outputFile ).getAbsolutePath() ) );
179        }
180    
181        /**
182         * @return
183         * @throws IOException
184         * @throws SAXException
185         */
186        private int getNumberOfAvailableRecords()
187                                throws Exception {
188            String s = Messages.get( "getRecordsHits" );
189            XMLFragment xml = new XMLFragment( new StringReader( s ), XMLFragment.DEFAULT_URL );
190            HttpMethod m = HttpUtils.performHttpPost( cswAddress, xml, 60000, null, null, null );
191            XMLFragment result = new XMLFragment();
192            result.load( m.getResponseBodyAsStream(), cswAddress );
193            return XMLTools.getNodeAsInt( result.getRootElement(), Messages.get( "xPathNumberOfRecordsMatched" ), nsc, 0 );
194        }
195    
196        /**
197         * reads a records from a CSW defined by a GetRecord request and validates them against INSPIRE metadata validator
198         * 
199         * @param queryFile
200         *            reference to GetRecords request
201         */
202        public void runDefinedQuery( String queryFile ) {
203            try {
204                query = new XMLFragment( new File( queryFile ) );
205                HttpMethod m = HttpUtils.performHttpPost( cswAddress, query, 60000, null, null, null );
206                XMLFragment xml = new XMLFragment();
207                xml.load( m.getResponseBodyAsStream(), cswAddress );
208                List<org.w3c.dom.Element> list = XMLTools.getElements( xml.getRootElement(),
209                                                                       Messages.get( "xPathMetadata" ), nsc );
210                outputWriter = new PrintWriter( new File( outputFile ) );
211                int cnt = 1;
212                for ( org.w3c.dom.Element element : list ) {
213                    System.out.println( Messages.get( "m7", cnt, list.size() ) );
214                    outputWriter.println( "---------------------------------------------------------------------" );
215                    String id = XMLTools.getNodeAsString( element, Messages.get( "xPathIdentifier" ), nsc, "none" );
216                    outputWriter.println( Messages.get( "m8", id ) );
217                    validateINSPIRE( element );
218                    cnt++;
219                }
220                System.out.println();
221            } catch ( Exception e ) {
222                LOG.logError( e );
223            } finally {
224                if ( outputWriter != null ) {
225                    outputWriter.flush();
226                    outputWriter.close();
227                }
228            }
229            System.out.println( "report stored at: " + new File( outputFile ).getAbsolutePath() );
230        }
231    
232        /**
233         * 
234         * @param fileName
235         *            name of a file containing a metadata record to be validated
236         */
237        public void runISOFile( String fileName ) {
238            try {
239                XMLFragment xml = new XMLFragment( new File( fileName ) );
240                outputWriter = new PrintWriter( new File( outputFile ) );
241                outputWriter.println( "---------------------------------------------------------------------" );
242                outputWriter.println( Messages.get( "m9", fileName ) );
243                validateINSPIRE( xml.getRootElement() );
244            } catch ( Exception e ) {
245                LOG.logError( e );
246            } finally {
247                if ( outputWriter != null ) {
248                    outputWriter.flush();
249                    outputWriter.close();
250                }
251            }
252        }
253    
254        /**
255         * 
256         * @param dirName
257         *            name of the directory containing metadata records to be validated
258         */
259        public void runISOFileDirectory( String dirName ) {
260            try {
261                File dir = new File( dirName );            
262                File[] files = dir.listFiles();
263                outputWriter = new PrintWriter( new File( outputFile ) );
264                outputWriter.println( Messages.get( "m10", dir.getAbsolutePath() ) );
265                for ( File file : files ) {
266                    XMLFragment xml = new XMLFragment( file );
267                    outputWriter.println( "---------------------------------------------------------------------" );
268                    outputWriter.println( Messages.get( "m11", file.getAbsolutePath() ) );
269                    System.out.println( Messages.get( "m11", file.getAbsolutePath() ) );
270                    validateINSPIRE( xml.getRootElement() );
271                }
272            } catch ( Exception e ) {
273                LOG.logError( e );
274            } finally {
275                if ( outputWriter != null ) {
276                    outputWriter.flush();
277                    outputWriter.close();
278                }
279            }
280        }
281    
282        /**
283         * main method to validate a metadata record using INSPIRE metadata validator
284         * 
285         * @param element
286         * @throws Exception
287         */
288        private void validateINSPIRE( org.w3c.dom.Element element )
289                                throws Exception {
290            HttpClient httpclient = new DefaultHttpClient();
291    
292            HttpPost httpPost = new HttpPost( Messages.get( "validator" ) );
293            // xml response: httpPost.addHeader("Accept", "application/xml");
294            // html response
295            httpPost.addHeader( "Accept", "text/html" );
296            XMLFragment xml = new XMLFragment( element );
297            File tmp = new File( "tmp.xml" );
298            tmp.deleteOnExit();
299            xml.write( new FileWriter( tmp ) );
300            FileBody dataFile = new FileBody( new File( "tmp.xml" ) );
301            MultipartEntity reqEntity = new MultipartEntity();
302            reqEntity.addPart( "dataFile", dataFile );
303            httpPost.setEntity( reqEntity );
304    
305            HttpResponse response = httpclient.execute( httpPost );
306            parseServiceResponse( response );
307    
308        }
309    
310        /**
311         * parse INSPIRE metadata validator response and print out result onto the console
312         * 
313         * @param response
314         * @throws IOException
315         * @throws IllegalStateException
316         */
317        private void parseServiceResponse( HttpResponse response )
318                                throws Exception {
319            String s = FileUtils.readTextFile( ( (BasicHttpResponse) response ).getEntity().getContent() ).toString();
320            if ( response.getStatusLine().getStatusCode() != 200 ) {
321                outputWriter.println( s );
322                outputWriter.println();
323                return;
324            }
325            s = "<html><head></head><body>" + s + "</body></html>";
326            BufferedReader br = new BufferedReader( new StringReader( s ) );
327    
328            HTMLEditorKit htmlKit = new HTMLEditorKit();
329            HTMLDocument htmlDoc = (HTMLDocument) htmlKit.createDefaultDocument();
330            HTMLEditorKit.Parser parser = new ParserDelegator();
331            HTMLEditorKit.ParserCallback callback = htmlDoc.getReader( 0 );
332            parser.parse( br, callback, true );
333    
334            // Parse
335            ElementIterator iterator = new ElementIterator( htmlDoc );
336            Element element;
337            while ( ( element = iterator.next() ) != null ) {
338                AttributeSet attributes = element.getAttributes();
339                Object name = attributes.getAttribute( StyleConstants.NameAttribute );
340                if ( ( name instanceof HTML.Tag ) && ( ( name == HTML.Tag.IMPLIED ) ) ) {
341                    // Build up content text as it may be within multiple elements
342                    StringBuffer text = new StringBuffer();
343                    int count = element.getElementCount();
344                    for ( int i = 0; i < count; i++ ) {
345                        Element child = element.getElement( i );
346                        AttributeSet childAttributes = child.getAttributes();
347                        if ( childAttributes.getAttribute( StyleConstants.NameAttribute ) == HTML.Tag.CONTENT ) {
348                            int startOffset = child.getStartOffset();
349                            int endOffset = child.getEndOffset();
350                            int length = endOffset - startOffset;
351                            text.append( htmlDoc.getText( startOffset, length ) );
352                        }
353                    }
354                    outputWriter.println( text.toString() );
355                }
356            }
357            outputWriter.println( "---------------------------------------------------------------------" );
358            outputWriter.println();
359        }
360    
361        /**
362         * validate input parameter
363         * 
364         * @param map
365         * @throws Exception
366         */
367        private static void validate( Properties map )
368                                throws Exception {
369    //        if ( map.get( "-cswAddress" ) == null ) {
370    //            throw new Exception( "-cswAddress parameter must be set" );
371    //        }
372            if ( map.get( "-outFile" ) == null ) {
373                map.put( "-outFile", "validation_result.txt" );
374            }
375    
376        }
377    
378        public static void main( String[] args )
379                                throws Exception {
380            Properties map = new Properties();
381            for ( int i = 0; i < args.length; i += 2 ) {
382                map.put( args[i], args[i + 1] );
383            }
384            try {
385                validate( map );
386            } catch ( Exception e ) {
387                LOG.logError( "!!! E R R O R !!!", e );
388                return;
389            }
390            LOG.logInfo( Messages.get( "m16" ) );
391            StringTools.printMap( map, System.out );
392            InspireValidator iv = new InspireValidator( map.getProperty( "-cswAddress" ), map.getProperty( "-outFile" ) );
393            if ( map.get( "-queryFile" ) != null ) {
394                LOG.logInfo( Messages.get( "m12" ), map.get( "-queryFile" ) );
395                iv.runDefinedQuery( map.getProperty( "-queryFile" ) );
396            } else if ( map.get( "-isoFile" ) != null ) {
397                LOG.logInfo( Messages.get( "m13" ), map.get( "-isoFile" ) );
398                iv.runISOFile( map.getProperty( "-isoFile" ) );
399            } else if ( map.get( "-directory" ) != null ) {
400                LOG.logInfo( Messages.get( "m14" ), map.get( "-directory" ) );
401                iv.runISOFileDirectory( map.getProperty( "-directory" ) );
402            } else {
403                LOG.logInfo( Messages.get( "m15" ) );
404                iv.runRandomQuery( map.getProperty( "-maxRecords" ) );
405            }
406        }
407    
408        /**
409         * 
410         * TODO add class documentation here
411         * 
412         * @author <a href="mailto:name@deegree.org">Andreas Poth</a>
413         * @author last edited by: $Author: poth $
414         * 
415         * @version $Revision: 1.3 $, $Date: 2011-03-09 07:41:56 $
416         */
417        private static class Messages {
418    
419            private static Properties props = new Properties();
420    
421            /**
422             * Initialization done at class loading time.
423             */
424            static {
425                try {
426                    InputStream is = InspireValidator.class.getResourceAsStream( "InspireValidator.properties" );
427                    props.load( is );
428                    is.close();
429                } catch ( IOException e ) {
430                    BootLogger.logError( "Error while initializing " + Messages.class.getName() + " : " + e.getMessage(), e );
431                }
432            }
433    
434            static String get( String key, Object... args ) {
435                String s = (String) props.get( key );
436                if ( s != null ) {
437                    return MessageFormat.format( s, args );
438                }
439    
440                return "$Message with key: " + key + " not found$";
441            }
442        }
443    
444    }