Skip to content

Commit dcff44e

Browse files
committed
Add HttpPoster (#460)
- refactor
1 parent 85095c0 commit dcff44e

File tree

4 files changed

+250
-77
lines changed

4 files changed

+250
-77
lines changed
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/*
2+
* Copyright 2022 hbz
3+
*
4+
* Licensed under the Apache License, Version 2.0 the "License";
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.metafacture.io;
18+
19+
import org.metafacture.framework.ObjectReceiver;
20+
import org.metafacture.framework.helpers.DefaultObjectPipe;
21+
22+
import java.io.IOException;
23+
import java.net.URL;
24+
import java.net.URLConnection;
25+
import java.util.Arrays;
26+
import java.util.HashMap;
27+
import java.util.Map;
28+
import java.util.regex.Pattern;
29+
30+
/**
31+
* Common functions for UrlConnections.
32+
*
33+
* @param <T> object type that this module processes
34+
* @param <R> receiver type of the downstream module
35+
* @author Jens Wille
36+
* @author Pascal Christoph (dr0i)
37+
*/
38+
abstract class AbstractUrlConnection<T, R> extends DefaultObjectPipe<T, ObjectReceiver<R>> {
39+
public static final String ACCEPT_HEADER = "accept";
40+
public static final String ENCODING_HEADER = "accept-charset";
41+
public static final String CONTENT_TYPE_HEADER = "content-Type";
42+
static final String ACCEPT_DEFAULT = "*/*";
43+
static final String ENCODING_DEFAULT = "UTF-8";
44+
private static Boolean doOutput = false;
45+
private final Map<String, String> headers = new HashMap<>();
46+
private final Pattern headerFieldSeparator = Pattern.compile("\n");
47+
private final Pattern headerValueSeparator = Pattern.compile(":");
48+
private URLConnection con;
49+
50+
/**
51+
* Sets the HTTP {@value #ACCEPT_HEADER} header. This is a mime-type such as text/plain
52+
* or text/html.
53+
*
54+
* @param accept mime-type to use for the HTTP accept header
55+
*/
56+
public void setAccept(final String accept) {
57+
setHeader(ACCEPT_HEADER, accept);
58+
}
59+
60+
/**
61+
* Sets the preferred encoding of the HTTP response. This value is set as the
62+
* {@value #ENCODING_HEADER} header. Additionally, the encoding is used for reading the
63+
* HTTP response if it does not specify an encoding.
64+
*
65+
* @param encoding name of the encoding used for the accept-charset HTTP
66+
* header
67+
*/
68+
public void setEncoding(final String encoding) {
69+
setHeader(ENCODING_HEADER, encoding);
70+
}
71+
72+
/**
73+
* Sets the HTTP {@value #CONTENT_TYPE_HEADER} header. This is a mime-type such as text/plain,
74+
* text/html or application/x-ndjson.
75+
*
76+
* @param contentType mime-type to use for the HTTP contentType header
77+
*/
78+
public void setContentType(final String contentType) {
79+
setHeader(CONTENT_TYPE_HEADER, contentType);
80+
}
81+
82+
/**
83+
* Set the DoOutput flag to true if you intend to use the URL connection for output, false if not. The default is false.
84+
*
85+
* @param doOutput the new value
86+
* @see URLConnection#setDoOutput(boolean)
87+
*/
88+
public void setDoOutput(final boolean doOutput) {
89+
AbstractUrlConnection.doOutput = doOutput;
90+
}
91+
92+
/**
93+
* Gets the {@value #ENCODING_HEADER} header of the URLConnection.
94+
*
95+
* @return the name of the encoding header
96+
*/
97+
public String getEncodingHeader() {
98+
return ENCODING_HEADER;
99+
}
100+
101+
/**
102+
* Sets a request property, or multiple request properties separated by
103+
* {@code \n}.
104+
*
105+
* @param header request property line
106+
*/
107+
public void setHeader(final String header) {
108+
Arrays.stream(headerFieldSeparator.split(header)).forEach(h -> {
109+
final String[] parts = headerValueSeparator.split(h, 2);
110+
if (parts.length == 2) {
111+
setHeader(parts[0], parts[1].trim());
112+
}
113+
else {
114+
throw new IllegalArgumentException("Invalid header: " + h);
115+
}
116+
});
117+
}
118+
119+
/**
120+
* Sets a request property.
121+
*
122+
* @param key request property key
123+
* @param value request property value
124+
*/
125+
public void setHeader(final String key, final String value) {
126+
headers.put(key.toLowerCase(), value);
127+
}
128+
129+
/**
130+
* Gets headers of the {@link java.net.URLConnection} as a HashMap.
131+
*
132+
* @return the headers od the URLConnection as HashMap
133+
*/
134+
public Map<String, String> getHeaders() {
135+
return headers;
136+
}
137+
138+
/**
139+
* Opens an {@link java.net.URLConnection} defined by the URL.
140+
*
141+
* @param urlStr an URL as String
142+
* @return {@link java.net.URLConnection}
143+
* @throws IOException if an I/O IOException occurs
144+
*/
145+
public URLConnection getUrlConnection(final String urlStr) throws IOException {
146+
final URL url = new URL(urlStr);
147+
con = url.openConnection();
148+
con.setDoOutput(doOutput);
149+
headers.forEach(con::addRequestProperty);
150+
return con;
151+
}
152+
153+
}

metafacture-io/src/main/java/org/metafacture/io/HttpOpener.java

Lines changed: 7 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2013, 2014 Deutsche Nationalbibliothek
2+
* Copyright 2013 - 2022 Deutsche Nationalbibliothek et al.
33
*
44
* Licensed under the Apache License, Version 2.0 the "License";
55
* you may not use this file except in compliance with the License.
@@ -18,24 +18,17 @@
1818

1919
import org.metafacture.framework.FluxCommand;
2020
import org.metafacture.framework.MetafactureException;
21-
import org.metafacture.framework.ObjectReceiver;
2221
import org.metafacture.framework.annotations.Description;
2322
import org.metafacture.framework.annotations.In;
2423
import org.metafacture.framework.annotations.Out;
25-
import org.metafacture.framework.helpers.DefaultObjectPipe;
2624

2725
import java.io.IOException;
2826
import java.io.InputStreamReader;
2927
import java.io.Reader;
30-
import java.net.URL;
3128
import java.net.URLConnection;
32-
import java.util.Arrays;
33-
import java.util.HashMap;
34-
import java.util.Map;
35-
import java.util.regex.Pattern;
3629

3730
/**
38-
* Opens a {@link URLConnection} and passes a reader to the receiver.
31+
* Opens a {@link java.net.URLConnection} and passes a reader to the receiver.
3932
*
4033
* @author Christoph Böhme
4134
* @author Jan Schnasse
@@ -44,88 +37,25 @@
4437
@In(String.class)
4538
@Out(Reader.class)
4639
@FluxCommand("open-http")
47-
public final class HttpOpener extends DefaultObjectPipe<String, ObjectReceiver<Reader>> {
48-
49-
private static final Pattern HEADER_FIELD_SEPARATOR = Pattern.compile("\n");
50-
private static final Pattern HEADER_VALUE_SEPARATOR = Pattern.compile(":");
51-
52-
private static final String ACCEPT_HEADER = "accept";
53-
private static final String ENCODING_HEADER = "accept-charset";
54-
55-
private static final String ACCEPT_DEFAULT = "*/*";
56-
private static final String ENCODING_DEFAULT = "UTF-8";
57-
58-
private final Map<String, String> headers = new HashMap<>();
40+
public final class HttpOpener extends AbstractUrlConnection<String, Reader> {
5941

6042
/**
6143
* Creates an instance of {@link HttpOpener}.
44+
* Sets the default value for the {@value #ENCODING_HEADER} is {@value #ENCODING_DEFAULT}.
45+
* The default value of the {@value #ACCEPT_HEADER} is *&#47;* which mean any mime-type.
6246
*/
6347
public HttpOpener() {
6448
setAccept(ACCEPT_DEFAULT);
6549
setEncoding(ENCODING_DEFAULT);
6650
}
6751

68-
/**
69-
* Sets the HTTP accept header value. This is a mime-type such as text/plain
70-
* or text/html. The default value of the accept is *&#47;* which means
71-
* any mime-type.
72-
*
73-
* @param accept mime-type to use for the HTTP accept header
74-
*/
75-
public void setAccept(final String accept) {
76-
setHeader(ACCEPT_HEADER, accept);
77-
}
78-
79-
/**
80-
* Sets the preferred encoding of the HTTP response. This value is in the
81-
* accept-charset header. Additonally, the encoding is used for reading the
82-
* HTTP resonse if it does not specify an encoding. The default value for
83-
* the encoding is UTF-8.
84-
*
85-
* @param encoding name of the encoding used for the accept-charset HTTP
86-
* header
87-
*/
88-
public void setEncoding(final String encoding) {
89-
setHeader(ENCODING_HEADER, encoding);
90-
}
91-
92-
/**
93-
* Sets a request property, or multiple request properties separated by
94-
* {@code \n}.
95-
*
96-
* @param header request property line
97-
*/
98-
public void setHeader(final String header) {
99-
Arrays.stream(HEADER_FIELD_SEPARATOR.split(header)).forEach(h -> {
100-
final String[] parts = HEADER_VALUE_SEPARATOR.split(h, 2);
101-
if (parts.length == 2) {
102-
setHeader(parts[0], parts[1].trim());
103-
}
104-
else {
105-
throw new IllegalArgumentException("Invalid header: " + h);
106-
}
107-
});
108-
}
109-
110-
/**
111-
* Sets a request property.
112-
*
113-
* @param key request property key
114-
* @param value request property value
115-
*/
116-
public void setHeader(final String key, final String value) {
117-
headers.put(key.toLowerCase(), value);
118-
}
119-
12052
@Override
12153
public void process(final String urlStr) {
12254
try {
123-
final URL url = new URL(urlStr);
124-
final URLConnection con = url.openConnection();
125-
headers.forEach(con::addRequestProperty);
55+
final URLConnection con = getUrlConnection(urlStr);
12656
String enc = con.getContentEncoding();
12757
if (enc == null) {
128-
enc = headers.get(ENCODING_HEADER);
58+
enc = getHeaders().get(ENCODING_HEADER);
12959
}
13060
getReceiver().process(new InputStreamReader(con.getInputStream(), enc));
13161
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Copyright 2022 Pascal Christoph, hbz
3+
*
4+
* Licensed under the Apache License, Version 2.0 the "License";
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.metafacture.io;
18+
19+
import org.metafacture.framework.FluxCommand;
20+
import org.metafacture.framework.MetafactureException;
21+
import org.metafacture.framework.annotations.Description;
22+
import org.metafacture.framework.annotations.In;
23+
import org.metafacture.framework.annotations.Out;
24+
25+
import java.io.IOException;
26+
import java.io.InputStreamReader;
27+
import java.io.OutputStream;
28+
import java.io.Reader;
29+
import java.net.HttpURLConnection;
30+
import java.net.ProtocolException;
31+
32+
/**
33+
* Uploads data using {@link java.net.HttpURLConnection} with POST method and passes the response to the receiver.
34+
* Supports the setting of 'Accept', 'ContentType' and 'Encoding' as HTTP header fields.
35+
*
36+
* @author Pascal Christoph (dr0i)
37+
*/
38+
@Description("POSTs data to a {@link java.net.HttpURLConnection}. Argument 'url' is mandatory. Supports the setting of 'accept', 'contentType' and 'encoding' (of the response) as http header fields.")
39+
@In(String.class)
40+
@Out(Reader.class)
41+
@FluxCommand("post-http")
42+
public final class HttpPoster extends AbstractUrlConnection<String, Reader> {
43+
44+
private static final String POST = "POST";
45+
private static final Boolean DO_OUTPUT = true;
46+
private static final int HTTP_STATUS_CODE_MIN = 100;
47+
private static final int HTTP_STATUS_CODE_MAX = 399;
48+
private String contentType = "application/json";
49+
private String url;
50+
51+
/**
52+
* Creates an instance of {@link HttpPoster}.
53+
*
54+
* @throws ProtocolException if a protocol error occurs
55+
*/
56+
public HttpPoster() throws ProtocolException {
57+
setDoOutput(DO_OUTPUT);
58+
}
59+
60+
/**
61+
* Sets the HTTP URL to POST to
62+
*
63+
* @param url the URL to POST to
64+
*/
65+
public void setUrl(final String url) {
66+
this.url = url;
67+
}
68+
69+
@Override
70+
public void process(final String data) throws IllegalStateException, NullPointerException {
71+
try {
72+
final HttpURLConnection conn = (HttpURLConnection) getUrlConnection(url);
73+
conn.setRequestMethod(POST);
74+
final InputStreamReader inputStreamReader;
75+
final OutputStream os = conn.getOutputStream();
76+
os.write(data.getBytes());
77+
if (HTTP_STATUS_CODE_MIN <= conn.getResponseCode() && conn.getResponseCode() <= HTTP_STATUS_CODE_MAX) {
78+
inputStreamReader = new InputStreamReader(conn.getInputStream());
79+
}
80+
else {
81+
inputStreamReader = new InputStreamReader(conn.getErrorStream());
82+
}
83+
getReceiver().process(inputStreamReader);
84+
}
85+
catch (final IOException e) {
86+
throw new MetafactureException(e);
87+
}
88+
}
89+
}

metafacture-io/src/main/resources/flux-commands.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,4 @@ write org.metafacture.io.ObjectWriter
2222
as-records org.metafacture.io.RecordReader
2323
open-resource org.metafacture.io.ResourceOpener
2424
open-tar org.metafacture.io.TarReader
25+
post-http org.metafacture.io.HttpPoster

0 commit comments

Comments
 (0)