2011-07-21 7 views
1

RSS、Atom、および単純なHTMLファイルである異なる種類のコンテンツを処理できるはずのクローラ/パーサーを作成しています。正しいパーサーを決定するために、私はURLを取り、コンテンツタイプを検出しようとし、正しいパーサーを返すParseFactoryというクラスを作成しました。コンテンツに応じて異なるオブジェクトによって処理される入力ストリーム

残念ながら、URLConnectionで提供されているメソッドを使用してcontent-typeをチェックすることは、必ずしも機能しません。例えば、

String contentType = url.openConnection().getContentType(); 

は常に正しいコンテンツタイプを提供していません(例えば「text/htmlの」それはRSSでなければなりません)、またはRSSとAtomを区別することはできません(例:「アプリケーション/ xmlの"はAtomでもRSSフィードでもかまいません)。この問題を解決するために、私はInputStreamで手がかりを探し始めました。問題は、私がInputStreamを一度しかダウンロードする必要がない、エレガントなクラスデザインに問題があることです。私の現在のデザインでは、正しいコンテンツタイプを決定する別のクラスを最初に書きました。次に、ParseFactoryはこの情報を使って対応するパーサーのインスタンスを作成します。これは、 'parse()'メソッドが呼び出されると、 InputStream全体が2度目です。

public Parser createParser(){ 

    InputStream inputStream = null; 
    String contentType = null; 
    String contentEncoding = null; 

    ContentTypeParser contentTypeParser = new ContentTypeParser(this.url); 
    Parser parser = null; 

    try { 

     inputStream = new BufferedInputStream(this.url.openStream()); 
     contentTypeParser.parse(inputStream); 
     contentType = contentTypeParser.getContentType(); 
     contentEncoding = contentTypeParser.getContentEncoding(); 

     assert (contentType != null); 

     inputStream = new BufferedInputStream(this.url.openStream()); 

     if (contentType.equals(ContentTypes.rss)) 
     { 
      logger.info("RSS feed detected"); 
      parser = new RssParser(this.url); 
      parser.parse(inputStream); 
     } 
     else if (contentType.equals(ContentTypes.atom)) 
     { 
      logger.info("Atom feed detected"); 
      parser = new AtomParser(this.url); 
     } 
     else if (contentType.equals(ContentTypes.html)) 
     { 
      logger.info("html detected"); 
      parser = new HtmlParser(this.url); 
      parser.setContentEncoding(contentEncoding); 
     } 
     else if (contentType.equals(ContentTypes.UNKNOWN)) 
      logger.debug("Unable to recognize content type"); 

     if (parser != null) 
      parser.parse(inputStream); 

    } catch (IOException e) { 
     e.printStackTrace(); 
    } finally { 
     try { 
      inputStream.close(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 

    return parser; 

} 

は基本的に、私は私が第二 "のinputStream =新しいBufferedInputStreamを(this.url.openStream())" を排除することを可能にするソリューションを探しています。

ご協力いただければ幸いです!

注釈1:完全な目的のために、URLConnection.guessContentTypeFromStream(inputStream)メソッドを使用してみましたが、これはあまりにも頻繁にnullを返します。

サイドノート2:XMLパーサー(AtomとRss)は、JsoupのHtmlパーサーであるSAXParserに基づいています。

答えて

1

markresetに電話することはできますか?

inputStream = new BufferedInputStream(this.url.openStream()); 
inputStream.mark(2048); // Or some other sensible number 

contentTypeParser.parse(inputStream); 
contentType = contentTypeParser.getContentType(); 
contentEncoding = contentTypeParser.getContentEncoding(); 

inputstream.reset(); // Let the parser have a crack at it now 
+0

いいえ、この方法も試しましたが、残念ながらこの方法は機能しません。私はIOException( "ストリームクローズ")を取得し続ける – jerraes

+0

@ jerraes:例外はどこで手に入りますか?あなたのContentTypeParserはストリームを閉じていますか?質問内で間違ったことの詳細*を試したことを含めてください。 –

+0

いいえ、ContentTypeParserはストリームを閉じません。ストリームは、ParserFactory内の「createParser()」メソッドの最後でのみ閉じられます。私は以前のコメントで少し急いでいた。マークを付けると、次のエラーが発生します。java.io.IOException:無効なマークにリセットする \t java.io.BufferedInputStream.reset(BufferedInputStream.java:416) \t at dataAcquisition.Parser.ParseFactory.createParser(ParseFactory .java:スレッド65)run.Main.main(Main.java:45で \t) 例外 "メイン" java.lang.NullPointerExceptionが run.Main.mainで\t(Main.java:46) – jerraes

0

おそらく、あなたのContentTypeParser内部コンテンツをキャッシュし、InputStreamから再取得するデータの代わりにappropiate ContentParserにそれを養う必要があります。

+0

これは文字列で現金化することを意味しますか?思ったことだけど、SAXParserで動作させるためには、このStringをInputStreamに変換し直さなければならないということです。私はそれが他の方法で動作させることができない場合、これはまだ最良の選択肢だと思います。 – jerraes

+0

'String'を' InputStream'に変換するのはとても簡単です: 'InputStream is = new ByteArrayInputStream(str.getBytes(" UTF-8 "));' しかし、無効化限度を過ぎる以外は、無効なマークを取得するその他の理由はありません。http://download.oracle.com/javase/6/docs/api/java/io/InputStream.html#mark%28int%29 – Vlad

関連する問題