diff --git a/src/de/schildbach/pte/AbstractEfaProvider.java b/src/de/schildbach/pte/AbstractEfaProvider.java index 751d809c..24485a44 100644 --- a/src/de/schildbach/pte/AbstractEfaProvider.java +++ b/src/de/schildbach/pte/AbstractEfaProvider.java @@ -19,7 +19,8 @@ package de.schildbach.pte; import java.io.FileNotFoundException; import java.io.IOException; -import java.io.StringReader; +import java.io.InputStream; +import java.net.SocketTimeoutException; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; @@ -54,20 +55,24 @@ import de.schildbach.pte.util.XmlPullUtil; */ public abstract class AbstractEfaProvider implements NetworkProvider { + private static final String DEFAULT_ENCODING = "ISO-8859-1"; + protected abstract String autocompleteUri(final CharSequence constraint); public List autocompleteStations(final CharSequence constraint) throws IOException { final String uri = autocompleteUri(constraint); + InputStream is = null; try { - final CharSequence page = ParserUtils.scrape(uri); + is = ParserUtils.scrapeInputStream(uri); + final List results = new ArrayList(); final XmlPullParserFactory factory = XmlPullParserFactory.newInstance(System.getProperty(XmlPullParserFactory.PROPERTY_NAME), null); final XmlPullParser pp = factory.newPullParser(); - pp.setInput(new StringReader(page.toString())); + pp.setInput(is, DEFAULT_ENCODING); // parse odv name elements XmlPullUtil.jumpToStartTag(pp, null, "itdOdv"); @@ -99,6 +104,15 @@ public abstract class AbstractEfaProvider implements NetworkProvider { throw new RuntimeException(x); } + catch (final SocketTimeoutException x) + { + throw new RuntimeException(x); + } + finally + { + if (is != null) + is.close(); + } } private Location processOdvNameElem(final XmlPullParser pp) throws XmlPullParserException, IOException @@ -168,8 +182,6 @@ public abstract class AbstractEfaProvider implements NetworkProvider return new Location(LocationType.STATION, id, lat, lon, name); } - private static final Pattern P_NEARBY_MESSAGES = Pattern.compile("(unsere Server zur Zeit ausgelastet)"); - protected abstract String nearbyLatLonUri(int lat, int lon); protected abstract String nearbyStationUri(String stationId); @@ -185,16 +197,14 @@ public abstract class AbstractEfaProvider implements NetworkProvider if (uri == null) throw new IllegalArgumentException("at least one of stationId or lat/lon must be given"); + InputStream is = null; try { - final CharSequence page = ParserUtils.scrape(uri); - - if (P_NEARBY_MESSAGES.matcher(page).find()) - return new NearbyStationsResult(uri, NearbyStationsResult.Status.SERVICE_DOWN); + is = ParserUtils.scrapeInputStream(uri); final XmlPullParserFactory factory = XmlPullParserFactory.newInstance(System.getProperty(XmlPullParserFactory.PROPERTY_NAME), null); final XmlPullParser pp = factory.newPullParser(); - pp.setInput(new StringReader(page.toString())); + pp.setInput(is, DEFAULT_ENCODING); XmlPullUtil.jumpToStartTag(pp, null, "itdOdvName"); final String nameState = pp.getAttributeValue(null, "state"); @@ -274,6 +284,15 @@ public abstract class AbstractEfaProvider implements NetworkProvider { return new NearbyStationsResult(uri, NearbyStationsResult.Status.SERVICE_DOWN); } + catch (final SocketTimeoutException x) + { + return new NearbyStationsResult(uri, NearbyStationsResult.Status.SERVICE_DOWN); + } + finally + { + if (is != null) + is.close(); + } } private static final Pattern P_LINE_IRE = Pattern.compile("IRE\\d+"); @@ -533,13 +552,14 @@ public abstract class AbstractEfaProvider implements NetworkProvider public QueryDeparturesResult queryDepartures(final String uri) throws IOException { + InputStream is = null; try { - final CharSequence page = ParserUtils.scrape(uri); + is = ParserUtils.scrapeInputStream(uri); final XmlPullParserFactory factory = XmlPullParserFactory.newInstance(System.getProperty(XmlPullParserFactory.PROPERTY_NAME), null); final XmlPullParser pp = factory.newPullParser(); - pp.setInput(new StringReader(page.toString())); + pp.setInput(is, DEFAULT_ENCODING); XmlPullUtil.jumpToStartTag(pp, null, "itdOdvName"); final String nameState = pp.getAttributeValue(null, "state"); @@ -602,6 +622,15 @@ public abstract class AbstractEfaProvider implements NetworkProvider { return new QueryDeparturesResult(uri, QueryDeparturesResult.Status.SERVICE_DOWN); } + catch (final SocketTimeoutException x) + { + return new QueryDeparturesResult(uri, QueryDeparturesResult.Status.SERVICE_DOWN); + } + finally + { + if (is != null) + is.close(); + } } private void processItdDateTime(final XmlPullParser pp, final Calendar calendar) throws XmlPullParserException, IOException @@ -654,25 +683,49 @@ public abstract class AbstractEfaProvider implements NetworkProvider { final String uri = connectionsQueryUri(from, via, to, date, dep, products, walkSpeed) + "&sessionID=0"; - final CharSequence page = ParserUtils.scrape(uri); - - return queryConnections(uri, page); + InputStream is = null; + try + { + is = ParserUtils.scrapeInputStream(uri); + return queryConnections(uri, is); + } + catch (final SocketTimeoutException x) + { + return new QueryConnectionsResult(QueryConnectionsResult.Status.SERVICE_DOWN); + } + finally + { + if (is != null) + is.close(); + } } public QueryConnectionsResult queryMoreConnections(final String uri) throws IOException { - final CharSequence page = ParserUtils.scrape(uri); - - return queryConnections(uri, page); + InputStream is = null; + try + { + is = ParserUtils.scrapeInputStream(uri); + return queryConnections(uri, is); + } + catch (final SocketTimeoutException x) + { + return new QueryConnectionsResult(QueryConnectionsResult.Status.SERVICE_DOWN); + } + finally + { + if (is != null) + is.close(); + } } - private QueryConnectionsResult queryConnections(final String uri, final CharSequence page) throws IOException + private QueryConnectionsResult queryConnections(final String uri, final InputStream is) throws IOException { try { final XmlPullParserFactory factory = XmlPullParserFactory.newInstance(System.getProperty(XmlPullParserFactory.PROPERTY_NAME), null); final XmlPullParser pp = factory.newPullParser(); - pp.setInput(new StringReader(page.toString())); + pp.setInput(is, DEFAULT_ENCODING); if (!XmlPullUtil.jumpToStartTag(pp, null, "itdRequest")) throw new IllegalStateException("cannot find "); diff --git a/src/de/schildbach/pte/dto/QueryConnectionsResult.java b/src/de/schildbach/pte/dto/QueryConnectionsResult.java index ba7e0a03..11571f3b 100644 --- a/src/de/schildbach/pte/dto/QueryConnectionsResult.java +++ b/src/de/schildbach/pte/dto/QueryConnectionsResult.java @@ -27,7 +27,7 @@ public final class QueryConnectionsResult implements Serializable { public enum Status { - OK, AMBIGUOUS, TOO_CLOSE, NO_CONNECTIONS, INVALID_DATE; + OK, AMBIGUOUS, TOO_CLOSE, NO_CONNECTIONS, INVALID_DATE, SERVICE_DOWN; } public static final QueryConnectionsResult TOO_CLOSE = new QueryConnectionsResult(Status.TOO_CLOSE); diff --git a/src/de/schildbach/pte/util/ParserUtils.java b/src/de/schildbach/pte/util/ParserUtils.java index 5c454d11..afd7bd53 100644 --- a/src/de/schildbach/pte/util/ParserUtils.java +++ b/src/de/schildbach/pte/util/ParserUtils.java @@ -18,6 +18,7 @@ package de.schildbach.pte.util; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.Reader; @@ -55,19 +56,19 @@ public final class ParserUtils stateCookie = null; } - public static CharSequence scrape(final String url) throws IOException + public static final CharSequence scrape(final String url) throws IOException { return scrape(url, false, null, null, false); } - public static CharSequence scrape(final String url, final boolean isPost, final String request, String encoding, final boolean cookieHandling) - throws IOException + public static final CharSequence scrape(final String url, final boolean isPost, final String request, String encoding, + final boolean cookieHandling) throws IOException { return scrape(url, isPost, request, encoding, cookieHandling, 3); } - public static CharSequence scrape(final String url, final boolean isPost, final String request, String encoding, final boolean cookieHandling, - int tries) throws IOException + public static final CharSequence scrape(final String url, final boolean isPost, final String request, String encoding, + final boolean cookieHandling, int tries) throws IOException { if (encoding == null) encoding = SCRAPE_DEFAULT_ENCODING; @@ -149,7 +150,7 @@ public final class ParserUtils } } - private static long copy(final Reader reader, final StringBuilder builder) throws IOException + private static final long copy(final Reader reader, final StringBuilder builder) throws IOException { final char[] buffer = new char[SCRAPE_INITIAL_CAPACITY]; long count = 0; @@ -162,6 +163,21 @@ public final class ParserUtils return count; } + public static final InputStream scrapeInputStream(final String url) throws IOException + { + final HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); + + connection.setDoInput(true); + connection.setDoOutput(false); + connection.setConnectTimeout(SCRAPE_CONNECT_TIMEOUT); + connection.setReadTimeout(SCRAPE_READ_TIMEOUT); + connection.addRequestProperty("User-Agent", SCRAPE_USER_AGENT); + // workaround to disable Vodafone compression + connection.addRequestProperty("Cache-Control", "no-cache"); + + return connection.getInputStream(); + } + private static final Pattern P_ENTITY = Pattern.compile("&(?:#(x[\\da-f]+|\\d+)|(amp|quot|apos));"); public static String resolveEntities(final CharSequence str)