Migrated to the Apache HttpComponents library

This commit is contained in:
Vhati 2018-01-06 10:30:30 -05:00
parent 8f55580ed2
commit 63f36b31cc
9 changed files with 111 additions and 76 deletions

View file

@ -59,6 +59,11 @@
<artifactId>jdom2</artifactId>
<version>2.0.6</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.4</version>
</dependency>
<dependency>
<groupId>info.picocli</groupId>
<artifactId>picocli</artifactId>

View file

@ -32,6 +32,9 @@ To build, run "mvn clean package" in this folder.
This project depends on the following libraries.
- Apache HttpComponents
https://hc.apache.org/
(For JavaDocs, click HttpCore or HttpClient, then again under "Project reports".)
- Jackson JSON Processor 2.x
http://jackson.codehaus.org/Home
(For JavaDocs, look right.)

View file

@ -23,6 +23,7 @@
"Disabled XML escaping when reencoding to ensure invalid chars cause an error",
"Changed logging framework to SLF4J/Logback",
"Changed command line parser to picocli",
"Migrated to the Apache HttpComponents library",
"Made launcher scripts on OSX find java the recommended way"
]
},

View file

@ -9,6 +9,7 @@ Changelog
- Added a Validate warning about FTL 1.5.13 for chars outside windows-1252
- Changed logging framework to SLF4J/Logback
- Changed command line parser to picocli
- Migrated to the Apache HttpComponents library
- Made launcher scripts on OSX find java the recommended way
1.9:

View file

@ -13,9 +13,9 @@ import net.vhati.modmanager.core.ComparableVersion;
*/
public class AutoUpdateInfo {
private ComparableVersion latestVersion = null;
private Map<String,String> latestURLs = new TreeMap<String,String>();
private Map<String, String> latestURLs = new TreeMap<String, String>();
private String notice = null;
private Map<ComparableVersion,List<String>> changelog = new TreeMap<ComparableVersion,List<String>>( Collections.reverseOrder() );
private Map<ComparableVersion, List<String>> changelog = new TreeMap<ComparableVersion, List<String>>( Collections.reverseOrder() );
public void setLatestVersion( ComparableVersion version ) {
@ -26,7 +26,6 @@ public class AutoUpdateInfo {
return latestVersion;
}
public void setNotice( String s ) {
notice = s;
}
@ -35,7 +34,6 @@ public class AutoUpdateInfo {
return notice;
}
public void putLatestURL( String os, String url ) {
latestURLs.put( os, url );
}
@ -44,13 +42,11 @@ public class AutoUpdateInfo {
changelog.put( version, changeList );
}
public Map<String,String> getLatestURLs() {
public Map<String, String> getLatestURLs() {
return latestURLs;
}
public Map<ComparableVersion,List<String>> getChangelog() {
public Map<ComparableVersion, List<String>> getChangelog() {
return changelog;
}
}

View file

@ -12,11 +12,9 @@ import java.util.regex.Pattern;
* It is composed of three parts:
* - A series of period-separated positive ints.
*
* - The numbers may be immediately followed by a short
* suffix string.
* - The numbers may be immediately followed by a short suffix string.
*
* - Finally, a string comment, separated from the rest
* by a space.
* - Finally, a string comment, separated from the rest by a space.
*
* The (numbers + suffix) or comment may appear alone.
*
@ -148,16 +146,14 @@ public class ComparableVersion implements Comparable<ComparableVersion> {
Matcher m = suffixPtn.matcher( s );
if ( m.matches() ) {
suffix = s;
// Matched groups 1 and 2... or 3.
if ( m.group(1) != null ) {
if ( m.group( 1 ) != null ) {
suffixDivider = m.group( 1 );
}
if ( m.group(2) != null ) {
suffixNum = Integer.parseInt( m.group(2) );
if ( m.group( 2 ) != null ) {
suffixNum = Integer.parseInt( m.group( 2 ) );
} else {
suffixNum = -1;
}

View file

@ -3,7 +3,6 @@ package net.vhati.modmanager.json;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@ -28,10 +27,11 @@ public class JacksonAutoUpdateReader {
public static AutoUpdateInfo parse( File jsonFile ) {
AutoUpdateInfo aui = new AutoUpdateInfo();
Exception exception = null;
try {
AutoUpdateInfo aui = new AutoUpdateInfo();
ObjectMapper mapper = new ObjectMapper();
mapper.configure( JsonParser.Feature.ALLOW_SINGLE_QUOTES, true );
mapper.setVisibility( PropertyAccessor.FIELD, Visibility.ANY );
@ -43,9 +43,9 @@ public class JacksonAutoUpdateReader {
JsonNode latestNode = historyNode.get( "latest" );
aui.setLatestVersion( new ComparableVersion( latestNode.get( "version" ).textValue() ) );
Iterator<Map.Entry<String,JsonNode>> fieldIt = latestNode.get( "urls" ).fields();
Iterator<Map.Entry<String, JsonNode>> fieldIt = latestNode.get( "urls" ).fields();
while ( fieldIt.hasNext() ) {
Map.Entry<String,JsonNode> entry = fieldIt.next();
Map.Entry<String, JsonNode> entry = fieldIt.next();
aui.putLatestURL( entry.getKey(), entry.getValue().textValue() );
}
@ -67,6 +67,8 @@ public class JacksonAutoUpdateReader {
}
aui.putChanges( new ComparableVersion( releaseVersion ), changeList );
}
return aui;
}
catch ( JsonProcessingException e ) {
exception = e;
@ -76,9 +78,8 @@ public class JacksonAutoUpdateReader {
}
if ( exception != null ) {
log.error( "Failed to parse info about available updates", exception );
return null;
}
return aui;
return null;
}
}

View file

@ -23,10 +23,11 @@ public class JacksonCatalogReader {
public static ModDB parse( File jsonFile ) {
ModDB modDB = new ModDB();
Exception exception = null;
try {
ModDB modDB = new ModDB();
ObjectMapper mapper = new ObjectMapper();
mapper.configure( JsonParser.Feature.ALLOW_SINGLE_QUOTES, true );
mapper.setVisibility( PropertyAccessor.FIELD, Visibility.ANY );
@ -53,6 +54,8 @@ public class JacksonCatalogReader {
modDB.addMod( modInfo );
}
}
return modDB;
}
catch ( JsonProcessingException e ) {
exception = e;
@ -65,6 +68,6 @@ public class JacksonCatalogReader {
return null;
}
return modDB;
return null;
}
}

View file

@ -10,10 +10,6 @@ import java.io.InputStreamReader;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Date;
import java.util.List;
import java.util.Map;
@ -21,6 +17,14 @@ import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
public class URLFetcher {
@ -28,104 +32,129 @@ public class URLFetcher {
/**
* Downloads content from a url into a file, if the remote content has changed.
* Downloads content from a url to one file, and its ETag to another.
*
* If the ETag files exists, it will be read to inform the GET request.
*
* If the content has not changed, the destination file's modified date
* will be reset to the present time.
*
* If the content has changed, it will be written to the file, as will the
* new ETag.
*
* @return true if successfully downloaded, false otherwise
*/
public static boolean refetchURL( String url, File localFile, File eTagFile ) {
String localETag = null;
log.debug( String.format( "Attempting to download the latest \"%s\".", localFile.getName() ) );
log.debug( String.format( "Attempting to download the latest \"%s\"", localFile.getName() ) );
if ( eTagFile.exists() ) {
// Load the old eTag.
InputStream etagIn = null;
BufferedReader etagReader = null;
try {
etagIn = new FileInputStream( eTagFile );
BufferedReader br = new BufferedReader( new InputStreamReader( etagIn, "UTF-8" ) );
String line = br.readLine();
if ( line.length() > 0 )
etagReader = new BufferedReader( new InputStreamReader( etagIn, "UTF-8" ) );
String line = etagReader.readLine();
if ( line.length() > 0 ) {
localETag = line;
}
}
catch ( IOException e ) {
// Not serious enough to be a real error.
log.debug( String.format( "Error reading eTag from \"%s\".", eTagFile.getName() ), e );
log.debug( String.format( "Error reading eTag from \"%s\"", eTagFile.getName() ), e );
}
finally {
try {if ( etagReader != null ) etagReader.close();}
catch ( IOException e ) {}
try {if ( etagIn != null ) etagIn.close();}
catch ( IOException e ) {}
}
}
String remoteETag = null;
InputStream urlIn = null;
HttpGet request = null;
OutputStream localOut = null;
String remoteETag = null;
RequestConfig requestConfig = RequestConfig.custom()
.setConnectionRequestTimeout( 5000 )
.setConnectTimeout( 5000 )
.setSocketTimeout( 10000 )
.setRedirectsEnabled( true )
.build();
CloseableHttpClient httpClient = HttpClientBuilder.create()
.setDefaultRequestConfig( requestConfig )
.disableAuthCaching()
.disableAutomaticRetries()
.disableConnectionState()
.disableCookieManagement()
//.setUserAgent( "" )
.build();
try {
URLConnection conn = new URL( url ).openConnection();
request = new HttpGet( url );
if ( conn instanceof HttpURLConnection == false ) {
log.error( String.format( "Non-Http(s) URL given for fetching: %s", url ) );
return false;
HttpResponse response = httpClient.execute( request );
int status = response.getStatusLine().getStatusCode();
if ( status >= 200 && status < 300 ) {
HttpEntity entity = response.getEntity();
if ( entity != null ) {
localOut = new FileOutputStream( localFile );
entity.writeTo( localOut );
}
HttpURLConnection httpConn = (HttpURLConnection)conn;
httpConn.setReadTimeout( 10000 );
if ( localETag != null )
httpConn.setRequestProperty( "If-None-Match", localETag );
httpConn.connect();
int responseCode = httpConn.getResponseCode();
if ( responseCode == HttpURLConnection.HTTP_NOT_MODIFIED ) {
log.debug( String.format( "No need to update \"%s\", the server's copy has not been modified since the previous check", localFile.getName() ) );
if ( response.containsHeader( "ETag" ) ) {
remoteETag = response.getLastHeader( "ETag" ).getValue();
}
}
else if ( status == 304 ) { // Not modified.
log.debug( String.format( "No need to download \"%s\", the server's copy has not been modified", localFile.getName() ) );
// Update the local file's timestamp as if it had downloaded.
localFile.setLastModified( new Date().getTime() );
return false;
}
else if ( responseCode == HttpURLConnection.HTTP_OK ) {
Map<String, List<String>> headerMap = httpConn.getHeaderFields();
List<String> eTagValues = headerMap.get( "ETag" );
if ( eTagValues != null && eTagValues.size() > 0 )
remoteETag = eTagValues.get( 0 );
urlIn = httpConn.getInputStream();
localOut = new FileOutputStream( localFile );
byte[] buf = new byte[4096];
int len;
while ( (len = urlIn.read(buf)) >= 0 ) {
localOut.write( buf, 0, len );
}
}
else {
log.error( String.format( "Download request failed for \"%s\": HTTP Code %d (%s)", httpConn.getURL(), responseCode, httpConn.getResponseMessage() ) );
throw new ClientProtocolException( "Unexpected response status: "+ status );
}
}
catch ( ClientProtocolException e ) {
log.error( "GET request failed for url: "+ request.getURI().toString(), e );
return false;
}
}
catch ( IOException e ) {
log.error( String.format( "Error downloading the latest \"%s\"", localFile.getName() ), e );
log.error( "Download failed for url: "+ request.getURI().toString(), e );
return false;
}
finally {
try {if ( urlIn != null ) urlIn.close();}
try {if ( localOut != null ) localOut.close();}
catch ( IOException e ) {}
try {if ( localOut != null ) localOut.close();}
try {httpClient.close();}
catch ( IOException e ) {}
}
if ( remoteETag != null ) {
// Save the new eTag.
OutputStream etagOut = null;
BufferedWriter etagWriter = null;
try {
etagOut = new FileOutputStream( eTagFile );
BufferedWriter bw = new BufferedWriter( new OutputStreamWriter( etagOut, "UTF-8" ) );
bw.append( remoteETag );
bw.flush();
etagWriter = new BufferedWriter( new OutputStreamWriter( etagOut, "UTF-8" ) );
etagWriter.append( remoteETag );
etagWriter.flush();
}
catch ( IOException e ) {
log.error( String.format( "Error writing eTag to \"%s\"", eTagFile.getName() ), e );
}
finally {
try {if ( etagWriter != null ) etagWriter.close();}
catch ( IOException e ) {}
try {if ( etagOut != null ) etagOut.close();}
catch ( IOException e ) {}
}