vayacondios-server 0.1.2 → 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -0
- data/app/http_shim.rb +1 -5
- data/lib/vayacondios-client.rb +2 -0
- data/lib/vayacondios-server.rb +1 -0
- data/lib/vayacondios/client/cube_client.rb +39 -0
- data/lib/vayacondios/client/itemset.rb +43 -28
- data/lib/vayacondios/client/notifier.rb +24 -1
- data/lib/vayacondios/client/zabbix_client.rb +148 -0
- data/lib/vayacondios/server/handlers/itemset_handler.rb +0 -1
- data/lib/vayacondios/server/model/itemset_document.rb +8 -4
- data/lib/vayacondios/server/rack/assume_json.rb +13 -0
- data/lib/vayacondios/server/rack/extract_methods.rb +11 -1
- data/lib/vayacondios/version.rb +1 -1
- data/pom.xml +97 -0
- data/scripts/hadoop_monitor/configurable.rb +1 -1
- data/scripts/hadoop_monitor/hadoop_attempt_scraper.rb +6 -3
- data/scripts/hadoop_monitor/hadoop_client.rb +20 -19
- data/scripts/hadoop_monitor/hadoop_monitor.rb +3 -3
- data/scripts/hadoop_monitor/hadoopable.rb +3 -3
- data/scripts/hadoop_monitor/machine_monitor.rb +2 -2
- data/spec/client/itemset_spec.rb +8 -8
- data/spec/server/itemset_spec.rb +4 -4
- data/src/main/java/com/infochimps/util/CurrentClass.java +26 -0
- data/src/main/java/com/infochimps/util/DebugUtil.java +38 -0
- data/src/main/java/com/infochimps/util/HttpHelper.java +112 -0
- data/src/main/java/com/infochimps/vayacondios/ItemSets.java +456 -0
- data/src/main/java/com/infochimps/vayacondios/Organization.java +49 -0
- data/src/main/java/com/infochimps/vayacondios/PathBuilder.java +13 -0
- data/src/main/java/com/infochimps/vayacondios/VCDIntegrationTest.java +68 -0
- data/src/main/java/com/infochimps/vayacondios/VayacondiosClient.java +88 -0
- data/vayacondios-client.gemspec +2 -2
- data/vayacondios-server.gemspec +4 -2
- metadata +37 -9
data/lib/vayacondios/version.rb
CHANGED
data/pom.xml
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
2
|
+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
3
|
+
<modelVersion>4.0.0</modelVersion>
|
4
|
+
<groupId>com.infochimps</groupId>
|
5
|
+
<artifactId>vayacondios</artifactId>
|
6
|
+
<packaging>jar</packaging>
|
7
|
+
<version>1.0-SNAPSHOT</version>
|
8
|
+
<name>java-common</name>
|
9
|
+
<url>http://maven.apache.org</url>
|
10
|
+
|
11
|
+
<parent>
|
12
|
+
<groupId>com.infochimps</groupId>
|
13
|
+
<artifactId>parent-pom</artifactId>
|
14
|
+
<version>1.0.0-SNAPSHOT</version>
|
15
|
+
</parent>
|
16
|
+
|
17
|
+
<build>
|
18
|
+
<plugins>
|
19
|
+
<plugin>
|
20
|
+
<groupId>org.apache.maven.plugins</groupId>
|
21
|
+
<artifactId>maven-shade-plugin</artifactId>
|
22
|
+
<version>2.0</version>
|
23
|
+
<executions>
|
24
|
+
<execution>
|
25
|
+
<phase>package</phase>
|
26
|
+
<goals>
|
27
|
+
<goal>shade</goal>
|
28
|
+
</goals>
|
29
|
+
<configuration>
|
30
|
+
<transformers>
|
31
|
+
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
32
|
+
<mainClass>com.infochimps.vayacondios.VCDIntegrationTest</mainClass>
|
33
|
+
</transformer>
|
34
|
+
</transformers>
|
35
|
+
</configuration>
|
36
|
+
</execution>
|
37
|
+
</executions>
|
38
|
+
</plugin>
|
39
|
+
<plugin>
|
40
|
+
<groupId>org.codehaus.mojo</groupId>
|
41
|
+
<artifactId>exec-maven-plugin</artifactId>
|
42
|
+
<version>1.2.1</version>
|
43
|
+
<configuration>
|
44
|
+
<executable>java</executable>
|
45
|
+
<arguments>
|
46
|
+
<argument>-cp</argument>
|
47
|
+
<classpath/>
|
48
|
+
<argument>-jar</argument>
|
49
|
+
<argument>target/${project.artifactId}-${project.version}.jar</argument>
|
50
|
+
</arguments>
|
51
|
+
</configuration>
|
52
|
+
</plugin>
|
53
|
+
</plugins>
|
54
|
+
</build>
|
55
|
+
|
56
|
+
<repositories>
|
57
|
+
<!-- Infochimps Repositories -->
|
58
|
+
<repository>
|
59
|
+
<id>infochimps.releases</id>
|
60
|
+
<name>Infochimps Internal Repository</name>
|
61
|
+
<url>https://s3.amazonaws.com/artifacts.chimpy.us/maven-s3p/releases</url>
|
62
|
+
</repository>
|
63
|
+
<repository>
|
64
|
+
<id>infochimps.snapshots</id>
|
65
|
+
<name>Infochimps Internal Repository</name>
|
66
|
+
<url>https://s3.amazonaws.com/artifacts.chimpy.us/maven-s3p/snapshots</url>
|
67
|
+
<snapshots>
|
68
|
+
<enabled>true</enabled>
|
69
|
+
<updatePolicy>always</updatePolicy>
|
70
|
+
</snapshots>
|
71
|
+
</repository>
|
72
|
+
</repositories>
|
73
|
+
|
74
|
+
<dependencies>
|
75
|
+
<dependency>
|
76
|
+
<groupId>com.google.code.gson</groupId>
|
77
|
+
<artifactId>gson</artifactId>
|
78
|
+
<version>2.2.2</version>
|
79
|
+
</dependency>
|
80
|
+
<dependency>
|
81
|
+
<groupId>commons-codec</groupId>
|
82
|
+
<artifactId>commons-codec</artifactId>
|
83
|
+
<version>1.2</version>
|
84
|
+
</dependency>
|
85
|
+
<dependency>
|
86
|
+
<groupId>org.slf4j</groupId>
|
87
|
+
<artifactId>slf4j-api</artifactId>
|
88
|
+
<version>1.7.2</version>
|
89
|
+
</dependency>
|
90
|
+
<dependency>
|
91
|
+
<groupId>junit</groupId>
|
92
|
+
<artifactId>junit</artifactId>
|
93
|
+
<version>3.8.1</version>
|
94
|
+
<scope>test</scope>
|
95
|
+
</dependency>
|
96
|
+
</dependencies>
|
97
|
+
</project>
|
@@ -27,16 +27,19 @@ class HadoopAttemptScraper < Nibbler
|
|
27
27
|
|
28
28
|
def to_attempts
|
29
29
|
attempts.map do |attempt|
|
30
|
+
start_time = Time.parse(attempt.start_time) rescue nil
|
31
|
+
finish_time = attempt.finish_time.length > 0 ? Time.parse(attempt.finish_time) : nil
|
30
32
|
{
|
31
33
|
_id: attempt.attempt_id.to_s,
|
32
34
|
task_id: task_id,
|
33
35
|
host: attempt.machine.to_s.gsub(/^http:\/\//, '').gsub(/:[0-9]+$/, ''),
|
34
36
|
status: attempt.status,
|
35
37
|
progress: attempt.progress.to_f / 100.0,
|
36
|
-
start_time:
|
37
|
-
finish_time:
|
38
|
+
start_time: start_time,
|
39
|
+
finish_time: finish_time,
|
40
|
+
duration: start_time ? (finish_time || Time.now) - start_time : nil,
|
38
41
|
errors: attempt.errors
|
39
42
|
}
|
40
43
|
end
|
41
44
|
end
|
42
|
-
end
|
45
|
+
end
|
@@ -10,7 +10,7 @@ require 'pp'
|
|
10
10
|
require 'gorillib/string/inflections'
|
11
11
|
require 'swineherd-fs'
|
12
12
|
|
13
|
-
|
13
|
+
class Vayacondios
|
14
14
|
|
15
15
|
class HadoopClient
|
16
16
|
|
@@ -87,6 +87,11 @@ module Vayacondios
|
|
87
87
|
finished_status = [:FAILED, :KILLED, :COMPLETE]
|
88
88
|
failed_status = [:FAILED]
|
89
89
|
|
90
|
+
|
91
|
+
# not sure what is what. I'm guessing
|
92
|
+
# JobStatus.getStartTime corresponds to the
|
93
|
+
# launch time in the logs
|
94
|
+
|
90
95
|
start_time = Time.at(job_status.get_start_time / 1000)
|
91
96
|
reduce_progress = job.reduce_progress
|
92
97
|
map_progress = job.map_progress
|
@@ -100,22 +105,16 @@ module Vayacondios
|
|
100
105
|
_id: job_id.to_s,
|
101
106
|
name: job.get_job_name.to_s,
|
102
107
|
|
103
|
-
|
104
|
-
# JobStatus.getStartTime corresponds to the
|
105
|
-
# launch time in the logs, but I'm going to
|
106
|
-
# go ahead and use it twice here.
|
107
|
-
|
108
|
-
launch_time: start_time,
|
109
|
-
submit_time: start_time,
|
108
|
+
start_time: start_time,
|
110
109
|
finish_time: finish_time,
|
111
110
|
|
112
|
-
|
111
|
+
duration: run_duration,
|
113
112
|
|
114
113
|
map_eta: map_eta,
|
115
114
|
reduce_eta: reduce_eta,
|
116
115
|
eta: reduce_eta,
|
117
116
|
|
118
|
-
|
117
|
+
status: case job_status.get_run_state
|
119
118
|
when JobStatus::FAILED then :FAILED
|
120
119
|
when JobStatus::KILLED then :KILLED
|
121
120
|
when JobStatus::PREP then :PREP
|
@@ -128,9 +127,7 @@ module Vayacondios
|
|
128
127
|
failed_maps: num_tasks(job_id, :map, failed_status),
|
129
128
|
failed_reduces: num_tasks(job_id, :reduce, failed_status),
|
130
129
|
|
131
|
-
counters: parse_counters(job.get_counters)
|
132
|
-
type: :job,
|
133
|
-
|
130
|
+
counters: parse_counters(job.get_counters)
|
134
131
|
}
|
135
132
|
|
136
133
|
job_event = {
|
@@ -195,13 +192,17 @@ module Vayacondios
|
|
195
192
|
# object that represents it.
|
196
193
|
#
|
197
194
|
def parse_task task_report, task_type, parent_job_id
|
195
|
+
start_time = task_report.get_start_time > 0 ? Time.at(task_report.get_start_time / 1000) : nil
|
196
|
+
finish_time = task_report.get_finish_time > 0 ? Time.at(task_report.get_finish_time / 1000) : nil
|
197
|
+
|
198
198
|
{
|
199
199
|
_id: task_report.get_task_id.to_s,
|
200
|
-
job_id: parent_job_id,
|
201
|
-
|
202
|
-
|
203
|
-
start_time:
|
204
|
-
finish_time:
|
200
|
+
job_id: parent_job_id.to_s,
|
201
|
+
type: task_type,
|
202
|
+
status: task_report.get_current_status.to_s,
|
203
|
+
start_time: start_time,
|
204
|
+
finish_time: finish_time,
|
205
|
+
duration: start_time ? (finish_time || Time.now) - start_time : nil,
|
205
206
|
counters: parse_counters(task_report.get_counters),
|
206
207
|
diagnostics: task_report.get_diagnostics.map(&:to_s),
|
207
208
|
successful_attempt_id: task_report.get_successful_task_attempt.to_s
|
@@ -212,7 +213,7 @@ module Vayacondios
|
|
212
213
|
{
|
213
214
|
t: Time.now,
|
214
215
|
d: {
|
215
|
-
|
216
|
+
task_id: task_report.get_task_id.to_s,
|
216
217
|
progress: task_report.get_progress,
|
217
218
|
running_attempt_ids: task_report.get_running_task_attempts.map(&:to_s)
|
218
219
|
}
|
@@ -10,7 +10,7 @@ require 'thread'
|
|
10
10
|
require 'open-uri'
|
11
11
|
require 'json'
|
12
12
|
|
13
|
-
|
13
|
+
class Vayacondios
|
14
14
|
|
15
15
|
class HadoopMonitor
|
16
16
|
def initialize
|
@@ -34,8 +34,8 @@ module Vayacondios
|
|
34
34
|
tasks: @db.create_collection('job_tasks'),
|
35
35
|
attempts: @db.create_collection('job_task_attempts'),
|
36
36
|
|
37
|
-
job_events: @db.create_collection('job_events',
|
38
|
-
task_events: @db.create_collection('
|
37
|
+
job_events: @db.create_collection('job_events', capped_collection_opts),
|
38
|
+
task_events: @db.create_collection('job_task_events', capped_collection_opts),
|
39
39
|
}
|
40
40
|
end
|
41
41
|
|
@@ -1,12 +1,12 @@
|
|
1
1
|
require 'stringio'
|
2
2
|
|
3
|
-
|
3
|
+
class Vayacondios
|
4
4
|
|
5
5
|
module Hadoopable
|
6
6
|
|
7
7
|
include Configurable
|
8
8
|
|
9
|
-
#--------------------------------------------------------------------------------
|
9
|
+
#--------------------------------------------------------------------------------
|
10
10
|
# Initialize jruby and tell it about hadoop.
|
11
11
|
#--------------------------------------------------------------------------------
|
12
12
|
|
@@ -22,7 +22,7 @@ module Vayacondios
|
|
22
22
|
|
23
23
|
$CLASSPATH << File.join(File.join(hadoop_home, 'conf') || ENV['HADOOP_CONF_DIR'],
|
24
24
|
'') # add trailing slash
|
25
|
-
|
25
|
+
|
26
26
|
Dir["#{hadoop_home}/{hadoop*.jar,lib/*.jar}"].each{|jar| require jar}
|
27
27
|
|
28
28
|
include_class org.apache.hadoop.mapred.JobConf
|
@@ -7,7 +7,7 @@ require 'scanf'
|
|
7
7
|
require 'json'
|
8
8
|
require 'mongo'
|
9
9
|
|
10
|
-
|
10
|
+
class Vayacondios
|
11
11
|
|
12
12
|
class StatServer
|
13
13
|
|
@@ -60,7 +60,7 @@ module Vayacondios
|
|
60
60
|
|
61
61
|
# main loop
|
62
62
|
loop do
|
63
|
-
|
63
|
+
|
64
64
|
logger.debug "In main event loop. Waiting to see if the cluster is busy."
|
65
65
|
|
66
66
|
# Get up-to-date on the state of the cluster.
|
data/spec/client/itemset_spec.rb
CHANGED
@@ -16,28 +16,28 @@ describe Vayacondios::Client::ItemSet do
|
|
16
16
|
itemset = Vayacondios::Client::ItemSet.new("foohost", 9999, "fooorg", "footopic", "fooid")
|
17
17
|
ary = ["foo", "bar", "baz"]
|
18
18
|
|
19
|
-
#
|
19
|
+
# testing internals here to avoid shimming up HTTP libraries.
|
20
20
|
|
21
21
|
it "generates a put request without a patch header when asked to create" do
|
22
|
-
req = itemset._req
|
22
|
+
req = itemset.instance_eval{_req(:create, ary)}
|
23
23
|
|
24
24
|
req.method.should eql('PUT')
|
25
|
-
req.body.should eql(ary
|
25
|
+
req.body.should eql(MultiJson.encode(contents: ary))
|
26
26
|
req.path.should eql('/v1/fooorg/itemset/footopic/fooid')
|
27
27
|
req.each_header.to_a.should_not include(["x_method", "PATCH"])
|
28
28
|
end
|
29
29
|
|
30
30
|
it "generates a put request with a patch header when asked to update" do
|
31
|
-
req = itemset._req
|
31
|
+
req = itemset.instance_eval{_req(:update, ary)}
|
32
32
|
|
33
33
|
req.method.should eql('PUT')
|
34
|
-
req.body.should eql(ary
|
34
|
+
req.body.should eql(MultiJson.encode(contents: ary))
|
35
35
|
req.path.should eql('/v1/fooorg/itemset/footopic/fooid')
|
36
36
|
req.each_header.to_a.should include(["x-method", "PATCH"])
|
37
37
|
end
|
38
38
|
|
39
39
|
it "generates a get request when asked to fetch" do
|
40
|
-
req = itemset._req
|
40
|
+
req = itemset.instance_eval{_req(:fetch)}
|
41
41
|
|
42
42
|
req.method.should eql('GET')
|
43
43
|
req.body.should be_nil
|
@@ -45,10 +45,10 @@ describe Vayacondios::Client::ItemSet do
|
|
45
45
|
end
|
46
46
|
|
47
47
|
it "generates a delete request when asked to remove" do
|
48
|
-
req = itemset._req
|
48
|
+
req = itemset.instance_eval{_req(:remove, ary)}
|
49
49
|
|
50
50
|
req.method.should eql('DELETE')
|
51
|
-
req.body.should eql(ary
|
51
|
+
req.body.should eql(MultiJson.encode(contents: ary))
|
52
52
|
req.path.should eql('/v1/fooorg/itemset/footopic/fooid')
|
53
53
|
end
|
54
54
|
end
|
data/spec/server/itemset_spec.rb
CHANGED
@@ -157,7 +157,7 @@ describe HttpShim do
|
|
157
157
|
with_api(HttpShim) do |api|
|
158
158
|
get_request({:path => '/v1/infochimps/itemset/power/level'}, err) do |c|
|
159
159
|
c.response_header.status.should == 200
|
160
|
-
MultiJson.load(c.response).should eql(["foo", "bar"])
|
160
|
+
MultiJson.load(c.response).should eql({"contents" => ["foo", "bar"]})
|
161
161
|
end
|
162
162
|
end
|
163
163
|
end
|
@@ -215,7 +215,7 @@ describe HttpShim do
|
|
215
215
|
with_api(HttpShim) do |api|
|
216
216
|
get_request({:path => '/v1/infochimps/itemset/merge/test'}, err) do |c|
|
217
217
|
c.response_header.status.should == 200
|
218
|
-
MultiJson.load(c.response).should eql(["foo", "bar"])
|
218
|
+
MultiJson.load(c.response).should eql({"contents" => ["foo", "bar"]})
|
219
219
|
end
|
220
220
|
end
|
221
221
|
end
|
@@ -277,7 +277,7 @@ describe HttpShim do
|
|
277
277
|
with_api(HttpShim) do |api|
|
278
278
|
get_request({:path => '/v1/infochimps/itemset/power/level'}, err) do |c|
|
279
279
|
c.response_header.status.should == 200
|
280
|
-
MultiJson.load(c.response).should eql ["foo"]
|
280
|
+
MultiJson.load(c.response).should eql({"contents" => ["foo"]})
|
281
281
|
end
|
282
282
|
end
|
283
283
|
end
|
@@ -304,7 +304,7 @@ describe HttpShim do
|
|
304
304
|
with_api(HttpShim) do |api|
|
305
305
|
get_request({:path => '/v1/infochimps/itemset/power/level'}, err) do |c|
|
306
306
|
c.response_header.status.should == 200
|
307
|
-
MultiJson.load(c.response).should eql []
|
307
|
+
MultiJson.load(c.response).should eql({"contents" => []})
|
308
308
|
end
|
309
309
|
end
|
310
310
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
package com.infochimps.util;
|
2
|
+
|
3
|
+
import org.slf4j.Logger;
|
4
|
+
import org.slf4j.LoggerFactory;
|
5
|
+
|
6
|
+
public class CurrentClass extends SecurityManager {
|
7
|
+
private static CurrentClass SINGLETON = new CurrentClass();
|
8
|
+
|
9
|
+
// must call this directly. behavior is dependent on call stack
|
10
|
+
public static Class get() {
|
11
|
+
return SINGLETON.getCurrentClass();
|
12
|
+
}
|
13
|
+
|
14
|
+
// must call this directly. behavior is dependent on call stack
|
15
|
+
public static Logger getLogger() {
|
16
|
+
return LoggerFactory.getLogger(SINGLETON.getCurrentClass(2));
|
17
|
+
}
|
18
|
+
|
19
|
+
private Class getCurrentClass(int i) {
|
20
|
+
return getClassContext()[i];
|
21
|
+
}
|
22
|
+
|
23
|
+
private Class getCurrentClass() {
|
24
|
+
return getCurrentClass(3);
|
25
|
+
}
|
26
|
+
}
|
@@ -0,0 +1,38 @@
|
|
1
|
+
package com.infochimps.util;
|
2
|
+
|
3
|
+
import java.net.InetSocketAddress;
|
4
|
+
|
5
|
+
import javax.net.ssl.HostnameVerifier;
|
6
|
+
import javax.net.ssl.HttpsURLConnection;
|
7
|
+
import javax.net.ssl.SSLContext;
|
8
|
+
import javax.net.ssl.SSLSession;
|
9
|
+
import javax.net.ssl.TrustManager;
|
10
|
+
import javax.net.ssl.X509TrustManager;
|
11
|
+
|
12
|
+
import java.security.cert.X509Certificate;
|
13
|
+
import java.security.SecureRandom;
|
14
|
+
import java.security.GeneralSecurityException;
|
15
|
+
|
16
|
+
import java.net.Proxy;
|
17
|
+
|
18
|
+
public class DebugUtil {
|
19
|
+
public static Proxy useCharles() {
|
20
|
+
trustAllCerts();
|
21
|
+
return new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 8888));
|
22
|
+
}
|
23
|
+
|
24
|
+
public static void trustAllCerts() {
|
25
|
+
try {
|
26
|
+
SSLContext sc = SSLContext.getInstance("SSL");
|
27
|
+
sc.init(null,
|
28
|
+
new TrustManager[] {
|
29
|
+
new X509TrustManager() {
|
30
|
+
public X509Certificate[] getAcceptedIssuers() { return null;}
|
31
|
+
public void checkClientTrusted(X509Certificate[] certs, String authType) {}
|
32
|
+
public void checkServerTrusted(X509Certificate[] certs, String authType) {}
|
33
|
+
}
|
34
|
+
}, new SecureRandom());
|
35
|
+
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
|
36
|
+
} catch (GeneralSecurityException e) {}
|
37
|
+
}
|
38
|
+
}
|
@@ -0,0 +1,112 @@
|
|
1
|
+
package com.infochimps.util;
|
2
|
+
|
3
|
+
import java.io.BufferedReader;
|
4
|
+
import java.io.InputStream;
|
5
|
+
import java.io.InputStreamReader;
|
6
|
+
import java.io.IOException;
|
7
|
+
import java.net.HttpURLConnection;
|
8
|
+
import java.net.MalformedURLException;
|
9
|
+
import java.net.URL;
|
10
|
+
import java.net.URLDecoder;
|
11
|
+
import java.nio.charset.Charset;
|
12
|
+
import java.util.HashMap;
|
13
|
+
import java.util.zip.GZIPInputStream;
|
14
|
+
|
15
|
+
import org.apache.commons.codec.binary.Base64;
|
16
|
+
|
17
|
+
import org.slf4j.Logger;
|
18
|
+
import org.slf4j.LoggerFactory;
|
19
|
+
|
20
|
+
import static java.util.Map.Entry;
|
21
|
+
|
22
|
+
public class HttpHelper {
|
23
|
+
private static final Base64 BASE64 = new Base64();
|
24
|
+
private static final boolean USE_CHARLES = false;
|
25
|
+
|
26
|
+
// opens or returns a null reader
|
27
|
+
public static BufferedReader openOrNull(Logger log, String urlString, Charset inputCharset) {
|
28
|
+
try { return open(log, urlString, inputCharset); }
|
29
|
+
catch (IOException e) {
|
30
|
+
log.warn("Got an exception trying to open {}: {}", urlString, e);
|
31
|
+
return null;
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
public static BufferedReader open(Logger log,
|
36
|
+
String urlString,
|
37
|
+
Charset inputCharset) throws IOException {
|
38
|
+
HttpURLConnection con = getConnection(urlString, log);
|
39
|
+
return getReader(con, log, inputCharset);
|
40
|
+
}
|
41
|
+
|
42
|
+
public static BufferedReader open(Logger log,
|
43
|
+
String urlString,
|
44
|
+
HashMap<String,String> extraHeaders,
|
45
|
+
Charset inputCharset) throws IOException {
|
46
|
+
|
47
|
+
HttpURLConnection con = getConnection(urlString, log);
|
48
|
+
for (Entry<String,String> header : extraHeaders.entrySet())
|
49
|
+
con.setRequestProperty(header.getKey(), header.getValue());
|
50
|
+
return getReader(con, log, inputCharset);
|
51
|
+
}
|
52
|
+
|
53
|
+
private static HttpURLConnection getConnection(String urlString, Logger log) throws IOException {
|
54
|
+
URL url = null;
|
55
|
+
try { url = new URL(urlString); }
|
56
|
+
catch (MalformedURLException e) {
|
57
|
+
log.warn("malformed URL: {}", url);
|
58
|
+
throw new IOException(e);
|
59
|
+
}
|
60
|
+
|
61
|
+
HttpURLConnection con = (HttpURLConnection)(USE_CHARLES ?
|
62
|
+
url.openConnection(DebugUtil.useCharles()) :
|
63
|
+
url.openConnection());
|
64
|
+
|
65
|
+
String userInfo = url.getUserInfo();
|
66
|
+
if (userInfo != null) {
|
67
|
+
userInfo = URLDecoder.decode(userInfo, "US-ASCII");
|
68
|
+
con.setRequestProperty("Authorization", "Basic " + new String(BASE64.encodeBase64(userInfo.getBytes())));
|
69
|
+
}
|
70
|
+
con.setRequestProperty("Accept-Encoding", "gzip,deflate");
|
71
|
+
return con;
|
72
|
+
}
|
73
|
+
|
74
|
+
private static BufferedReader getReader(HttpURLConnection con,
|
75
|
+
Logger log,
|
76
|
+
Charset inputCharset) throws IOException {
|
77
|
+
InputStream in = null;
|
78
|
+
|
79
|
+
try { in = con.getInputStream(); }
|
80
|
+
catch (IOException e) {
|
81
|
+
// Some HTTP responses will raise an exception, but the
|
82
|
+
// useful information is in the error stream.
|
83
|
+
|
84
|
+
log.warn("Exception opening {}", con.getURL().toString());
|
85
|
+
|
86
|
+
InputStream errorStream = con.getErrorStream();
|
87
|
+
if (errorStream != null) {
|
88
|
+
BufferedReader r = new BufferedReader(new InputStreamReader(errorStream));
|
89
|
+
try { for (String line; (line = r.readLine()) != null; log.error(line)); }
|
90
|
+
catch (IOException nested_exc) {
|
91
|
+
log.error("Got an exception in the exception handler: {}", nested_exc);
|
92
|
+
throw e;
|
93
|
+
}
|
94
|
+
}
|
95
|
+
throw e;
|
96
|
+
}
|
97
|
+
|
98
|
+
String encoding = con.getContentEncoding();
|
99
|
+
log.debug("Got HTTP stream with content encoding type '" + encoding + "'");
|
100
|
+
|
101
|
+
if (encoding != null && encoding.equals("gzip")) in = new GZIPInputStream(in);
|
102
|
+
|
103
|
+
InputStreamReader istream_reader = new InputStreamReader(in, inputCharset);
|
104
|
+
BufferedReader reader = new BufferedReader(istream_reader);
|
105
|
+
|
106
|
+
log.debug("successfully opened connection to {} with character encoding {}",
|
107
|
+
con.getURL().toString(),
|
108
|
+
istream_reader.getEncoding());
|
109
|
+
|
110
|
+
return reader;
|
111
|
+
}
|
112
|
+
}
|