move Pair class
This commit is contained in:
parent
8975cd0978
commit
81b1aad724
143 changed files with 10792 additions and 154 deletions
|
@ -1,21 +1,26 @@
|
||||||
group = org.xbib
|
group = org.xbib
|
||||||
name = netty-http
|
name = netty-http
|
||||||
version = 4.1.36.5
|
version = 4.1.36.6
|
||||||
|
|
||||||
# main packages
|
# main packages
|
||||||
netty.version = 4.1.36.Final
|
netty.version = 4.1.36.Final
|
||||||
tcnative.version = 2.0.25.Final
|
tcnative.version = 2.0.25.Final
|
||||||
|
|
||||||
# common
|
# common
|
||||||
xbib-net-url.version = 1.3.2
|
xbib-net-url.version = 1.3.3
|
||||||
|
|
||||||
# server
|
# server
|
||||||
bouncycastle.version = 1.61
|
bouncycastle.version = 1.61
|
||||||
|
|
||||||
|
# reactive
|
||||||
reactivestreams.version = 1.0.2
|
reactivestreams.version = 1.0.2
|
||||||
|
|
||||||
# server-rest
|
# rest
|
||||||
xbib-guice.version = 4.0.4
|
xbib-guice.version = 4.0.4
|
||||||
|
|
||||||
|
# xmlrpc-client
|
||||||
|
commons-httpclient.version = 3.1
|
||||||
|
|
||||||
# test packages
|
# test packages
|
||||||
junit.version = 5.4.2
|
junit.version = 5.4.2
|
||||||
conscrypt.version = 2.0.0
|
conscrypt.version = 2.0.0
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation "org.xbib:net-url:${project.property('xbib-net-url.version')}"
|
compile "org.xbib:net-url:${project.property('xbib-net-url.version')}"
|
||||||
implementation "io.netty:netty-codec-http2:${project.property('netty.version')}"
|
compile "io.netty:netty-codec-http2:${project.property('netty.version')}"
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import org.xbib.net.PercentDecoder;
|
||||||
import org.xbib.net.PercentEncoder;
|
import org.xbib.net.PercentEncoder;
|
||||||
import org.xbib.net.PercentEncoders;
|
import org.xbib.net.PercentEncoders;
|
||||||
import org.xbib.netty.http.common.util.LimitedSet;
|
import org.xbib.netty.http.common.util.LimitedSet;
|
||||||
import org.xbib.netty.http.common.util.LimitedMap;
|
import org.xbib.netty.http.common.util.LimitedTreeMap;
|
||||||
|
|
||||||
import java.nio.charset.MalformedInputException;
|
import java.nio.charset.MalformedInputException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
@ -42,7 +42,7 @@ public class HttpParameters implements Map<String, SortedSet<String>> {
|
||||||
|
|
||||||
private final int elementSizeLimit;
|
private final int elementSizeLimit;
|
||||||
|
|
||||||
private final LimitedMap<String, String> map;
|
private final LimitedTreeMap<String, String> map;
|
||||||
|
|
||||||
private final PercentEncoder percentEncoder;
|
private final PercentEncoder percentEncoder;
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ public class HttpParameters implements Map<String, SortedSet<String>> {
|
||||||
this.maxParam = maxParam;
|
this.maxParam = maxParam;
|
||||||
this.sizeLimit = sizeLimit;
|
this.sizeLimit = sizeLimit;
|
||||||
this.elementSizeLimit = elementSizeLimit;
|
this.elementSizeLimit = elementSizeLimit;
|
||||||
this.map = new LimitedMap<>(maxParam);
|
this.map = new LimitedTreeMap<>(maxParam);
|
||||||
this.percentEncoder = PercentEncoders.getQueryEncoder(StandardCharsets.UTF_8);
|
this.percentEncoder = PercentEncoders.getQueryEncoder(StandardCharsets.UTF_8);
|
||||||
this.percentDecoder = new PercentDecoder();
|
this.percentDecoder = new PercentDecoder();
|
||||||
this.contentType = contentType;
|
this.contentType = contentType;
|
||||||
|
|
|
@ -13,6 +13,7 @@ public class LRUCache<K, V> extends LinkedHashMap<K, V> {
|
||||||
this.cacheSize = cacheSize;
|
this.cacheSize = cacheSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
|
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
|
||||||
return size() >= cacheSize;
|
return size() >= cacheSize;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
package org.xbib.netty.http.common.util;
|
package org.xbib.netty.http.common.util;
|
||||||
|
|
||||||
import java.util.SortedSet;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.TreeMap;
|
|
||||||
|
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public class LimitedMap<K, V> extends TreeMap<K, SortedSet<V>> {
|
public class LimitedMap<K, V> extends LinkedHashMap<K, V> {
|
||||||
|
|
||||||
private final int limit;
|
private final int limit;
|
||||||
|
|
||||||
public LimitedMap(int limit) {
|
public LimitedMap(int limit) {
|
||||||
|
super(16, 0.75f, true);
|
||||||
this.limit = limit;
|
this.limit = limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SortedSet<V> put(K key, SortedSet<V> value) {
|
public V put(K key, V value) {
|
||||||
if (size() < limit) {
|
if (size() < limit) {
|
||||||
return super.put(key, value);
|
return super.put(key, value);
|
||||||
}
|
}
|
||||||
return null;
|
throw new IllegalArgumentException("size limit exceeded: " + limit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ public class LimitedSet<T extends CharSequence> extends TreeSet<T> {
|
||||||
if (size() < sizeLimit && t.length() <= elementMaximumLength) {
|
if (size() < sizeLimit && t.length() <= elementMaximumLength) {
|
||||||
return super.add(t);
|
return super.add(t);
|
||||||
}
|
}
|
||||||
return false;
|
throw new IllegalArgumentException("limit exceeded");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
package org.xbib.netty.http.common.util;
|
||||||
|
|
||||||
|
import java.util.SortedSet;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public class LimitedTreeMap<K, V> extends TreeMap<K, SortedSet<V>> {
|
||||||
|
|
||||||
|
private final int limit;
|
||||||
|
|
||||||
|
public LimitedTreeMap(int limit) {
|
||||||
|
this.limit = limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SortedSet<V> put(K key, SortedSet<V> value) {
|
||||||
|
if (size() < limit) {
|
||||||
|
return super.put(key, value);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
7
netty-http-server-reactive/build.gradle
Normal file
7
netty-http-server-reactive/build.gradle
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
dependencies {
|
||||||
|
compile project(':netty-http-server')
|
||||||
|
implementation "org.reactivestreams:reactive-streams:${project.property('reactivestreams.version')}"
|
||||||
|
testImplementation("org.reactivestreams:reactive-streams-tck:${project.property('reactivestreams.version')}") {
|
||||||
|
exclude module: 'testng'
|
||||||
|
}
|
||||||
|
}
|
|
@ -154,8 +154,11 @@ public class HandlerPublisher<T> extends ChannelDuplexHandler implements Publish
|
||||||
private State state = NO_SUBSCRIBER_OR_CONTEXT;
|
private State state = NO_SUBSCRIBER_OR_CONTEXT;
|
||||||
|
|
||||||
private volatile Subscriber<? super T> subscriber;
|
private volatile Subscriber<? super T> subscriber;
|
||||||
|
|
||||||
private ChannelHandlerContext ctx;
|
private ChannelHandlerContext ctx;
|
||||||
|
|
||||||
private long outstandingDemand = 0;
|
private long outstandingDemand = 0;
|
||||||
|
|
||||||
private Throwable noSubscriberError;
|
private Throwable noSubscriberError;
|
||||||
|
|
||||||
@Override
|
@Override
|
|
@ -1,4 +1,4 @@
|
||||||
package org.xbib.netty.http.server.test.reactive;
|
package org.xbib.netty.http.server.reactive.test;
|
||||||
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelOutboundHandlerAdapter;
|
import io.netty.channel.ChannelOutboundHandlerAdapter;
|
|
@ -1,4 +1,4 @@
|
||||||
package org.xbib.netty.http.server.test.reactive;
|
package org.xbib.netty.http.server.reactive.test;
|
||||||
|
|
||||||
import io.netty.bootstrap.Bootstrap;
|
import io.netty.bootstrap.Bootstrap;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
|
@ -44,8 +44,7 @@ class ChannelPublisherTest {
|
||||||
EventLoop eventLoop = group.next();
|
EventLoop eventLoop = group.next();
|
||||||
HandlerPublisher<Channel> handlerPublisher = new HandlerPublisher<>(eventLoop, Channel.class);
|
HandlerPublisher<Channel> handlerPublisher = new HandlerPublisher<>(eventLoop, Channel.class);
|
||||||
Bootstrap bootstrap = new Bootstrap();
|
Bootstrap bootstrap = new Bootstrap();
|
||||||
bootstrap
|
bootstrap.channel(NioServerSocketChannel.class)
|
||||||
.channel(NioServerSocketChannel.class)
|
|
||||||
.group(eventLoop)
|
.group(eventLoop)
|
||||||
.option(ChannelOption.AUTO_READ, false)
|
.option(ChannelOption.AUTO_READ, false)
|
||||||
.handler(handlerPublisher)
|
.handler(handlerPublisher)
|
|
@ -1,4 +1,4 @@
|
||||||
package org.xbib.netty.http.server.test.reactive;
|
package org.xbib.netty.http.server.reactive.test;
|
||||||
|
|
||||||
import io.netty.channel.AbstractChannel;
|
import io.netty.channel.AbstractChannel;
|
||||||
import io.netty.channel.ChannelConfig;
|
import io.netty.channel.ChannelConfig;
|
|
@ -1,14 +1,9 @@
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(":netty-http-common")
|
compile project(":netty-http-common")
|
||||||
implementation "io.netty:netty-handler:${project.property('netty.version')}"
|
compile "io.netty:netty-handler:${project.property('netty.version')}"
|
||||||
implementation "io.netty:netty-transport-native-epoll:${project.property('netty.version')}"
|
compile "io.netty:netty-transport-native-epoll:${project.property('netty.version')}"
|
||||||
implementation "io.netty:netty-tcnative-boringssl-static:${project.property('tcnative.version')}"
|
compile "io.netty:netty-tcnative-boringssl-static:${project.property('tcnative.version')}"
|
||||||
implementation "io.netty:netty-codec-http2:${project.property('netty.version')}"
|
compile "io.netty:netty-codec-http2:${project.property('netty.version')}"
|
||||||
implementation "org.xbib:net-url:${project.property('xbib-net-url.version')}"
|
compile "org.bouncycastle:bcpkix-jdk15on:${project.property('bouncycastle.version')}"
|
||||||
implementation "org.bouncycastle:bcpkix-jdk15on:${project.property('bouncycastle.version')}"
|
|
||||||
implementation "org.reactivestreams:reactive-streams:${project.property('reactivestreams.version')}"
|
|
||||||
testImplementation project(":netty-http-client")
|
testImplementation project(":netty-http-client")
|
||||||
testImplementation("org.reactivestreams:reactive-streams-tck:${project.property('reactivestreams.version')}") {
|
|
||||||
exclude module: 'testng'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import io.netty.handler.ssl.SslProvider;
|
||||||
import org.xbib.netty.http.common.HttpAddress;
|
import org.xbib.netty.http.common.HttpAddress;
|
||||||
import org.xbib.netty.http.common.SecurityUtil;
|
import org.xbib.netty.http.common.SecurityUtil;
|
||||||
import org.xbib.netty.http.server.endpoint.HttpEndpoint;
|
import org.xbib.netty.http.server.endpoint.HttpEndpoint;
|
||||||
import org.xbib.netty.http.server.endpoint.EndpointResolver;
|
import org.xbib.netty.http.server.endpoint.HttpEndpointResolver;
|
||||||
import org.xbib.netty.http.server.endpoint.service.Service;
|
import org.xbib.netty.http.server.endpoint.service.Service;
|
||||||
import org.xbib.netty.http.server.security.tls.SelfSignedCertificate;
|
import org.xbib.netty.http.server.security.tls.SelfSignedCertificate;
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ public class Domain {
|
||||||
|
|
||||||
private final SslContext sslContext;
|
private final SslContext sslContext;
|
||||||
|
|
||||||
private final List<EndpointResolver> endpointResolvers;
|
private final List<HttpEndpointResolver> httpEndpointResolvers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a {@code NamedServer} with the given name.
|
* Constructs a {@code NamedServer} with the given name.
|
||||||
|
@ -47,18 +47,18 @@ public class Domain {
|
||||||
* @param name the name, or null if it is the default server
|
* @param name the name, or null if it is the default server
|
||||||
* @param aliases alias names for the named server
|
* @param aliases alias names for the named server
|
||||||
* @param httpAddress HTTP address, used for determining if named server is secure or not
|
* @param httpAddress HTTP address, used for determining if named server is secure or not
|
||||||
* @param endpointResolvers the endpoint resolvers
|
* @param httpEndpointResolvers the endpoint resolvers
|
||||||
* @param sslContext SSL context or null
|
* @param sslContext SSL context or null
|
||||||
*/
|
*/
|
||||||
protected Domain(String name, Set<String> aliases,
|
protected Domain(String name, Set<String> aliases,
|
||||||
HttpAddress httpAddress,
|
HttpAddress httpAddress,
|
||||||
List<EndpointResolver> endpointResolvers,
|
List<HttpEndpointResolver> httpEndpointResolvers,
|
||||||
SslContext sslContext) {
|
SslContext sslContext) {
|
||||||
this.httpAddress = httpAddress;
|
this.httpAddress = httpAddress;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.sslContext = sslContext;
|
this.sslContext = sslContext;
|
||||||
this.aliases = Collections.unmodifiableSet(aliases);
|
this.aliases = Collections.unmodifiableSet(aliases);
|
||||||
this.endpointResolvers = endpointResolvers;
|
this.httpEndpointResolvers = httpEndpointResolvers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Builder builder() {
|
public static Builder builder() {
|
||||||
|
@ -99,10 +99,10 @@ public class Domain {
|
||||||
return aliases;
|
return aliases;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void execute(ServerRequest serverRequest, ServerResponse serverResponse) throws IOException {
|
public void handle(ServerRequest serverRequest, ServerResponse serverResponse) throws IOException {
|
||||||
if (endpointResolvers != null && !endpointResolvers.isEmpty()) {
|
if (httpEndpointResolvers != null && !httpEndpointResolvers.isEmpty()) {
|
||||||
for (EndpointResolver endpointResolver : endpointResolvers) {
|
for (HttpEndpointResolver httpEndpointResolver : httpEndpointResolvers) {
|
||||||
endpointResolver.resolve(serverRequest, serverResponse);
|
httpEndpointResolver.handle(serverRequest, serverResponse);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ServerResponse.write(serverResponse, HttpResponseStatus.NOT_IMPLEMENTED);
|
ServerResponse.write(serverResponse, HttpResponseStatus.NOT_IMPLEMENTED);
|
||||||
|
@ -122,7 +122,7 @@ public class Domain {
|
||||||
|
|
||||||
private Set<String> aliases;
|
private Set<String> aliases;
|
||||||
|
|
||||||
private List<EndpointResolver> endpointResolvers;
|
private List<HttpEndpointResolver> httpEndpointResolvers;
|
||||||
|
|
||||||
private TrustManagerFactory trustManagerFactory;
|
private TrustManagerFactory trustManagerFactory;
|
||||||
|
|
||||||
|
@ -146,7 +146,7 @@ public class Domain {
|
||||||
this.httpAddress = httpAddress;
|
this.httpAddress = httpAddress;
|
||||||
this.serverName = serverName;
|
this.serverName = serverName;
|
||||||
this.aliases = new LinkedHashSet<>();
|
this.aliases = new LinkedHashSet<>();
|
||||||
this.endpointResolvers = new ArrayList<>();
|
this.httpEndpointResolvers = new ArrayList<>();
|
||||||
this.trustManagerFactory = SecurityUtil.Defaults.DEFAULT_TRUST_MANAGER_FACTORY; // InsecureTrustManagerFactory.INSTANCE;
|
this.trustManagerFactory = SecurityUtil.Defaults.DEFAULT_TRUST_MANAGER_FACTORY; // InsecureTrustManagerFactory.INSTANCE;
|
||||||
this.sslProvider = SecurityUtil.Defaults.DEFAULT_SSL_PROVIDER;
|
this.sslProvider = SecurityUtil.Defaults.DEFAULT_SSL_PROVIDER;
|
||||||
this.ciphers = SecurityUtil.Defaults.DEFAULT_CIPHERS;
|
this.ciphers = SecurityUtil.Defaults.DEFAULT_CIPHERS;
|
||||||
|
@ -243,25 +243,25 @@ public class Domain {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder addEndpointResolver(EndpointResolver endpointResolver) {
|
public Builder addEndpointResolver(HttpEndpointResolver httpEndpointResolver) {
|
||||||
this.endpointResolvers.add(endpointResolver);
|
this.httpEndpointResolvers.add(httpEndpointResolver);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder singleEndpoint(String path, Service service) {
|
public Builder singleEndpoint(String path, Service service) {
|
||||||
addEndpointResolver(EndpointResolver.builder()
|
addEndpointResolver(HttpEndpointResolver.builder()
|
||||||
.addEndpoint(HttpEndpoint.builder().setPath(path).addFilter(service).build()).build());
|
.addEndpoint(HttpEndpoint.builder().setPath(path).addFilter(service).build()).build());
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder singleEndpoint(String prefix, String path, Service service) {
|
public Builder singleEndpoint(String prefix, String path, Service service) {
|
||||||
addEndpointResolver(EndpointResolver.builder()
|
addEndpointResolver(HttpEndpointResolver.builder()
|
||||||
.addEndpoint(HttpEndpoint.builder().setPrefix(prefix).setPath(path).addFilter(service).build()).build());
|
.addEndpoint(HttpEndpoint.builder().setPrefix(prefix).setPath(path).addFilter(service).build()).build());
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder singleEndpoint(String prefix, String path, Service service, String... methods) {
|
public Builder singleEndpoint(String prefix, String path, Service service, String... methods) {
|
||||||
addEndpointResolver(EndpointResolver.builder()
|
addEndpointResolver(HttpEndpointResolver.builder()
|
||||||
.addEndpoint(HttpEndpoint.builder().setPrefix(prefix).setPath(path).addFilter(service)
|
.addEndpoint(HttpEndpoint.builder().setPrefix(prefix).setPath(path).addFilter(service)
|
||||||
.setMethods(Arrays.asList(methods)).build()).build());
|
.setMethods(Arrays.asList(methods)).build()).build());
|
||||||
return this;
|
return this;
|
||||||
|
@ -282,12 +282,12 @@ public class Domain {
|
||||||
if (httpAddress.getVersion().majorVersion() == 2) {
|
if (httpAddress.getVersion().majorVersion() == 2) {
|
||||||
sslContextBuilder.applicationProtocolConfig(newApplicationProtocolConfig());
|
sslContextBuilder.applicationProtocolConfig(newApplicationProtocolConfig());
|
||||||
}
|
}
|
||||||
return new Domain(serverName, aliases, httpAddress, endpointResolvers, sslContextBuilder.build());
|
return new Domain(serverName, aliases, httpAddress, httpEndpointResolvers, sslContextBuilder.build());
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
throw new RuntimeException(t);
|
throw new RuntimeException(t);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return new Domain(serverName, aliases, httpAddress, endpointResolvers, null);
|
return new Domain(serverName, aliases, httpAddress, httpEndpointResolvers, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
package org.xbib.netty.http.server;
|
package org.xbib.netty.http.server;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.ByteBufInputStream;
|
||||||
|
import io.netty.channel.Channel;
|
||||||
import io.netty.handler.codec.http.HttpHeaders;
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
import io.netty.handler.codec.http.HttpMethod;
|
import io.netty.handler.codec.http.HttpMethod;
|
||||||
import org.xbib.net.URL;
|
import org.xbib.net.URL;
|
||||||
import org.xbib.netty.http.common.HttpParameters;
|
import org.xbib.netty.http.common.HttpParameters;
|
||||||
import org.xbib.netty.http.server.endpoint.EndpointInfo;
|
import org.xbib.netty.http.server.endpoint.HttpEndpointDescriptor;
|
||||||
|
|
||||||
import javax.net.ssl.SSLSession;
|
import javax.net.ssl.SSLSession;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -16,7 +18,9 @@ public interface ServerRequest {
|
||||||
|
|
||||||
URL getURL();
|
URL getURL();
|
||||||
|
|
||||||
EndpointInfo getEndpointInfo();
|
Channel getChannel();
|
||||||
|
|
||||||
|
HttpEndpointDescriptor getEndpointDescriptor();
|
||||||
|
|
||||||
void setContext(List<String> context);
|
void setContext(List<String> context);
|
||||||
|
|
||||||
|
@ -24,8 +28,6 @@ public interface ServerRequest {
|
||||||
|
|
||||||
void addPathParameter(String key, String value) throws IOException;
|
void addPathParameter(String key, String value) throws IOException;
|
||||||
|
|
||||||
void createParameters() throws IOException;
|
|
||||||
|
|
||||||
Map<String, String> getPathParameters();
|
Map<String, String> getPathParameters();
|
||||||
|
|
||||||
HttpMethod getMethod();
|
HttpMethod getMethod();
|
||||||
|
@ -40,12 +42,14 @@ public interface ServerRequest {
|
||||||
|
|
||||||
Integer getSequenceId();
|
Integer getSequenceId();
|
||||||
|
|
||||||
Integer streamId();
|
Integer getStreamId();
|
||||||
|
|
||||||
Integer requestId();
|
Integer getRequestId();
|
||||||
|
|
||||||
SSLSession getSession();
|
SSLSession getSession();
|
||||||
|
|
||||||
ByteBuf getContent();
|
ByteBuf getContent();
|
||||||
|
|
||||||
|
ByteBufInputStream getInputStream();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.xbib.netty.http.server;
|
package org.xbib.netty.http.server;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.ByteBufOutputStream;
|
||||||
import io.netty.buffer.ByteBufUtil;
|
import io.netty.buffer.ByteBufUtil;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
|
@ -30,10 +31,22 @@ public interface ServerResponse {
|
||||||
|
|
||||||
ServerResponse withCookie(Cookie cookie);
|
ServerResponse withCookie(Cookie cookie);
|
||||||
|
|
||||||
|
ByteBufOutputStream getOutputStream();
|
||||||
|
|
||||||
|
void flush();
|
||||||
|
|
||||||
|
void write(byte[] bytes);
|
||||||
|
|
||||||
|
void write(ByteBufOutputStream byteBufOutputStream);
|
||||||
|
|
||||||
void write(ByteBuf byteBuf);
|
void write(ByteBuf byteBuf);
|
||||||
|
|
||||||
void write(ChunkedInput<ByteBuf> chunkedInput);
|
void write(ChunkedInput<ByteBuf> chunkedInput);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience methods.
|
||||||
|
*/
|
||||||
|
|
||||||
static void write(ServerResponse serverResponse, int status) {
|
static void write(ServerResponse serverResponse, int status) {
|
||||||
write(serverResponse, HttpResponseStatus.valueOf(status));
|
write(serverResponse, HttpResponseStatus.valueOf(status));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,19 @@
|
||||||
package org.xbib.netty.http.server.endpoint;
|
package org.xbib.netty.http.server.endpoint;
|
||||||
|
|
||||||
import org.xbib.netty.http.server.ServerRequest;
|
import org.xbib.netty.http.server.ServerRequest;
|
||||||
|
import org.xbib.netty.http.server.ServerResponse;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public interface Endpoint {
|
public interface Endpoint<D extends EndpointDescriptor> {
|
||||||
|
|
||||||
String getPrefix();
|
String getPrefix();
|
||||||
|
|
||||||
String getPath();
|
String getPath();
|
||||||
|
|
||||||
boolean matches(EndpointInfo info);
|
boolean matches(D descriptor);
|
||||||
|
|
||||||
void resolveUriTemplate(ServerRequest serverRequest) throws IOException;
|
void resolveUriTemplate(ServerRequest serverRequest) throws IOException;
|
||||||
|
|
||||||
|
void handle(ServerRequest serverRequest, ServerResponse serverResponse) throws IOException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
package org.xbib.netty.http.server.endpoint;
|
||||||
|
|
||||||
|
public interface EndpointDescriptor {
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package org.xbib.netty.http.server.endpoint;
|
package org.xbib.netty.http.server.endpoint;
|
||||||
|
|
||||||
|
import org.xbib.net.Pair;
|
||||||
import org.xbib.net.QueryParameters;
|
import org.xbib.net.QueryParameters;
|
||||||
import org.xbib.net.path.PathMatcher;
|
import org.xbib.net.path.PathMatcher;
|
||||||
import org.xbib.net.path.PathNormalizer;
|
import org.xbib.net.path.PathNormalizer;
|
||||||
|
@ -13,7 +14,7 @@ import java.util.Arrays;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class HttpEndpoint {
|
public class HttpEndpoint implements Endpoint<HttpEndpointDescriptor> {
|
||||||
|
|
||||||
private static final PathMatcher pathMatcher = new PathMatcher();
|
private static final PathMatcher pathMatcher = new PathMatcher();
|
||||||
|
|
||||||
|
@ -50,32 +51,36 @@ public class HttpEndpoint {
|
||||||
.setFilters(endpoint.filters);
|
.setFilters(endpoint.filters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getPrefix() {
|
public String getPrefix() {
|
||||||
return prefix;
|
return prefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getPath() {
|
public String getPath() {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean matches(EndpointInfo info) {
|
@Override
|
||||||
|
public boolean matches(HttpEndpointDescriptor info) {
|
||||||
return pathMatcher.match(prefix + path, info.getPath()) &&
|
return pathMatcher.match(prefix + path, info.getPath()) &&
|
||||||
(methods == null || methods.isEmpty() || (methods.contains(info.getMethod()))) &&
|
(methods == null || methods.isEmpty() || (methods.contains(info.getMethod()))) &&
|
||||||
(contentTypes == null || contentTypes.isEmpty() || info.getContentType() == null ||
|
(contentTypes == null || contentTypes.isEmpty() || info.getContentType() == null ||
|
||||||
contentTypes.stream().anyMatch(info.getContentType()::startsWith));
|
contentTypes.stream().anyMatch(info.getContentType()::startsWith));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void resolveUriTemplate(ServerRequest serverRequest) throws IOException {
|
public void resolveUriTemplate(ServerRequest serverRequest) throws IOException {
|
||||||
if (pathMatcher.match(prefix + path, serverRequest.getEffectiveRequestPath() /*serverRequest.getRequest().uri()*/ )) {
|
if (pathMatcher.match(prefix + path, serverRequest.getEffectiveRequestPath())) {
|
||||||
QueryParameters queryParameters = pathMatcher.extractUriTemplateVariables(prefix + path,
|
QueryParameters queryParameters = pathMatcher.extractUriTemplateVariables(prefix + path,
|
||||||
serverRequest.getEffectiveRequestPath() /*serverRequest.getRequest().uri()*/ );
|
serverRequest.getEffectiveRequestPath());
|
||||||
for (QueryParameters.Pair<String, String> pair : queryParameters) {
|
for (Pair<String, String> pair : queryParameters) {
|
||||||
serverRequest.addPathParameter(pair.getFirst(), pair.getSecond());
|
serverRequest.addPathParameter(pair.getFirst(), pair.getSecond());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void executeFilters(ServerRequest serverRequest, ServerResponse serverResponse) throws IOException {
|
public void handle(ServerRequest serverRequest, ServerResponse serverResponse) throws IOException {
|
||||||
serverRequest.setContext(pathMatcher.tokenizePath(getPrefix()));
|
serverRequest.setContext(pathMatcher.tokenizePath(getPrefix()));
|
||||||
for (Service service : filters) {
|
for (Service service : filters) {
|
||||||
service.handle(serverRequest, serverResponse);
|
service.handle(serverRequest, serverResponse);
|
||||||
|
|
|
@ -4,7 +4,7 @@ import org.xbib.netty.http.server.transport.HttpServerRequest;
|
||||||
|
|
||||||
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
|
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
|
||||||
|
|
||||||
public class EndpointInfo implements Comparable<EndpointInfo> {
|
public class HttpEndpointDescriptor implements EndpointDescriptor, Comparable<HttpEndpointDescriptor> {
|
||||||
|
|
||||||
private final String path;
|
private final String path;
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ public class EndpointInfo implements Comparable<EndpointInfo> {
|
||||||
|
|
||||||
private final String contentType;
|
private final String contentType;
|
||||||
|
|
||||||
public EndpointInfo(HttpServerRequest serverRequest) {
|
public HttpEndpointDescriptor(HttpServerRequest serverRequest) {
|
||||||
this.path = extractPath(serverRequest.getRequest().uri());
|
this.path = extractPath(serverRequest.getRequest().uri());
|
||||||
this.method = serverRequest.getRequest().method().name();
|
this.method = serverRequest.getRequest().method().name();
|
||||||
this.contentType = serverRequest.getRequest().headers().get(CONTENT_TYPE);
|
this.contentType = serverRequest.getRequest().headers().get(CONTENT_TYPE);
|
||||||
|
@ -42,11 +42,11 @@ public class EndpointInfo implements Comparable<EndpointInfo> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
return o instanceof EndpointInfo && toString().equals(o.toString());
|
return o instanceof HttpEndpointDescriptor && toString().equals(o.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(EndpointInfo o) {
|
public int compareTo(HttpEndpointDescriptor o) {
|
||||||
return toString().compareTo(o.toString());
|
return toString().compareTo(o.toString());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.xbib.netty.http.server.endpoint;
|
package org.xbib.netty.http.server.endpoint;
|
||||||
|
|
||||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
|
import org.xbib.netty.http.common.util.LimitedMap;
|
||||||
import org.xbib.netty.http.server.ServerRequest;
|
import org.xbib.netty.http.server.ServerRequest;
|
||||||
import org.xbib.netty.http.server.ServerResponse;
|
import org.xbib.netty.http.server.ServerResponse;
|
||||||
import org.xbib.netty.http.server.annotation.Endpoint;
|
import org.xbib.netty.http.server.annotation.Endpoint;
|
||||||
|
@ -17,9 +18,9 @@ import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class EndpointResolver {
|
public class HttpEndpointResolver {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(EndpointResolver.class.getName());
|
private static final Logger logger = Logger.getLogger(HttpEndpointResolver.class.getName());
|
||||||
|
|
||||||
private final HttpEndpoint defaultEndpoint;
|
private final HttpEndpoint defaultEndpoint;
|
||||||
|
|
||||||
|
@ -27,31 +28,32 @@ public class EndpointResolver {
|
||||||
|
|
||||||
private final EndpointDispatcher endpointDispatcher;
|
private final EndpointDispatcher endpointDispatcher;
|
||||||
|
|
||||||
private final LRUCache<EndpointInfo, List<HttpEndpoint>> endpointInfos;
|
private final LimitedMap<HttpEndpointDescriptor, List<HttpEndpoint>> endpointDescriptors;
|
||||||
|
|
||||||
private EndpointResolver(HttpEndpoint defaultEndpoint,
|
private HttpEndpointResolver(HttpEndpoint defaultEndpoint,
|
||||||
List<HttpEndpoint> endpoints,
|
List<HttpEndpoint> endpoints,
|
||||||
EndpointDispatcher endpointDispatcher,
|
EndpointDispatcher endpointDispatcher,
|
||||||
int cacheSize) {
|
int limit) {
|
||||||
this.defaultEndpoint = defaultEndpoint == null ? createDefaultEndpoint() : defaultEndpoint;
|
this.defaultEndpoint = defaultEndpoint == null ? createDefaultEndpoint() : defaultEndpoint;
|
||||||
this.endpoints = endpoints;
|
this.endpoints = endpoints;
|
||||||
this.endpointDispatcher = endpointDispatcher;
|
this.endpointDispatcher = endpointDispatcher;
|
||||||
this.endpointInfos = new LRUCache<>(cacheSize);
|
this.endpointDescriptors = new LimitedMap<>(limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void resolve(ServerRequest serverRequest, ServerResponse serverResponse) throws IOException {
|
public void handle(ServerRequest serverRequest, ServerResponse serverResponse) throws IOException {
|
||||||
EndpointInfo endpointInfo = serverRequest.getEndpointInfo();
|
HttpEndpointDescriptor httpEndpointDescriptor = serverRequest.getEndpointDescriptor();
|
||||||
endpointInfos.putIfAbsent(endpointInfo, endpoints.stream()
|
endpointDescriptors.putIfAbsent(httpEndpointDescriptor, endpoints.stream()
|
||||||
.filter(endpoint -> endpoint.matches(endpointInfo))
|
.filter(endpoint -> endpoint.matches(httpEndpointDescriptor))
|
||||||
.sorted(new HttpEndpoint.EndpointPathComparator(endpointInfo.getPath())).collect(Collectors.toList()));
|
.sorted(new HttpEndpoint.EndpointPathComparator(httpEndpointDescriptor.getPath())).collect(Collectors.toList()));
|
||||||
List<HttpEndpoint> matchingEndpoints = endpointInfos.get(endpointInfo);
|
List<HttpEndpoint> matchingEndpoints = endpointDescriptors.get(httpEndpointDescriptor);
|
||||||
if (logger.isLoggable(Level.FINE)) {
|
if (logger.isLoggable(Level.FINE)) {
|
||||||
logger.log(Level.FINE, "endpoint info = " + endpointInfo + " matching endpoints = " + matchingEndpoints + " cache size=" + endpointInfos.size());
|
logger.log(Level.FINE, () -> "endpoint = " + httpEndpointDescriptor +
|
||||||
|
" matching endpoints = " + matchingEndpoints);
|
||||||
}
|
}
|
||||||
if (matchingEndpoints.isEmpty()) {
|
if (matchingEndpoints.isEmpty()) {
|
||||||
if (defaultEndpoint != null) {
|
if (defaultEndpoint != null) {
|
||||||
defaultEndpoint.resolveUriTemplate(serverRequest);
|
defaultEndpoint.resolveUriTemplate(serverRequest);
|
||||||
defaultEndpoint.executeFilters(serverRequest, serverResponse);
|
defaultEndpoint.handle(serverRequest, serverResponse);
|
||||||
if (endpointDispatcher != null) {
|
if (endpointDispatcher != null) {
|
||||||
endpointDispatcher.dispatch(defaultEndpoint, serverRequest, serverResponse);
|
endpointDispatcher.dispatch(defaultEndpoint, serverRequest, serverResponse);
|
||||||
}
|
}
|
||||||
|
@ -61,7 +63,7 @@ public class EndpointResolver {
|
||||||
} else {
|
} else {
|
||||||
for (HttpEndpoint endpoint : matchingEndpoints) {
|
for (HttpEndpoint endpoint : matchingEndpoints) {
|
||||||
endpoint.resolveUriTemplate(serverRequest);
|
endpoint.resolveUriTemplate(serverRequest);
|
||||||
endpoint.executeFilters(serverRequest, serverResponse);
|
endpoint.handle(serverRequest, serverResponse);
|
||||||
if (serverResponse.getStatus() != null) {
|
if (serverResponse.getStatus() != null) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -77,8 +79,8 @@ public class EndpointResolver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<EndpointInfo, List<HttpEndpoint>> getEndpointInfos() {
|
public Map<HttpEndpointDescriptor, List<HttpEndpoint>> getEndpointDescriptors() {
|
||||||
return endpointInfos;
|
return endpointDescriptors;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected HttpEndpoint createDefaultEndpoint() {
|
protected HttpEndpoint createDefaultEndpoint() {
|
||||||
|
@ -119,7 +121,7 @@ public class EndpointResolver {
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
|
|
||||||
private int cacheSize;
|
private int limit;
|
||||||
|
|
||||||
private String prefix;
|
private String prefix;
|
||||||
|
|
||||||
|
@ -130,12 +132,12 @@ public class EndpointResolver {
|
||||||
private EndpointDispatcher endpointDispatcher;
|
private EndpointDispatcher endpointDispatcher;
|
||||||
|
|
||||||
Builder() {
|
Builder() {
|
||||||
this.cacheSize = 1024;
|
this.limit = 1024;
|
||||||
this.endpoints = new ArrayList<>();
|
this.endpoints = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setCacheSize(int cacheSize) {
|
public Builder setLimit(int limit) {
|
||||||
this.cacheSize = cacheSize;
|
this.limit = limit;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,8 +199,8 @@ public class EndpointResolver {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EndpointResolver build() {
|
public HttpEndpointResolver build() {
|
||||||
return new EndpointResolver(defaultEndpoint, endpoints, endpointDispatcher, cacheSize);
|
return new HttpEndpointResolver(defaultEndpoint, endpoints, endpointDispatcher, limit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -81,7 +81,7 @@ abstract class BaseTransport implements Transport {
|
||||||
*/
|
*/
|
||||||
static void handle(Domain domain, HttpServerRequest serverRequest, ServerResponse serverResponse) throws IOException {
|
static void handle(Domain domain, HttpServerRequest serverRequest, ServerResponse serverResponse) throws IOException {
|
||||||
// create server URL and parse parameters from query string, path, and parse body, if exists
|
// create server URL and parse parameters from query string, path, and parse body, if exists
|
||||||
serverRequest.createParameters();
|
serverRequest.handleParameters();
|
||||||
domain.execute(serverRequest, serverResponse);
|
domain.handle(serverRequest, serverResponse);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.xbib.netty.http.server.transport;
|
package org.xbib.netty.http.server.transport;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.ByteBufOutputStream;
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.ChannelFuture;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
import io.netty.channel.ChannelFutureListener;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
@ -42,6 +43,8 @@ public class Http2ServerResponse implements ServerResponse {
|
||||||
|
|
||||||
private HttpResponseStatus httpResponseStatus;
|
private HttpResponseStatus httpResponseStatus;
|
||||||
|
|
||||||
|
private ByteBufOutputStream byteBufOutputStream;
|
||||||
|
|
||||||
public Http2ServerResponse(HttpServerRequest serverRequest) {
|
public Http2ServerResponse(HttpServerRequest serverRequest) {
|
||||||
Objects.requireNonNull(serverRequest);
|
Objects.requireNonNull(serverRequest);
|
||||||
Objects.requireNonNull(serverRequest.getChannelHandlerContext());
|
Objects.requireNonNull(serverRequest.getChannelHandlerContext());
|
||||||
|
@ -96,9 +99,31 @@ public class Http2ServerResponse implements ServerResponse {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ByteBufOutputStream getOutputStream() {
|
||||||
|
this.byteBufOutputStream = new ByteBufOutputStream(ctx.alloc().buffer());
|
||||||
|
return byteBufOutputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void flush() {
|
||||||
|
write((ByteBuf) null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(byte[] bytes) {
|
||||||
|
ByteBuf byteBuf = ctx.alloc().buffer(bytes.length);
|
||||||
|
byteBuf.writeBytes(bytes);
|
||||||
|
write(byteBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBufOutputStream byteBufOutputStream) {
|
||||||
|
write(byteBufOutputStream.buffer());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(ByteBuf byteBuf) {
|
public void write(ByteBuf byteBuf) {
|
||||||
Objects.requireNonNull(byteBuf);
|
|
||||||
if (httpResponseStatus == null) {
|
if (httpResponseStatus == null) {
|
||||||
httpResponseStatus = HttpResponseStatus.OK;
|
httpResponseStatus = HttpResponseStatus.OK;
|
||||||
}
|
}
|
||||||
|
@ -107,8 +132,10 @@ public class Http2ServerResponse implements ServerResponse {
|
||||||
headers.add(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_OCTET_STREAM);
|
headers.add(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_OCTET_STREAM);
|
||||||
}
|
}
|
||||||
if (!headers.contains(HttpHeaderNames.CONTENT_LENGTH) && !headers.contains(HttpHeaderNames.TRANSFER_ENCODING)) {
|
if (!headers.contains(HttpHeaderNames.CONTENT_LENGTH) && !headers.contains(HttpHeaderNames.TRANSFER_ENCODING)) {
|
||||||
|
if (byteBuf != null) {
|
||||||
headers.add(HttpHeaderNames.CONTENT_LENGTH, Long.toString(byteBuf.readableBytes()));
|
headers.add(HttpHeaderNames.CONTENT_LENGTH, Long.toString(byteBuf.readableBytes()));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (serverRequest != null && "close".equalsIgnoreCase(serverRequest.getHeaders().get(HttpHeaderNames.CONNECTION)) &&
|
if (serverRequest != null && "close".equalsIgnoreCase(serverRequest.getHeaders().get(HttpHeaderNames.CONNECTION)) &&
|
||||||
!headers.contains(HttpHeaderNames.CONNECTION)) {
|
!headers.contains(HttpHeaderNames.CONNECTION)) {
|
||||||
headers.add(HttpHeaderNames.CONNECTION, "close");
|
headers.add(HttpHeaderNames.CONNECTION, "close");
|
||||||
|
@ -117,21 +144,20 @@ public class Http2ServerResponse implements ServerResponse {
|
||||||
headers.add(HttpHeaderNames.DATE, DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now(ZoneOffset.UTC)));
|
headers.add(HttpHeaderNames.DATE, DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now(ZoneOffset.UTC)));
|
||||||
}
|
}
|
||||||
headers.add(HttpHeaderNames.SERVER, ServerName.getServerName());
|
headers.add(HttpHeaderNames.SERVER, ServerName.getServerName());
|
||||||
|
|
||||||
if (serverRequest != null) {
|
if (serverRequest != null) {
|
||||||
Integer streamId = serverRequest.streamId();
|
Integer streamId = serverRequest.getStreamId();
|
||||||
if (streamId != null) {
|
if (streamId != null) {
|
||||||
headers.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), streamId);
|
headers.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), streamId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ctx.channel().isWritable()) {
|
if (ctx.channel().isWritable()) {
|
||||||
Http2Headers http2Headers = new DefaultHttp2Headers().status(httpResponseStatus.codeAsText()).add(headers);
|
Http2Headers http2Headers = new DefaultHttp2Headers().status(httpResponseStatus.codeAsText()).add(headers);
|
||||||
Http2HeadersFrame http2HeadersFrame = new DefaultHttp2HeadersFrame(http2Headers, false);
|
Http2HeadersFrame http2HeadersFrame = new DefaultHttp2HeadersFrame(http2Headers, byteBuf == null);
|
||||||
logger.log(Level.FINEST, http2HeadersFrame::toString);
|
|
||||||
ctx.channel().write(http2HeadersFrame);
|
ctx.channel().write(http2HeadersFrame);
|
||||||
|
if (byteBuf != null) {
|
||||||
Http2DataFrame http2DataFrame = new DefaultHttp2DataFrame(byteBuf, true);
|
Http2DataFrame http2DataFrame = new DefaultHttp2DataFrame(byteBuf, true);
|
||||||
logger.log(Level.FINEST, http2DataFrame::toString);
|
|
||||||
ctx.channel().write(http2DataFrame);
|
ctx.channel().write(http2DataFrame);
|
||||||
|
}
|
||||||
ctx.channel().flush();
|
ctx.channel().flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,23 @@
|
||||||
package org.xbib.netty.http.server.transport;
|
package org.xbib.netty.http.server.transport;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.ByteBufInputStream;
|
||||||
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.handler.codec.http.FullHttpRequest;
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpHeaders;
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
import io.netty.handler.codec.http.HttpMethod;
|
import io.netty.handler.codec.http.HttpMethod;
|
||||||
import io.netty.handler.codec.http.HttpUtil;
|
import io.netty.handler.codec.http.HttpUtil;
|
||||||
|
import org.xbib.net.Pair;
|
||||||
import org.xbib.net.QueryParameters;
|
import org.xbib.net.QueryParameters;
|
||||||
import org.xbib.net.URL;
|
import org.xbib.net.URL;
|
||||||
import org.xbib.netty.http.common.HttpParameters;
|
import org.xbib.netty.http.common.HttpParameters;
|
||||||
import org.xbib.netty.http.server.ServerRequest;
|
import org.xbib.netty.http.server.ServerRequest;
|
||||||
import org.xbib.netty.http.server.endpoint.EndpointInfo;
|
import org.xbib.netty.http.server.endpoint.HttpEndpointDescriptor;
|
||||||
|
|
||||||
import javax.net.ssl.SSLSession;
|
import javax.net.ssl.SSLSession;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.nio.charset.MalformedInputException;
|
import java.nio.charset.MalformedInputException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.charset.UnmappableCharacterException;
|
import java.nio.charset.UnmappableCharacterException;
|
||||||
|
@ -40,7 +44,7 @@ public class HttpServerRequest implements ServerRequest {
|
||||||
|
|
||||||
private FullHttpRequest httpRequest;
|
private FullHttpRequest httpRequest;
|
||||||
|
|
||||||
private EndpointInfo info;
|
private HttpEndpointDescriptor info;
|
||||||
|
|
||||||
private HttpParameters parameters;
|
private HttpParameters parameters;
|
||||||
|
|
||||||
|
@ -54,6 +58,26 @@ public class HttpServerRequest implements ServerRequest {
|
||||||
|
|
||||||
private SSLSession sslSession;
|
private SSLSession sslSession;
|
||||||
|
|
||||||
|
public void handleParameters() throws IOException {
|
||||||
|
try {
|
||||||
|
HttpParameters httpParameters = new HttpParameters();
|
||||||
|
URL.Builder builder = URL.builder().path(getRequest().uri());
|
||||||
|
this.url = builder.build();
|
||||||
|
QueryParameters queryParameters = url.getQueryParams();
|
||||||
|
ByteBuf byteBuf = httpRequest.content();
|
||||||
|
if (APPLICATION_FORM_URL_ENCODED.equals(HttpUtil.getMimeType(httpRequest)) && byteBuf != null) {
|
||||||
|
String content = byteBuf.toString(HttpUtil.getCharset(httpRequest, StandardCharsets.ISO_8859_1));
|
||||||
|
queryParameters.addPercentEncodedBody(content);
|
||||||
|
}
|
||||||
|
for (Pair<String, String> pair : queryParameters) {
|
||||||
|
httpParameters.add(pair.getFirst(), pair.getSecond());
|
||||||
|
}
|
||||||
|
this.parameters = httpParameters;
|
||||||
|
} catch (MalformedInputException | UnmappableCharacterException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void setChannelHandlerContext(ChannelHandlerContext ctx) {
|
public void setChannelHandlerContext(ChannelHandlerContext ctx) {
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
}
|
}
|
||||||
|
@ -64,7 +88,7 @@ public class HttpServerRequest implements ServerRequest {
|
||||||
|
|
||||||
public void setRequest(FullHttpRequest fullHttpRequest) {
|
public void setRequest(FullHttpRequest fullHttpRequest) {
|
||||||
this.httpRequest = fullHttpRequest;
|
this.httpRequest = fullHttpRequest;
|
||||||
this.info = new EndpointInfo(this);
|
this.info = new HttpEndpointDescriptor(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FullHttpRequest getRequest() {
|
public FullHttpRequest getRequest() {
|
||||||
|
@ -77,7 +101,12 @@ public class HttpServerRequest implements ServerRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EndpointInfo getEndpointInfo() {
|
public Channel getChannel() {
|
||||||
|
return ctx.channel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpEndpointDescriptor getEndpointDescriptor() {
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +128,7 @@ public class HttpServerRequest implements ServerRequest {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getEffectiveRequestPath() {
|
public String getEffectiveRequestPath() {
|
||||||
String path = getEndpointInfo().getPath();
|
String path = getEndpointDescriptor().getPath();
|
||||||
String effective = contextPath != null && !PATH_SEPARATOR.equals(contextPath) && path.startsWith(contextPath) ?
|
String effective = contextPath != null && !PATH_SEPARATOR.equals(contextPath) && path.startsWith(contextPath) ?
|
||||||
path.substring(contextPath.length()) : path;
|
path.substring(contextPath.length()) : path;
|
||||||
return effective.isEmpty() ? PATH_SEPARATOR : effective;
|
return effective.isEmpty() ? PATH_SEPARATOR : effective;
|
||||||
|
@ -126,27 +155,6 @@ public class HttpServerRequest implements ServerRequest {
|
||||||
return httpRequest.headers();
|
return httpRequest.headers();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void createParameters() throws IOException {
|
|
||||||
try {
|
|
||||||
HttpParameters httpParameters = new HttpParameters();
|
|
||||||
URL.Builder builder = URL.builder().path(getRequest().uri());
|
|
||||||
this.url = builder.build();
|
|
||||||
QueryParameters queryParameters = url.getQueryParams();
|
|
||||||
ByteBuf byteBuf = httpRequest.content();
|
|
||||||
if (APPLICATION_FORM_URL_ENCODED.equals(HttpUtil.getMimeType(httpRequest)) && byteBuf != null) {
|
|
||||||
String content = byteBuf.toString(HttpUtil.getCharset(httpRequest, StandardCharsets.ISO_8859_1));
|
|
||||||
queryParameters.addPercentEncodedBody(content);
|
|
||||||
}
|
|
||||||
for (QueryParameters.Pair<String, String> pair : queryParameters) {
|
|
||||||
httpParameters.add(pair.getFirst(), pair.getSecond());
|
|
||||||
}
|
|
||||||
this.parameters = httpParameters;
|
|
||||||
} catch (MalformedInputException | UnmappableCharacterException e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HttpParameters getParameters() {
|
public HttpParameters getParameters() {
|
||||||
return parameters;
|
return parameters;
|
||||||
|
@ -166,7 +174,7 @@ public class HttpServerRequest implements ServerRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Integer streamId() {
|
public Integer getStreamId() {
|
||||||
return streamId;
|
return streamId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,7 +183,7 @@ public class HttpServerRequest implements ServerRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Integer requestId() {
|
public Integer getRequestId() {
|
||||||
return requestId;
|
return requestId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,6 +201,11 @@ public class HttpServerRequest implements ServerRequest {
|
||||||
return httpRequest.content();
|
return httpRequest.content();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ByteBufInputStream getInputStream() {
|
||||||
|
return new ByteBufInputStream(getContent(), true);
|
||||||
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "ServerRequest[request=" + httpRequest + "]";
|
return "ServerRequest[request=" + httpRequest + "]";
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package org.xbib.netty.http.server.transport;
|
package org.xbib.netty.http.server.transport;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.ByteBufOutputStream;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.ChannelFuture;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
import io.netty.channel.ChannelFutureListener;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
@ -23,6 +25,7 @@ import org.xbib.netty.http.server.ServerResponse;
|
||||||
import org.xbib.netty.http.server.cookie.ServerCookieEncoder;
|
import org.xbib.netty.http.server.cookie.ServerCookieEncoder;
|
||||||
import org.xbib.netty.http.server.handler.http.HttpPipelinedResponse;
|
import org.xbib.netty.http.server.handler.http.HttpPipelinedResponse;
|
||||||
|
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.time.ZoneOffset;
|
import java.time.ZoneOffset;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
|
@ -45,6 +48,8 @@ public class HttpServerResponse implements ServerResponse {
|
||||||
|
|
||||||
private HttpResponseStatus httpResponseStatus;
|
private HttpResponseStatus httpResponseStatus;
|
||||||
|
|
||||||
|
private ByteBufOutputStream byteBufOutputStream;
|
||||||
|
|
||||||
public HttpServerResponse(HttpServerRequest serverRequest) {
|
public HttpServerResponse(HttpServerRequest serverRequest) {
|
||||||
Objects.requireNonNull(serverRequest, "serverRequest");
|
Objects.requireNonNull(serverRequest, "serverRequest");
|
||||||
Objects.requireNonNull(serverRequest.getChannelHandlerContext(), "serverRequest channelHandlerContext");
|
Objects.requireNonNull(serverRequest.getChannelHandlerContext(), "serverRequest channelHandlerContext");
|
||||||
|
@ -100,9 +105,31 @@ public class HttpServerResponse implements ServerResponse {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ByteBufOutputStream getOutputStream() {
|
||||||
|
this.byteBufOutputStream = new ByteBufOutputStream(ctx.alloc().buffer());
|
||||||
|
return byteBufOutputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void flush() {
|
||||||
|
write((ByteBuf) null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(byte[] bytes) {
|
||||||
|
ByteBuf byteBuf = ctx.alloc().buffer(bytes.length);
|
||||||
|
byteBuf.writeBytes(bytes);
|
||||||
|
write(byteBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBufOutputStream byteBufOutputStream) {
|
||||||
|
write(byteBufOutputStream.buffer());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(ByteBuf byteBuf) {
|
public void write(ByteBuf byteBuf) {
|
||||||
Objects.requireNonNull(byteBuf);
|
|
||||||
if (httpResponseStatus == null) {
|
if (httpResponseStatus == null) {
|
||||||
httpResponseStatus = HttpResponseStatus.OK;
|
httpResponseStatus = HttpResponseStatus.OK;
|
||||||
}
|
}
|
||||||
|
@ -110,9 +137,12 @@ public class HttpServerResponse implements ServerResponse {
|
||||||
if (contentType == null) {
|
if (contentType == null) {
|
||||||
headers.add(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_OCTET_STREAM);
|
headers.add(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_OCTET_STREAM);
|
||||||
}
|
}
|
||||||
|
if (httpResponseStatus.code() >= 200 && httpResponseStatus.code() != 204) {
|
||||||
if (!headers.contains(HttpHeaderNames.CONTENT_LENGTH) && !headers.contains(HttpHeaderNames.TRANSFER_ENCODING)) {
|
if (!headers.contains(HttpHeaderNames.CONTENT_LENGTH) && !headers.contains(HttpHeaderNames.TRANSFER_ENCODING)) {
|
||||||
int length = byteBuf.readableBytes();
|
if (byteBuf != null) {
|
||||||
headers.add(HttpHeaderNames.CONTENT_LENGTH, Long.toString(length));
|
headers.add(HttpHeaderNames.CONTENT_LENGTH, Long.toString(byteBuf.readableBytes()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (serverRequest != null && "close".equalsIgnoreCase(serverRequest.getHeaders().get(HttpHeaderNames.CONNECTION)) &&
|
if (serverRequest != null && "close".equalsIgnoreCase(serverRequest.getHeaders().get(HttpHeaderNames.CONNECTION)) &&
|
||||||
!headers.contains(HttpHeaderNames.CONNECTION)) {
|
!headers.contains(HttpHeaderNames.CONNECTION)) {
|
||||||
|
@ -123,9 +153,9 @@ public class HttpServerResponse implements ServerResponse {
|
||||||
}
|
}
|
||||||
headers.add(HttpHeaderNames.SERVER, ServerName.getServerName());
|
headers.add(HttpHeaderNames.SERVER, ServerName.getServerName());
|
||||||
if (ctx.channel().isWritable()) {
|
if (ctx.channel().isWritable()) {
|
||||||
FullHttpResponse fullHttpResponse =
|
FullHttpResponse fullHttpResponse = byteBuf != null ?
|
||||||
new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, httpResponseStatus, byteBuf, headers, trailingHeaders);
|
new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, httpResponseStatus, byteBuf, headers, trailingHeaders) :
|
||||||
logger.log(Level.FINEST, fullHttpResponse.headers()::toString);
|
new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, httpResponseStatus, Unpooled.buffer(0), headers, trailingHeaders);
|
||||||
if (serverRequest != null && serverRequest.getSequenceId() != null) {
|
if (serverRequest != null && serverRequest.getSequenceId() != null) {
|
||||||
HttpPipelinedResponse httpPipelinedResponse = new HttpPipelinedResponse(fullHttpResponse,
|
HttpPipelinedResponse httpPipelinedResponse = new HttpPipelinedResponse(fullHttpResponse,
|
||||||
ctx.channel().newPromise(), serverRequest.getSequenceId());
|
ctx.channel().newPromise(), serverRequest.getSequenceId());
|
||||||
|
|
|
@ -10,7 +10,7 @@ import org.xbib.netty.http.common.HttpAddress;
|
||||||
import org.xbib.netty.http.server.Server;
|
import org.xbib.netty.http.server.Server;
|
||||||
import org.xbib.netty.http.server.ServerResponse;
|
import org.xbib.netty.http.server.ServerResponse;
|
||||||
import org.xbib.netty.http.server.endpoint.HttpEndpoint;
|
import org.xbib.netty.http.server.endpoint.HttpEndpoint;
|
||||||
import org.xbib.netty.http.server.endpoint.EndpointResolver;
|
import org.xbib.netty.http.server.endpoint.HttpEndpointResolver;
|
||||||
import org.xbib.netty.http.server.Domain;
|
import org.xbib.netty.http.server.Domain;
|
||||||
import org.xbib.netty.http.server.endpoint.service.FileService;
|
import org.xbib.netty.http.server.endpoint.service.FileService;
|
||||||
import org.xbib.netty.http.server.endpoint.service.Service;
|
import org.xbib.netty.http.server.endpoint.service.Service;
|
||||||
|
@ -38,15 +38,15 @@ class EndpointTest {
|
||||||
Path vartmp = Paths.get("/var/tmp/");
|
Path vartmp = Paths.get("/var/tmp/");
|
||||||
Service service = new FileService(vartmp);
|
Service service = new FileService(vartmp);
|
||||||
HttpAddress httpAddress = HttpAddress.http1("localhost", 8008);
|
HttpAddress httpAddress = HttpAddress.http1("localhost", 8008);
|
||||||
EndpointResolver endpointResolver = EndpointResolver.builder()
|
HttpEndpointResolver httpEndpointResolver = HttpEndpointResolver.builder()
|
||||||
.addEndpoint(HttpEndpoint.builder().setPath("/**").build())
|
.addEndpoint(HttpEndpoint.builder().setPath("/**").build())
|
||||||
.setDispatcher((endpoint, req, resp) -> {
|
.setDispatcher((endpoint, req, resp) -> {
|
||||||
logger.log(Level.FINE, "dispatching endpoint=" + endpoint + " req=" + req);
|
logger.log(Level.FINE, "dispatching endpoint = " + endpoint + " req = " + req);
|
||||||
service.handle(req, resp);
|
service.handle(req, resp);
|
||||||
})
|
})
|
||||||
.build();
|
.build();
|
||||||
Domain domain = Domain.builder(httpAddress)
|
Domain domain = Domain.builder(httpAddress)
|
||||||
.addEndpointResolver(endpointResolver)
|
.addEndpointResolver(httpEndpointResolver)
|
||||||
.build();
|
.build();
|
||||||
Server server = Server.builder(domain)
|
Server server = Server.builder(domain)
|
||||||
.build();
|
.build();
|
||||||
|
@ -78,15 +78,15 @@ class EndpointTest {
|
||||||
Path vartmp = Paths.get("/var/tmp/");
|
Path vartmp = Paths.get("/var/tmp/");
|
||||||
Service service = new FileService(vartmp);
|
Service service = new FileService(vartmp);
|
||||||
HttpAddress httpAddress = HttpAddress.http1("localhost", 8008);
|
HttpAddress httpAddress = HttpAddress.http1("localhost", 8008);
|
||||||
EndpointResolver endpointResolver = EndpointResolver.builder()
|
HttpEndpointResolver httpEndpointResolver = HttpEndpointResolver.builder()
|
||||||
.addEndpoint(HttpEndpoint.builder().setPrefix("/").setPath("/**").build())
|
.addEndpoint(HttpEndpoint.builder().setPrefix("/").setPath("/**").build())
|
||||||
.setDispatcher((endpoint, req, resp) -> {
|
.setDispatcher((endpoint, req, resp) -> {
|
||||||
logger.log(Level.FINE, "dispatching endpoint=" + endpoint + " req=" + req);
|
logger.log(Level.FINE, "dispatching endpoint = " + endpoint + " req = " + req);
|
||||||
service.handle(req, resp);
|
service.handle(req, resp);
|
||||||
})
|
})
|
||||||
.build();
|
.build();
|
||||||
Domain domain = Domain.builder(httpAddress)
|
Domain domain = Domain.builder(httpAddress)
|
||||||
.addEndpointResolver(endpointResolver)
|
.addEndpointResolver(httpEndpointResolver)
|
||||||
.build();
|
.build();
|
||||||
Server server = Server.builder(domain)
|
Server server = Server.builder(domain)
|
||||||
.build();
|
.build();
|
||||||
|
@ -119,17 +119,17 @@ class EndpointTest {
|
||||||
Path vartmp = Paths.get("/var/tmp/");
|
Path vartmp = Paths.get("/var/tmp/");
|
||||||
Service service = new FileService(vartmp);
|
Service service = new FileService(vartmp);
|
||||||
HttpAddress httpAddress = HttpAddress.http1("localhost", 8008);
|
HttpAddress httpAddress = HttpAddress.http1("localhost", 8008);
|
||||||
EndpointResolver endpointResolver = EndpointResolver.builder()
|
HttpEndpointResolver httpEndpointResolver = HttpEndpointResolver.builder()
|
||||||
.addEndpoint(HttpEndpoint.builder().setPrefix("/static").setPath("/**").build())
|
.addEndpoint(HttpEndpoint.builder().setPrefix("/static").setPath("/**").build())
|
||||||
.addEndpoint(HttpEndpoint.builder().setPrefix("/static1").setPath("/**").build())
|
.addEndpoint(HttpEndpoint.builder().setPrefix("/static1").setPath("/**").build())
|
||||||
.addEndpoint(HttpEndpoint.builder().setPrefix("/static2").setPath("/**").build())
|
.addEndpoint(HttpEndpoint.builder().setPrefix("/static2").setPath("/**").build())
|
||||||
.setDispatcher((endpoint, req, resp) -> {
|
.setDispatcher((endpoint, req, resp) -> {
|
||||||
logger.log(Level.FINE, "dispatching endpoint=" + endpoint + " req=" + req);
|
logger.log(Level.FINE, "dispatching endpoint = " + endpoint + " req = " + req);
|
||||||
service.handle(req, resp);
|
service.handle(req, resp);
|
||||||
})
|
})
|
||||||
.build();
|
.build();
|
||||||
Domain domain = Domain.builder(httpAddress)
|
Domain domain = Domain.builder(httpAddress)
|
||||||
.addEndpointResolver(endpointResolver)
|
.addEndpointResolver(httpEndpointResolver)
|
||||||
.build();
|
.build();
|
||||||
Server server = Server.builder(domain)
|
Server server = Server.builder(domain)
|
||||||
.build();
|
.build();
|
||||||
|
@ -185,18 +185,18 @@ class EndpointTest {
|
||||||
Path vartmp = Paths.get("/var/tmp/");
|
Path vartmp = Paths.get("/var/tmp/");
|
||||||
Service service = new FileService(vartmp);
|
Service service = new FileService(vartmp);
|
||||||
HttpAddress httpAddress = HttpAddress.http1("localhost", 8008);
|
HttpAddress httpAddress = HttpAddress.http1("localhost", 8008);
|
||||||
EndpointResolver endpointResolver = EndpointResolver.builder()
|
HttpEndpointResolver httpEndpointResolver = HttpEndpointResolver.builder()
|
||||||
.addEndpoint(HttpEndpoint.builder().setPrefix("/static").setPath("/**").build())
|
.addEndpoint(HttpEndpoint.builder().setPrefix("/static").setPath("/**").build())
|
||||||
.addEndpoint(HttpEndpoint.builder().setPrefix("/static1").setPath("/**").build())
|
.addEndpoint(HttpEndpoint.builder().setPrefix("/static1").setPath("/**").build())
|
||||||
.addEndpoint(HttpEndpoint.builder().setPrefix("/static2").setPath("/**").build())
|
.addEndpoint(HttpEndpoint.builder().setPrefix("/static2").setPath("/**").build())
|
||||||
.setDispatcher((endpoint, req, resp) -> {
|
.setDispatcher((endpoint, req, resp) -> {
|
||||||
logger.log(Level.FINE, "dispatching endpoint=" + endpoint + " req=" + req +
|
logger.log(Level.FINE, "dispatching endpoint = " + endpoint + " req = " + req +
|
||||||
" fragment=" + req.getURL().getFragment());
|
" fragment=" + req.getURL().getFragment());
|
||||||
service.handle(req, resp);
|
service.handle(req, resp);
|
||||||
})
|
})
|
||||||
.build();
|
.build();
|
||||||
Domain domain = Domain.builder(httpAddress)
|
Domain domain = Domain.builder(httpAddress)
|
||||||
.addEndpointResolver(endpointResolver)
|
.addEndpointResolver(httpEndpointResolver)
|
||||||
.build();
|
.build();
|
||||||
Server server = Server.builder(domain)
|
Server server = Server.builder(domain)
|
||||||
.build();
|
.build();
|
||||||
|
@ -264,9 +264,9 @@ class EndpointTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testMassiveEndpoints() throws IOException {
|
void testMassiveEndpoints() throws IOException {
|
||||||
int max = 2; // more than 1024
|
int max = 1024;
|
||||||
HttpAddress httpAddress = HttpAddress.http1("localhost", 8008);
|
HttpAddress httpAddress = HttpAddress.http1("localhost", 8008);
|
||||||
EndpointResolver.Builder endpointResolverBuilder = EndpointResolver.builder()
|
HttpEndpointResolver.Builder endpointResolverBuilder = HttpEndpointResolver.builder()
|
||||||
.setPrefix("/static");
|
.setPrefix("/static");
|
||||||
for (int i = 0; i < max; i++) {
|
for (int i = 0; i < max; i++) {
|
||||||
endpointResolverBuilder.addEndpoint(HttpEndpoint.builder()
|
endpointResolverBuilder.addEndpoint(HttpEndpoint.builder()
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
package org.xbib.netty.http.server.test;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBufInputStream;
|
||||||
|
import io.netty.buffer.ByteBufOutputStream;
|
||||||
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
|
import io.netty.handler.codec.http.HttpVersion;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.xbib.netty.http.client.Client;
|
||||||
|
import org.xbib.netty.http.client.Request;
|
||||||
|
import org.xbib.netty.http.common.HttpAddress;
|
||||||
|
import org.xbib.netty.http.server.Domain;
|
||||||
|
import org.xbib.netty.http.server.Server;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
@ExtendWith(NettyHttpExtension.class)
|
||||||
|
class StreamTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testServerStreams() throws Exception {
|
||||||
|
HttpAddress httpAddress = HttpAddress.http1("localhost", 8008);
|
||||||
|
Domain domain = Domain.builder(httpAddress)
|
||||||
|
.singleEndpoint("/", (request, response) -> {
|
||||||
|
ByteBufInputStream inputStream = request.getInputStream();
|
||||||
|
String content = inputStream.readLine();
|
||||||
|
assertEquals("my body parameter", content);
|
||||||
|
ByteBufOutputStream outputStream = response.getOutputStream();
|
||||||
|
outputStream.writeBytes("Hello World");
|
||||||
|
response.withStatus(HttpResponseStatus.OK)
|
||||||
|
.withContentType("text/plain")
|
||||||
|
.write(outputStream);
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
Server server = Server.builder(domain)
|
||||||
|
.build();
|
||||||
|
Client client = Client.builder()
|
||||||
|
.build();
|
||||||
|
int max = 1;
|
||||||
|
final AtomicInteger count = new AtomicInteger(0);
|
||||||
|
try {
|
||||||
|
server.accept();
|
||||||
|
Request request = Request.get().setVersion(HttpVersion.HTTP_1_1)
|
||||||
|
.url(server.getServerConfig().getAddress().base().resolve("/"))
|
||||||
|
.content("my body parameter", "text/plain")
|
||||||
|
.build()
|
||||||
|
.setResponseListener(response -> {
|
||||||
|
if (response.status().equals(HttpResponseStatus.OK)) {
|
||||||
|
assertEquals("Hello World", response.content().toString(StandardCharsets.UTF_8));
|
||||||
|
count.incrementAndGet();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for (int i = 0; i < max; i++) {
|
||||||
|
client.execute(request).get();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
server.shutdownGracefully();
|
||||||
|
client.shutdownGracefully();
|
||||||
|
}
|
||||||
|
assertEquals(max, count.get());
|
||||||
|
}
|
||||||
|
}
|
7
netty-http-xmlrpc-client/build.gradle
Normal file
7
netty-http-xmlrpc-client/build.gradle
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
dependencies {
|
||||||
|
compile project(":netty-http-xmlrpc-common")
|
||||||
|
compile "commons-httpclient:commons-httpclient:${project.property(('commons-httpclient.version'))}"
|
||||||
|
testCompile project(":netty-http-xmlrpc-servlet")
|
||||||
|
testCompileOnly group: 'javax.servlet', name: 'javax.servlet-api', version: '3.1.0'
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A callback interface for an asynchronous XML-RPC call.
|
||||||
|
*/
|
||||||
|
public interface AsyncCallback {
|
||||||
|
/** Call went ok, handle result.
|
||||||
|
* @param pRequest The request being performed.
|
||||||
|
* @param pResult The result object, which was returned by the server.
|
||||||
|
*/
|
||||||
|
public void handleResult(XmlRpcRequest pRequest, Object pResult);
|
||||||
|
|
||||||
|
/** Something went wrong, handle error.
|
||||||
|
* @param pRequest The request being performed.
|
||||||
|
* @param pError The error being thrown.
|
||||||
|
*/
|
||||||
|
public void handleError(XmlRpcRequest pRequest, Throwable pError);
|
||||||
|
}
|
|
@ -0,0 +1,139 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.TypeConverter;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.TypeConverterFactory;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.TypeConverterFactoryImpl;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcInvocationException;
|
||||||
|
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
import java.lang.reflect.UndeclaredThrowableException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>The {@link ClientFactory} is a useful tool for simplifying the
|
||||||
|
* use of Apache XML-RPC. The rough idea is as follows: All XML-RPC
|
||||||
|
* handlers are implemented as interfaces. The server uses the actual
|
||||||
|
* implementation. The client uses the {@link ClientFactory} to
|
||||||
|
* obtain an implementation, which is based on running XML-RPC calls.</p>
|
||||||
|
*/
|
||||||
|
public class ClientFactory {
|
||||||
|
|
||||||
|
private final XmlRpcClient client;
|
||||||
|
|
||||||
|
private final TypeConverterFactory typeConverterFactory;
|
||||||
|
|
||||||
|
private boolean objectMethodLocal;
|
||||||
|
|
||||||
|
/** Creates a new instance.
|
||||||
|
* @param pClient A fully configured XML-RPC client, which is
|
||||||
|
* used internally to perform XML-RPC calls.
|
||||||
|
* @param pTypeConverterFactory Creates instances of {@link TypeConverterFactory},
|
||||||
|
* which are used to transform the result object in its target representation.
|
||||||
|
*/
|
||||||
|
public ClientFactory(XmlRpcClient pClient, TypeConverterFactory pTypeConverterFactory) {
|
||||||
|
typeConverterFactory = pTypeConverterFactory;
|
||||||
|
client = pClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a new instance. Shortcut for
|
||||||
|
* <pre>
|
||||||
|
* new ClientFactory(pClient, new TypeConverterFactoryImpl());
|
||||||
|
* </pre>
|
||||||
|
* @param pClient A fully configured XML-RPC client, which is
|
||||||
|
* used internally to perform XML-RPC calls.
|
||||||
|
* @see TypeConverterFactoryImpl
|
||||||
|
*/
|
||||||
|
public ClientFactory(XmlRpcClient pClient) {
|
||||||
|
this(pClient, new TypeConverterFactoryImpl());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the factories client.
|
||||||
|
*/
|
||||||
|
public XmlRpcClient getClient() {
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns, whether a method declared by the {@link Object
|
||||||
|
* Object class} is performed by the local object, rather than
|
||||||
|
* by the server. Defaults to true.
|
||||||
|
*/
|
||||||
|
public boolean isObjectMethodLocal() {
|
||||||
|
return objectMethodLocal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sets, whether a method declared by the {@link Object
|
||||||
|
* Object class} is performed by the local object, rather than
|
||||||
|
* by the server. Defaults to true.
|
||||||
|
*/
|
||||||
|
public void setObjectMethodLocal(boolean pObjectMethodLocal) {
|
||||||
|
objectMethodLocal = pObjectMethodLocal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an object, which is implementing the given interface.
|
||||||
|
* The objects methods are internally calling an XML-RPC server
|
||||||
|
* by using the factories client; shortcut for
|
||||||
|
* <pre>
|
||||||
|
* newInstance(Thread.currentThread().getContextClassLoader(),
|
||||||
|
* pClass)
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
public Object newInstance(Class<?> pClass) {
|
||||||
|
return newInstance(Thread.currentThread().getContextClassLoader(), pClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates an object, which is implementing the given interface.
|
||||||
|
* The objects methods are internally calling an XML-RPC server
|
||||||
|
* by using the factories client; shortcut for
|
||||||
|
* <pre>
|
||||||
|
* newInstance(pClassLoader, pClass, pClass.getName())
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
public Object newInstance(ClassLoader pClassLoader, Class<?> pClass) {
|
||||||
|
return newInstance(pClassLoader, pClass, pClass.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates an object, which is implementing the given interface.
|
||||||
|
* The objects methods are internally calling an XML-RPC server
|
||||||
|
* by using the factories client.
|
||||||
|
* @param pClassLoader The class loader, which is being used for
|
||||||
|
* loading classes, if required.
|
||||||
|
* @param pClass Interface, which is being implemented.
|
||||||
|
* @param pRemoteName Handler name, which is being used when
|
||||||
|
* calling the server. This is used for composing the
|
||||||
|
* method name. For example, if <code>pRemoteName</code>
|
||||||
|
* is "Foo" and you want to invoke the method "bar" in
|
||||||
|
* the handler, then the full method name would be "Foo.bar".
|
||||||
|
*/
|
||||||
|
public Object newInstance(ClassLoader pClassLoader, final Class<?> pClass, final String pRemoteName) {
|
||||||
|
|
||||||
|
return Proxy.newProxyInstance(pClassLoader, new Class<?>[] { pClass }, (pProxy, pMethod, pArgs) -> {
|
||||||
|
if (isObjectMethodLocal() && pMethod.getDeclaringClass().equals(Object.class)) {
|
||||||
|
return pMethod.invoke(pProxy, pArgs);
|
||||||
|
}
|
||||||
|
final String methodName;
|
||||||
|
if (pRemoteName == null || pRemoteName.length() == 0) {
|
||||||
|
methodName = pMethod.getName();
|
||||||
|
} else {
|
||||||
|
methodName = pRemoteName + "." + pMethod.getName();
|
||||||
|
}
|
||||||
|
Object result;
|
||||||
|
try {
|
||||||
|
result = client.execute(methodName, pArgs);
|
||||||
|
} catch (XmlRpcInvocationException e) {
|
||||||
|
Throwable t = e.linkedException;
|
||||||
|
if (t instanceof RuntimeException) {
|
||||||
|
throw t;
|
||||||
|
}
|
||||||
|
Class<?>[] exceptionTypes = pMethod.getExceptionTypes();
|
||||||
|
for (Class<?> c : exceptionTypes) {
|
||||||
|
if (c.isAssignableFrom(t.getClass())) {
|
||||||
|
throw t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new UndeclaredThrowableException(t);
|
||||||
|
}
|
||||||
|
TypeConverter typeConverter = typeConverterFactory.getTypeConverter(pMethod.getReturnType());
|
||||||
|
return typeConverter.convert(result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcException;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>A callback object that can wait up to a specified amount
|
||||||
|
* of time for the XML-RPC response. Suggested use is as follows:
|
||||||
|
* </p>
|
||||||
|
* <pre>
|
||||||
|
* // Wait for 10 seconds.
|
||||||
|
* TimingOutCallback callback = new TimingOutCallback(10 * 1000);
|
||||||
|
* XmlRpcClient client = new XmlRpcClient(url);
|
||||||
|
* client.executeAsync(methodName, aVector, callback);
|
||||||
|
* try {
|
||||||
|
* return callback.waitForResponse();
|
||||||
|
* } catch (TimeoutException e) {
|
||||||
|
* System.out.println("No response from server.");
|
||||||
|
* } catch (Exception e) {
|
||||||
|
* System.out.println("Server returned an error message.");
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
public class TimingOutCallback implements AsyncCallback {
|
||||||
|
/** This exception is thrown, if the request times out.
|
||||||
|
*/
|
||||||
|
public static class TimeoutException extends XmlRpcException {
|
||||||
|
private static final long serialVersionUID = 4875266372372105081L;
|
||||||
|
|
||||||
|
/** Creates a new instance with the given error code and
|
||||||
|
* error message.
|
||||||
|
*/
|
||||||
|
public TimeoutException(int pCode, String message) {
|
||||||
|
super(pCode, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final long timeout;
|
||||||
|
private Object result;
|
||||||
|
private Throwable error;
|
||||||
|
private boolean responseSeen;
|
||||||
|
|
||||||
|
/** Waits the specified number of milliseconds for a response.
|
||||||
|
*/
|
||||||
|
public TimingOutCallback(long pTimeout) {
|
||||||
|
timeout = pTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Called to wait for the response.
|
||||||
|
* @throws InterruptedException The thread was interrupted.
|
||||||
|
* @throws TimeoutException No response was received after waiting the specified time.
|
||||||
|
* @throws Throwable An error was returned by the server.
|
||||||
|
*/
|
||||||
|
public synchronized Object waitForResponse() throws Throwable {
|
||||||
|
if (!responseSeen) {
|
||||||
|
wait(timeout);
|
||||||
|
if (!responseSeen) {
|
||||||
|
throw new TimeoutException(0, "No response after waiting for " + timeout + " milliseconds.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (error != null) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void handleError(XmlRpcRequest pRequest, Throwable pError) {
|
||||||
|
responseSeen = true;
|
||||||
|
error = pError;
|
||||||
|
notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void handleResult(XmlRpcRequest pRequest, Object pResult) {
|
||||||
|
responseSeen = true;
|
||||||
|
result = pResult;
|
||||||
|
notify();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,246 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcConfig;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcController;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcException;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcRequest;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcWorkerFactory;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.serializer.XmlWriterFactory;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>The main access point of an XML-RPC client. This object serves mainly
|
||||||
|
* as an object factory. It is designed with singletons in mind: Basically,
|
||||||
|
* an application should be able to hold a single instance of
|
||||||
|
* <code>XmlRpcClient</code> in a static variable, unless you would be
|
||||||
|
* working with different factories.</p>
|
||||||
|
* <p>Until Apache XML-RPC 2.0, this object was used both as an object
|
||||||
|
* factory and as a place, where configuration details (server URL,
|
||||||
|
* suggested encoding, user credentials and the like) have been stored.
|
||||||
|
* In Apache XML-RPC 3.0, the configuration details has been moved to
|
||||||
|
* the {@link XmlRpcClientConfig} object.
|
||||||
|
* The configuration object is designed for being passed through the
|
||||||
|
* actual worker methods.</p>
|
||||||
|
* <p>A configured XmlRpcClient object is thread safe: In other words,
|
||||||
|
* the suggested use is, that you configure the client using
|
||||||
|
* {@link #setTransportFactory(XmlRpcTransportFactory)} and similar
|
||||||
|
* methods, store it in a field and never modify it again. Without
|
||||||
|
* modifications, the client may be used for an arbitrary number
|
||||||
|
* of concurrent requests.</p>
|
||||||
|
*/
|
||||||
|
public class XmlRpcClient extends XmlRpcController {
|
||||||
|
|
||||||
|
private XmlRpcTransportFactory transportFactory = XmlRpcClientDefaults.newTransportFactory(this);
|
||||||
|
|
||||||
|
private XmlRpcClientConfig config = XmlRpcClientDefaults.newXmlRpcClientConfig();
|
||||||
|
|
||||||
|
private XmlWriterFactory xmlWriterFactory = XmlRpcClientDefaults.newXmlWriterFactory();
|
||||||
|
|
||||||
|
protected XmlRpcWorkerFactory getDefaultXmlRpcWorkerFactory() {
|
||||||
|
return new XmlRpcClientWorkerFactory(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the clients default configuration. This configuration
|
||||||
|
* is used by the methods
|
||||||
|
* {@link #execute(String, List)},
|
||||||
|
* {@link #execute(String, Object[])}, and
|
||||||
|
* {@link #execute(XmlRpcRequest)}.
|
||||||
|
* You may overwrite this per request by using
|
||||||
|
* {@link #execute(XmlRpcClientConfig, String, List)},
|
||||||
|
* or {@link #execute(XmlRpcClientConfig, String, Object[])}.
|
||||||
|
* @param pConfig The default request configuration.
|
||||||
|
*/
|
||||||
|
public void setConfig(XmlRpcClientConfig pConfig) {
|
||||||
|
config = pConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the clients default configuration. This configuration
|
||||||
|
* is used by the methods
|
||||||
|
* {@link #execute(String, List)},
|
||||||
|
* {@link #execute(String, Object[])}.
|
||||||
|
* You may overwrite this per request by using
|
||||||
|
* {@link #execute(XmlRpcClientConfig, String, List)},
|
||||||
|
* or {@link #execute(XmlRpcClientConfig, String, Object[])}.
|
||||||
|
* @return The default request configuration.
|
||||||
|
*/
|
||||||
|
public XmlRpcConfig getConfig() {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the clients default configuration. Shortcut for
|
||||||
|
* <code>(XmlRpcClientConfig) getConfig()</code>.
|
||||||
|
* This configuration is used by the methods
|
||||||
|
* {@link #execute(String, List)},
|
||||||
|
* {@link #execute(String, Object[])}.
|
||||||
|
* You may overwrite this per request by using
|
||||||
|
* {@link #execute(XmlRpcClientConfig, String, List)}, or
|
||||||
|
* {@link #execute(XmlRpcClientConfig, String, Object[])}
|
||||||
|
* @return The default request configuration.
|
||||||
|
*/
|
||||||
|
public XmlRpcClientConfig getClientConfig() {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the clients transport factory. The client will invoke the
|
||||||
|
* factory method {@link XmlRpcTransportFactory#getTransport()}
|
||||||
|
* for any request.
|
||||||
|
* @param pFactory The clients transport factory.
|
||||||
|
*/
|
||||||
|
public void setTransportFactory(XmlRpcTransportFactory pFactory) {
|
||||||
|
transportFactory = pFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the clients transport factory. The client will use this factory
|
||||||
|
* for invocation of {@link XmlRpcTransportFactory#getTransport()}
|
||||||
|
* for any request.
|
||||||
|
* @return The clients transport factory.
|
||||||
|
*/
|
||||||
|
public XmlRpcTransportFactory getTransportFactory() {
|
||||||
|
return transportFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a request with the clients default configuration.
|
||||||
|
* @param pMethodName The method being performed.
|
||||||
|
* @param pParams The parameters.
|
||||||
|
* @return The result object.
|
||||||
|
* @throws XmlRpcException Performing the request failed.
|
||||||
|
*/
|
||||||
|
public Object execute(String pMethodName, Object[] pParams) throws XmlRpcException {
|
||||||
|
return execute(getClientConfig(), pMethodName, pParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a request with the given configuration.
|
||||||
|
* @param pConfig The request configuration.
|
||||||
|
* @param pMethodName The method being performed.
|
||||||
|
* @param pParams The parameters.
|
||||||
|
* @return The result object.
|
||||||
|
* @throws XmlRpcException Performing the request failed.
|
||||||
|
*/
|
||||||
|
public Object execute(XmlRpcClientConfig pConfig, String pMethodName, Object[] pParams) throws XmlRpcException {
|
||||||
|
return execute(new XmlRpcClientRequestImpl(pConfig, pMethodName, pParams));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a request with the clients default configuration.
|
||||||
|
* @param pMethodName The method being performed.
|
||||||
|
* @param pParams The parameters.
|
||||||
|
* @return The result object.
|
||||||
|
* @throws XmlRpcException Performing the request failed.
|
||||||
|
*/
|
||||||
|
public Object execute(String pMethodName, List<Object> pParams) throws XmlRpcException {
|
||||||
|
return execute(getClientConfig(), pMethodName, pParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a request with the given configuration.
|
||||||
|
* @param pConfig The request configuration.
|
||||||
|
* @param pMethodName The method being performed.
|
||||||
|
* @param pParams The parameters.
|
||||||
|
* @return The result object.
|
||||||
|
* @throws XmlRpcException Performing the request failed.
|
||||||
|
*/
|
||||||
|
public Object execute(XmlRpcClientConfig pConfig, String pMethodName, List<Object> pParams) throws XmlRpcException {
|
||||||
|
return execute(new XmlRpcClientRequestImpl(pConfig, pMethodName, pParams));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a request with the clients default configuration.
|
||||||
|
* @param pRequest The request being performed.
|
||||||
|
* @return The result object.
|
||||||
|
* @throws XmlRpcException Performing the request failed.
|
||||||
|
*/
|
||||||
|
public Object execute(XmlRpcRequest pRequest) throws XmlRpcException {
|
||||||
|
return getWorkerFactory().getWorker().execute(pRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs an asynchronous request with the clients default configuration.
|
||||||
|
* @param pMethodName The method being performed.
|
||||||
|
* @param pParams The parameters.
|
||||||
|
* @param pCallback The callback being notified when the request is finished.
|
||||||
|
* @throws XmlRpcException Performing the request failed.
|
||||||
|
*/
|
||||||
|
public void executeAsync(String pMethodName, Object[] pParams,
|
||||||
|
AsyncCallback pCallback) throws XmlRpcException {
|
||||||
|
executeAsync(getClientConfig(), pMethodName, pParams, pCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs an asynchronous request with the given configuration.
|
||||||
|
* @param pConfig The request configuration.
|
||||||
|
* @param pMethodName The method being performed.
|
||||||
|
* @param pParams The parameters.
|
||||||
|
* @param pCallback The callback being notified when the request is finished.
|
||||||
|
* @throws XmlRpcException Performing the request failed.
|
||||||
|
*/
|
||||||
|
public void executeAsync(XmlRpcClientConfig pConfig,
|
||||||
|
String pMethodName, Object[] pParams,
|
||||||
|
AsyncCallback pCallback) throws XmlRpcException {
|
||||||
|
executeAsync(new XmlRpcClientRequestImpl(pConfig, pMethodName, pParams),
|
||||||
|
pCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs an asynchronous request with the clients default configuration.
|
||||||
|
* @param pMethodName The method being performed.
|
||||||
|
* @param pParams The parameters.
|
||||||
|
* @param pCallback The callback being notified when the request is finished.
|
||||||
|
* @throws XmlRpcException Performing the request failed.
|
||||||
|
*/
|
||||||
|
public void executeAsync(String pMethodName, List<Object> pParams,
|
||||||
|
AsyncCallback pCallback) throws XmlRpcException {
|
||||||
|
executeAsync(getClientConfig(), pMethodName, pParams, pCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs an asynchronous request with the given configuration.
|
||||||
|
* @param pConfig The request configuration.
|
||||||
|
* @param pMethodName The method being performed.
|
||||||
|
* @param pParams The parameters.
|
||||||
|
* @param pCallback The callback being notified when the request is finished.
|
||||||
|
* @throws XmlRpcException Performing the request failed.
|
||||||
|
*/
|
||||||
|
public void executeAsync(XmlRpcClientConfig pConfig,
|
||||||
|
String pMethodName, List<Object> pParams,
|
||||||
|
AsyncCallback pCallback) throws XmlRpcException {
|
||||||
|
executeAsync(new XmlRpcClientRequestImpl(pConfig, pMethodName, pParams), pCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a request with the clients default configuration.
|
||||||
|
* @param pRequest The request being performed.
|
||||||
|
* @param pCallback The callback being notified when the request is finished.
|
||||||
|
* @throws XmlRpcException Performing the request failed.
|
||||||
|
*/
|
||||||
|
public void executeAsync(XmlRpcRequest pRequest,
|
||||||
|
AsyncCallback pCallback) throws XmlRpcException {
|
||||||
|
XmlRpcClientWorker w = (XmlRpcClientWorker) getWorkerFactory().getWorker();
|
||||||
|
w.execute(pRequest, pCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the clients instance of
|
||||||
|
* {@link XmlWriterFactory}.
|
||||||
|
* @return A factory for creating instances.
|
||||||
|
*/
|
||||||
|
public XmlWriterFactory getXmlWriterFactory() {
|
||||||
|
return xmlWriterFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the clients instance of
|
||||||
|
* {@link XmlWriterFactory}.
|
||||||
|
* @param pFactory A factory for creating instances}.
|
||||||
|
*/
|
||||||
|
public void setXmlWriterFactory(XmlWriterFactory pFactory) {
|
||||||
|
xmlWriterFactory = pFactory;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcRequestConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface is being implemented by an Apache XML-RPC clients
|
||||||
|
* configuration object. Depending on the transport factory, a
|
||||||
|
* configuration object must implement additional methods. For
|
||||||
|
* example, an HTTP transport requires an instance of
|
||||||
|
* {@link XmlRpcHttpClientConfig}. A
|
||||||
|
* local transport requires an instance of
|
||||||
|
* {@link XmlRpcLocalClientConfig}.
|
||||||
|
*/
|
||||||
|
public interface XmlRpcClientConfig extends XmlRpcRequestConfig {
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcHttpRequestConfigImpl;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcRequestProcessor;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation of a clients request configuration.
|
||||||
|
*/
|
||||||
|
public class XmlRpcClientConfigImpl extends XmlRpcHttpRequestConfigImpl
|
||||||
|
implements XmlRpcHttpClientConfig, XmlRpcLocalClientConfig, Cloneable, Serializable {
|
||||||
|
private static final long serialVersionUID = 4121131450507800889L;
|
||||||
|
private URL serverURL;
|
||||||
|
private XmlRpcRequestProcessor xmlRpcServer;
|
||||||
|
private String userAgent;
|
||||||
|
|
||||||
|
/** Creates a new client configuration with default settings.
|
||||||
|
*/
|
||||||
|
public XmlRpcClientConfigImpl() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a clone of this client configuration.
|
||||||
|
* @return A clone of this configuration.
|
||||||
|
*/
|
||||||
|
public XmlRpcClientConfigImpl cloneMe() {
|
||||||
|
try {
|
||||||
|
return (XmlRpcClientConfigImpl) clone();
|
||||||
|
} catch (CloneNotSupportedException e) {
|
||||||
|
throw new IllegalStateException("Unable to create my clone");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sets the servers URL.
|
||||||
|
* @param pURL Servers URL
|
||||||
|
*/
|
||||||
|
public void setServerURL(URL pURL) {
|
||||||
|
serverURL = pURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public URL getServerURL() { return serverURL; }
|
||||||
|
/** Returns the {@link XmlRpcRequestProcessor} being invoked.
|
||||||
|
* @param pServer Server object being invoked. This will typically
|
||||||
|
* be a singleton instance, but could as well create a new
|
||||||
|
* instance with any call.
|
||||||
|
*/
|
||||||
|
public void setXmlRpcServer(XmlRpcRequestProcessor pServer) {
|
||||||
|
xmlRpcServer = pServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public XmlRpcRequestProcessor getXmlRpcServer() { return xmlRpcServer; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the user agent header to use
|
||||||
|
* @return the http user agent header to set when doing xmlrpc requests
|
||||||
|
*/
|
||||||
|
public String getUserAgent() {
|
||||||
|
return userAgent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param pUserAgent the http user agent header to set when doing xmlrpc requests
|
||||||
|
*/
|
||||||
|
public void setUserAgent(String pUserAgent) {
|
||||||
|
userAgent = pUserAgent;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.serializer.DefaultXMLWriterFactory;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.serializer.XmlWriterFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is responsible to provide default settings.
|
||||||
|
*/
|
||||||
|
public class XmlRpcClientDefaults {
|
||||||
|
|
||||||
|
private static final XmlWriterFactory xmlWriterFactory = new DefaultXMLWriterFactory();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new transport factory for the given client.
|
||||||
|
*/
|
||||||
|
public static XmlRpcTransportFactory newTransportFactory(XmlRpcClient pClient) {
|
||||||
|
try {
|
||||||
|
return new XmlRpcSun15HttpTransportFactory(pClient);
|
||||||
|
} catch (Throwable t1) {
|
||||||
|
try {
|
||||||
|
return new XmlRpcSun14HttpTransportFactory(pClient);
|
||||||
|
} catch (Throwable t2) {
|
||||||
|
return new XmlRpcSunHttpTransportFactory(pClient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of {@link XmlRpcClientConfig}.
|
||||||
|
*/
|
||||||
|
public static XmlRpcClientConfig newXmlRpcClientConfig() {
|
||||||
|
return new XmlRpcClientConfigImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link XmlWriterFactory}.
|
||||||
|
*/
|
||||||
|
public static XmlWriterFactory newXmlWriterFactory() {
|
||||||
|
return xmlWriterFactory;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcException;
|
||||||
|
|
||||||
|
/** <p>This is thrown by many of the client classes if an error occured processing
|
||||||
|
* and XML-RPC request or response due to client side processing..</p>
|
||||||
|
*/
|
||||||
|
public class XmlRpcClientException extends XmlRpcException {
|
||||||
|
private static final long serialVersionUID = 3545798797134608691L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an XmlRpcClientException with the given message and
|
||||||
|
* underlying cause exception.
|
||||||
|
*
|
||||||
|
* @param pMessage the message for this exception.
|
||||||
|
* @param pCause the cause of the exception.
|
||||||
|
*/
|
||||||
|
public XmlRpcClientException(String pMessage, Throwable pCause) {
|
||||||
|
super(0, pMessage, pCause);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcRequest;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcRequestConfig;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation of
|
||||||
|
* {@link XmlRpcRequest}.
|
||||||
|
*/
|
||||||
|
public class XmlRpcClientRequestImpl implements XmlRpcRequest {
|
||||||
|
|
||||||
|
private static final Object[] ZERO_PARAMS = new Object[0];
|
||||||
|
|
||||||
|
private final XmlRpcRequestConfig config;
|
||||||
|
|
||||||
|
private final String methodName;
|
||||||
|
|
||||||
|
private final Object[] params;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance.
|
||||||
|
* @param pConfig The request configuration.
|
||||||
|
* @param pMethodName The method name being performed.
|
||||||
|
* @param pParams The parameters.
|
||||||
|
* @throws NullPointerException One of the parameters is null.
|
||||||
|
*/
|
||||||
|
public XmlRpcClientRequestImpl(XmlRpcRequestConfig pConfig,
|
||||||
|
String pMethodName, Object[] pParams) {
|
||||||
|
config = pConfig;
|
||||||
|
if (config == null) {
|
||||||
|
throw new NullPointerException("The request configuration must not be null.");
|
||||||
|
}
|
||||||
|
methodName = pMethodName;
|
||||||
|
if (methodName == null) {
|
||||||
|
throw new NullPointerException("The method name must not be null.");
|
||||||
|
}
|
||||||
|
params = pParams == null ? ZERO_PARAMS : pParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance.
|
||||||
|
* @param pConfig The request configuration.
|
||||||
|
* @param pMethodName The method name being performed.
|
||||||
|
* @param pParams The parameters.
|
||||||
|
* @throws NullPointerException The method name or the parameters are null.
|
||||||
|
*/
|
||||||
|
public XmlRpcClientRequestImpl(XmlRpcRequestConfig pConfig,
|
||||||
|
String pMethodName, List<Object> pParams) {
|
||||||
|
this(pConfig, pMethodName, pParams == null ? null : pParams.toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMethodName() { return methodName; }
|
||||||
|
|
||||||
|
public int getParameterCount() { return params.length; }
|
||||||
|
|
||||||
|
public Object getParameter(int pIndex) { return params[pIndex]; }
|
||||||
|
|
||||||
|
public XmlRpcRequestConfig getConfig() { return config; }
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcController;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcException;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcRequest;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcWorker;
|
||||||
|
|
||||||
|
/** Object, which performs a request on the clients behalf.
|
||||||
|
* The client maintains a pool of workers. The main purpose of the
|
||||||
|
* pool is limitation of the maximum number of concurrent requests.
|
||||||
|
*/
|
||||||
|
public class XmlRpcClientWorker implements XmlRpcWorker {
|
||||||
|
private final XmlRpcClientWorkerFactory factory;
|
||||||
|
|
||||||
|
/** Creates a new instance.
|
||||||
|
* @param pFactory The factory, which is being notified, if
|
||||||
|
* the worker's ready.
|
||||||
|
*/
|
||||||
|
public XmlRpcClientWorker(XmlRpcClientWorkerFactory pFactory) {
|
||||||
|
factory = pFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public XmlRpcController getController() {
|
||||||
|
return factory.getController();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Performs a synchronous request.
|
||||||
|
* @param pRequest The request being performed.
|
||||||
|
* @return The requests result.
|
||||||
|
* @throws XmlRpcException Performing the request failed.
|
||||||
|
*/
|
||||||
|
public Object execute(XmlRpcRequest pRequest)
|
||||||
|
throws XmlRpcException {
|
||||||
|
try {
|
||||||
|
XmlRpcClient client = (XmlRpcClient) getController();
|
||||||
|
return client.getTransportFactory().getTransport().sendRequest(pRequest);
|
||||||
|
} finally {
|
||||||
|
factory.releaseWorker(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Thread newThread(Runnable pRunnable) {
|
||||||
|
Thread result = new Thread(pRunnable);
|
||||||
|
result.setDaemon(true);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Performs an synchronous request.
|
||||||
|
* @param pRequest The request being performed.
|
||||||
|
* @param pCallback The callback being invoked, when the request is finished.
|
||||||
|
*/
|
||||||
|
public void execute(final XmlRpcRequest pRequest,
|
||||||
|
final AsyncCallback pCallback) {
|
||||||
|
Runnable runnable = new Runnable(){
|
||||||
|
public void run(){
|
||||||
|
Object result = null;
|
||||||
|
Throwable th = null;
|
||||||
|
try {
|
||||||
|
XmlRpcClient client = (XmlRpcClient) getController();
|
||||||
|
result = client.getTransportFactory().getTransport().sendRequest(pRequest);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
th = t;
|
||||||
|
}
|
||||||
|
factory.releaseWorker(XmlRpcClientWorker.this);
|
||||||
|
if (th == null) {
|
||||||
|
pCallback.handleResult(pRequest, result);
|
||||||
|
} else {
|
||||||
|
pCallback.handleError(pRequest, th);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
newThread(runnable).start();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcWorker;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcWorkerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A worker factory for the client, creating instances of
|
||||||
|
* {@link XmlRpcClientWorker}.
|
||||||
|
*/
|
||||||
|
public class XmlRpcClientWorkerFactory extends XmlRpcWorkerFactory {
|
||||||
|
/** Creates a new instance.
|
||||||
|
* @param pClient The factory controller.
|
||||||
|
*/
|
||||||
|
public XmlRpcClientWorkerFactory(XmlRpcClient pClient) {
|
||||||
|
super(pClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a new worker instance.
|
||||||
|
* @return New instance of {@link XmlRpcClientWorker}.
|
||||||
|
*/
|
||||||
|
protected XmlRpcWorker newWorker() {
|
||||||
|
return new XmlRpcClientWorker(this);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,242 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.FilterOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import org.apache.commons.httpclient.Credentials;
|
||||||
|
import org.apache.commons.httpclient.Header;
|
||||||
|
import org.apache.commons.httpclient.HttpClient;
|
||||||
|
import org.apache.commons.httpclient.HttpException;
|
||||||
|
import org.apache.commons.httpclient.HttpMethod;
|
||||||
|
import org.apache.commons.httpclient.HttpStatus;
|
||||||
|
import org.apache.commons.httpclient.HttpVersion;
|
||||||
|
import org.apache.commons.httpclient.URI;
|
||||||
|
import org.apache.commons.httpclient.URIException;
|
||||||
|
import org.apache.commons.httpclient.UsernamePasswordCredentials;
|
||||||
|
import org.apache.commons.httpclient.auth.AuthScope;
|
||||||
|
import org.apache.commons.httpclient.methods.PostMethod;
|
||||||
|
import org.apache.commons.httpclient.methods.RequestEntity;
|
||||||
|
import org.apache.commons.httpclient.params.HttpMethodParams;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcException;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcRequest;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcStreamConfig;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcStreamRequestConfig;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.util.HttpUtil;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An HTTP transport factory, which is based on the Jakarta Commons HTTP Client.
|
||||||
|
*/
|
||||||
|
public class XmlRpcCommonsTransport extends XmlRpcHttpTransport {
|
||||||
|
/**
|
||||||
|
* Maximum number of allowed redirects.
|
||||||
|
*/
|
||||||
|
private static final int MAX_REDIRECT_ATTEMPTS = 100;
|
||||||
|
|
||||||
|
protected final HttpClient client;
|
||||||
|
private static final String userAgent = USER_AGENT + " (Jakarta Commons httpclient Transport)";
|
||||||
|
protected PostMethod method;
|
||||||
|
private int contentLength = -1;
|
||||||
|
private XmlRpcHttpClientConfig config;
|
||||||
|
|
||||||
|
/** Creates a new instance.
|
||||||
|
* @param pFactory The factory, which created this transport.
|
||||||
|
*/
|
||||||
|
public XmlRpcCommonsTransport(XmlRpcCommonsTransportFactory pFactory) {
|
||||||
|
super(pFactory.getClient(), userAgent);
|
||||||
|
HttpClient httpClient = pFactory.getHttpClient();
|
||||||
|
if (httpClient == null) {
|
||||||
|
httpClient = newHttpClient();
|
||||||
|
}
|
||||||
|
client = httpClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setContentLength(int pLength) {
|
||||||
|
contentLength = pLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected HttpClient newHttpClient() {
|
||||||
|
return new HttpClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void initHttpHeaders(XmlRpcRequest pRequest) throws XmlRpcClientException {
|
||||||
|
config = (XmlRpcHttpClientConfig) pRequest.getConfig();
|
||||||
|
method = newPostMethod(config);
|
||||||
|
super.initHttpHeaders(pRequest);
|
||||||
|
|
||||||
|
if (config.getConnectionTimeout() != 0)
|
||||||
|
client.getHttpConnectionManager().getParams().setConnectionTimeout(config.getConnectionTimeout());
|
||||||
|
|
||||||
|
if (config.getReplyTimeout() != 0)
|
||||||
|
client.getHttpConnectionManager().getParams().setSoTimeout(config.getReplyTimeout());
|
||||||
|
|
||||||
|
method.getParams().setVersion(HttpVersion.HTTP_1_1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected PostMethod newPostMethod(XmlRpcHttpClientConfig pConfig) {
|
||||||
|
return new PostMethod(pConfig.getServerURL().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setRequestHeader(String pHeader, String pValue) {
|
||||||
|
method.setRequestHeader(new Header(pHeader, pValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isResponseGzipCompressed() {
|
||||||
|
Header h = method.getResponseHeader( "Content-Encoding" );
|
||||||
|
if (h == null) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return HttpUtil.isUsingGzipEncoding(h.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected InputStream getInputStream() throws XmlRpcException {
|
||||||
|
try {
|
||||||
|
checkStatus(method);
|
||||||
|
return method.getResponseBodyAsStream();
|
||||||
|
} catch (HttpException e) {
|
||||||
|
throw new XmlRpcClientException("Error in HTTP transport: " + e.getMessage(), e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new XmlRpcClientException("I/O error in server communication: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setCredentials(XmlRpcHttpClientConfig pConfig) throws XmlRpcClientException {
|
||||||
|
String userName = pConfig.getBasicUserName();
|
||||||
|
if (userName != null) {
|
||||||
|
String enc = pConfig.getBasicEncoding();
|
||||||
|
if (enc == null) {
|
||||||
|
enc = XmlRpcStreamConfig.UTF8_ENCODING;
|
||||||
|
}
|
||||||
|
client.getParams().setParameter(HttpMethodParams.CREDENTIAL_CHARSET, enc);
|
||||||
|
Credentials creds = new UsernamePasswordCredentials(userName, pConfig.getBasicPassword());
|
||||||
|
AuthScope scope = new AuthScope(null, AuthScope.ANY_PORT, null, AuthScope.ANY_SCHEME);
|
||||||
|
client.getState().setCredentials(scope, creds);
|
||||||
|
client.getParams().setAuthenticationPreemptive(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void close() throws XmlRpcClientException {
|
||||||
|
method.releaseConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isResponseGzipCompressed(XmlRpcStreamRequestConfig pConfig) {
|
||||||
|
Header h = method.getResponseHeader( "Content-Encoding" );
|
||||||
|
if (h == null) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return HttpUtil.isUsingGzipEncoding(h.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isRedirectRequired() {
|
||||||
|
switch (method.getStatusCode()) {
|
||||||
|
case HttpStatus.SC_MOVED_TEMPORARILY:
|
||||||
|
case HttpStatus.SC_MOVED_PERMANENTLY:
|
||||||
|
case HttpStatus.SC_SEE_OTHER:
|
||||||
|
case HttpStatus.SC_TEMPORARY_REDIRECT:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void resetClientForRedirect()
|
||||||
|
throws XmlRpcException {
|
||||||
|
//get the location header to find out where to redirect to
|
||||||
|
Header locationHeader = method.getResponseHeader("location");
|
||||||
|
if (locationHeader == null) {
|
||||||
|
throw new XmlRpcException("Invalid redirect: Missing location header");
|
||||||
|
}
|
||||||
|
String location = locationHeader.getValue();
|
||||||
|
|
||||||
|
URI redirectUri = null;
|
||||||
|
URI currentUri = null;
|
||||||
|
try {
|
||||||
|
currentUri = method.getURI();
|
||||||
|
String charset = currentUri.getProtocolCharset();
|
||||||
|
redirectUri = new URI(location, true, charset);
|
||||||
|
method.setURI(redirectUri);
|
||||||
|
} catch (URIException ex) {
|
||||||
|
throw new XmlRpcException(ex.getMessage(), ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
//And finally invalidate the actual authentication scheme
|
||||||
|
method.getHostAuthState().invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void writeRequest(final ReqWriter pWriter) throws XmlRpcException {
|
||||||
|
method.setRequestEntity(new RequestEntity(){
|
||||||
|
public boolean isRepeatable() { return true; }
|
||||||
|
public void writeRequest(OutputStream pOut) throws IOException {
|
||||||
|
try {
|
||||||
|
/* Make sure, that the socket is not closed by replacing it with our
|
||||||
|
* own BufferedOutputStream.
|
||||||
|
*/
|
||||||
|
OutputStream ostream;
|
||||||
|
if (isUsingByteArrayOutput(config)) {
|
||||||
|
// No need to buffer the output.
|
||||||
|
ostream = new FilterOutputStream(pOut){
|
||||||
|
public void close() throws IOException {
|
||||||
|
flush();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
ostream = new BufferedOutputStream(pOut){
|
||||||
|
public void close() throws IOException {
|
||||||
|
flush();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
pWriter.write(ostream);
|
||||||
|
} catch (XmlRpcException e) {
|
||||||
|
throw new XmlRpcIOException(e);
|
||||||
|
} catch (SAXException e) {
|
||||||
|
throw new XmlRpcIOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public long getContentLength() { return contentLength; }
|
||||||
|
public String getContentType() { return "text/xml"; }
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
int redirectAttempts = 0;
|
||||||
|
for (;;) {
|
||||||
|
client.executeMethod(method);
|
||||||
|
if (!isRedirectRequired()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (redirectAttempts++ > MAX_REDIRECT_ATTEMPTS) {
|
||||||
|
throw new XmlRpcException("Too many redirects.");
|
||||||
|
}
|
||||||
|
resetClientForRedirect();
|
||||||
|
}
|
||||||
|
} catch (XmlRpcIOException e) {
|
||||||
|
Throwable t = e.getLinkedException();
|
||||||
|
if (t instanceof XmlRpcException) {
|
||||||
|
throw (XmlRpcException) t;
|
||||||
|
} else {
|
||||||
|
throw new XmlRpcException("Unexpected exception: " + t.getMessage(), t);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new XmlRpcException("I/O error while communicating with HTTP server: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the status of the HTTP request and throw an XmlRpcHttpTransportException if it
|
||||||
|
* indicates that there is an error.
|
||||||
|
* @param pMethod the method that has been executed
|
||||||
|
* @throws XmlRpcHttpTransportException if the status of the method indicates that there is an error.
|
||||||
|
*/
|
||||||
|
private void checkStatus(HttpMethod pMethod) throws XmlRpcHttpTransportException {
|
||||||
|
final int status = pMethod.getStatusCode();
|
||||||
|
|
||||||
|
// All status codes except SC_OK are handled as errors. Perhaps some should require special handling (e.g., SC_UNAUTHORIZED)
|
||||||
|
if (status < 200 || status > 299) {
|
||||||
|
throw new XmlRpcHttpTransportException(status, pMethod.getStatusText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
import org.apache.commons.httpclient.HttpClient;
|
||||||
|
|
||||||
|
|
||||||
|
/** An HTTP transport factory, which is based on the Jakarta Commons
|
||||||
|
* HTTP Client.
|
||||||
|
*/
|
||||||
|
public class XmlRpcCommonsTransportFactory extends XmlRpcTransportFactoryImpl {
|
||||||
|
private HttpClient httpClient;
|
||||||
|
|
||||||
|
/** Creates a new instance.
|
||||||
|
* @param pClient The client, which is controlling the factory.
|
||||||
|
*/
|
||||||
|
public XmlRpcCommonsTransportFactory(XmlRpcClient pClient) {
|
||||||
|
super(pClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
public XmlRpcTransport getTransport() {
|
||||||
|
return new XmlRpcCommonsTransport(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Sets the factories {@link HttpClient}. By default, a new instance
|
||||||
|
* of {@link HttpClient} is created for any request.</p>
|
||||||
|
* <p>Reusing the {@link HttpClient} is required, if you want to preserve
|
||||||
|
* some state between requests. This applies, in particular, if you want
|
||||||
|
* to use cookies: In that case, create an instance of {@link HttpClient},
|
||||||
|
* give it to the factory, and use {@link HttpClient#getState()} to
|
||||||
|
* read or set cookies.
|
||||||
|
*/
|
||||||
|
public void setHttpClient(HttpClient pHttpClient) {
|
||||||
|
httpClient = pHttpClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Returns the factories {@link HttpClient}. By default, a new instance
|
||||||
|
* of {@link HttpClient} is created for any request.</p>
|
||||||
|
* <p>Reusing the {@link HttpClient} is required, if you want to preserve
|
||||||
|
* some state between requests. This applies, in particular, if you want
|
||||||
|
* to use cookies: In that case, create an instance of {@link HttpClient},
|
||||||
|
* give it to the factory, and use {@link HttpClient#getState()} to
|
||||||
|
* read or set cookies.
|
||||||
|
*/
|
||||||
|
public HttpClient getHttpClient() {
|
||||||
|
return httpClient;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcHttpRequestConfig;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
/** Extension of {@link XmlRpcClientConfig}
|
||||||
|
* for HTTP based transport. Provides details like server URL,
|
||||||
|
* user credentials, and so on.
|
||||||
|
*/
|
||||||
|
public interface XmlRpcHttpClientConfig extends XmlRpcHttpRequestConfig {
|
||||||
|
/** Returns the HTTP servers URL.
|
||||||
|
* @return XML-RPC servers URL; for example, this may be the URL of a
|
||||||
|
* servlet
|
||||||
|
*/
|
||||||
|
URL getServerURL();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the user agent header to use
|
||||||
|
* @return the http user agent header to set when doing xmlrpc requests
|
||||||
|
*/
|
||||||
|
String getUserAgent();
|
||||||
|
}
|
|
@ -0,0 +1,147 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.lang.reflect.UndeclaredThrowableException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcException;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcRequest;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.util.HttpUtil;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
|
||||||
|
/** Abstract base implementation of an HTTP transport. Base class for the
|
||||||
|
* concrete implementations, like {@link XmlRpcSunHttpTransport},
|
||||||
|
* or {@link XmlRpcCommonsTransport}.
|
||||||
|
*/
|
||||||
|
public abstract class XmlRpcHttpTransport extends XmlRpcStreamTransport {
|
||||||
|
protected class ByteArrayReqWriter implements ReqWriter {
|
||||||
|
private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
ByteArrayReqWriter(XmlRpcRequest pRequest)
|
||||||
|
throws XmlRpcException, IOException, SAXException {
|
||||||
|
new ReqWriterImpl(pRequest).write(baos);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getContentLength() {
|
||||||
|
return baos.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(OutputStream pStream) throws IOException {
|
||||||
|
try {
|
||||||
|
baos.writeTo(pStream);
|
||||||
|
pStream.close();
|
||||||
|
pStream = null;
|
||||||
|
} finally {
|
||||||
|
if (pStream != null) { try { pStream.close(); } catch (Throwable ignore) {} }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The user agent string.
|
||||||
|
*/
|
||||||
|
public static final String USER_AGENT;
|
||||||
|
static {
|
||||||
|
final String p = "XmlRpcClient.properties";
|
||||||
|
final URL url = XmlRpcHttpTransport.class.getResource(p);
|
||||||
|
if (url == null) {
|
||||||
|
throw new IllegalStateException("Failed to locate resource: " + p);
|
||||||
|
}
|
||||||
|
InputStream stream = null;
|
||||||
|
try {
|
||||||
|
stream = url.openStream();
|
||||||
|
final Properties props = new Properties();
|
||||||
|
props.load(stream);
|
||||||
|
USER_AGENT = props.getProperty("user.agent");
|
||||||
|
if (USER_AGENT == null || USER_AGENT.trim().length() == 0) {
|
||||||
|
throw new IllegalStateException("The property user.agent is not set.");
|
||||||
|
}
|
||||||
|
stream.close();
|
||||||
|
stream = null;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UndeclaredThrowableException(e, "Failed to load resource " + url + ": " + e.getMessage());
|
||||||
|
} finally {
|
||||||
|
if (stream != null) { try { stream.close(); } catch (Throwable t) { /* Ignore me */ } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String userAgent;
|
||||||
|
|
||||||
|
|
||||||
|
protected XmlRpcHttpTransport(XmlRpcClient pClient, String pUserAgent) {
|
||||||
|
super(pClient);
|
||||||
|
userAgent = pUserAgent;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getUserAgent() { return userAgent; }
|
||||||
|
|
||||||
|
protected abstract void setRequestHeader(String pHeader, String pValue);
|
||||||
|
|
||||||
|
protected void setCredentials(XmlRpcHttpClientConfig pConfig)
|
||||||
|
throws XmlRpcClientException {
|
||||||
|
String auth;
|
||||||
|
try {
|
||||||
|
auth = HttpUtil.encodeBasicAuthentication(pConfig.getBasicUserName(),
|
||||||
|
pConfig.getBasicPassword(),
|
||||||
|
pConfig.getBasicEncoding());
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new XmlRpcClientException("Unsupported encoding: " + pConfig.getBasicEncoding(), e);
|
||||||
|
}
|
||||||
|
if (auth != null) {
|
||||||
|
setRequestHeader("Authorization", "Basic " + auth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setContentLength(int pLength) {
|
||||||
|
setRequestHeader("Content-Length", Integer.toString(pLength));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setCompressionHeaders(XmlRpcHttpClientConfig pConfig) {
|
||||||
|
if (pConfig.isGzipCompressing()) {
|
||||||
|
setRequestHeader("Content-Encoding", "gzip");
|
||||||
|
}
|
||||||
|
if (pConfig.isGzipRequesting()) {
|
||||||
|
setRequestHeader("Accept-Encoding", "gzip");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void initHttpHeaders(XmlRpcRequest pRequest) throws XmlRpcClientException {
|
||||||
|
XmlRpcHttpClientConfig config = (XmlRpcHttpClientConfig) pRequest.getConfig();
|
||||||
|
setRequestHeader("Content-Type", "text/xml");
|
||||||
|
if(config.getUserAgent() != null)
|
||||||
|
setRequestHeader("User-Agent", config.getUserAgent());
|
||||||
|
else
|
||||||
|
setRequestHeader("User-Agent", getUserAgent());
|
||||||
|
setCredentials(config);
|
||||||
|
setCompressionHeaders(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object sendRequest(XmlRpcRequest pRequest) throws XmlRpcException {
|
||||||
|
initHttpHeaders(pRequest);
|
||||||
|
return super.sendRequest(pRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isUsingByteArrayOutput(XmlRpcHttpClientConfig pConfig) {
|
||||||
|
return !pConfig.isEnabledForExtensions()
|
||||||
|
|| !pConfig.isContentLengthOptional();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ReqWriter newReqWriter(XmlRpcRequest pRequest)
|
||||||
|
throws XmlRpcException, IOException, SAXException {
|
||||||
|
final XmlRpcHttpClientConfig config = (XmlRpcHttpClientConfig) pRequest.getConfig();
|
||||||
|
if (isUsingByteArrayOutput(config)) {
|
||||||
|
ByteArrayReqWriter reqWriter = new ByteArrayReqWriter(pRequest);
|
||||||
|
setContentLength(reqWriter.getContentLength());
|
||||||
|
if (isCompressingRequest(config)) {
|
||||||
|
return new GzipReqWriter(reqWriter);
|
||||||
|
}
|
||||||
|
return reqWriter;
|
||||||
|
} else {
|
||||||
|
return super.newReqWriter(pRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception thrown if the HTTP status code sent by the server
|
||||||
|
* indicates that the request could not be processed. In
|
||||||
|
* general, the 400 and 500 level HTTP status codes will
|
||||||
|
* result in an XmlRpcHttpTransportException being thrown.
|
||||||
|
*/
|
||||||
|
public class XmlRpcHttpTransportException extends XmlRpcException {
|
||||||
|
private static final long serialVersionUID = -6933992871198450027L;
|
||||||
|
|
||||||
|
private final int status;
|
||||||
|
private final String statusMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance with the specified HTTP status code
|
||||||
|
* and HTTP status message.
|
||||||
|
* @param pCode The HTTP status code
|
||||||
|
* @param pMessage The HTTP status message returned by the HTTP server
|
||||||
|
*/
|
||||||
|
public XmlRpcHttpTransportException(int pCode, String pMessage) {
|
||||||
|
this(pCode, pMessage, "HTTP server returned unexpected status: " + pMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new XmlRpcHttpTransportException with the specified HTTP status code,
|
||||||
|
* HTTP status message, and exception message.
|
||||||
|
* @param httpStatusCode the HTTP status code
|
||||||
|
* @param httpStatusMessage the HTTP status message returned by the HTTP server
|
||||||
|
* @param message the exception message.
|
||||||
|
*/
|
||||||
|
public XmlRpcHttpTransportException(int httpStatusCode, String httpStatusMessage, String message) {
|
||||||
|
super( message );
|
||||||
|
this.status = httpStatusCode;
|
||||||
|
this.statusMessage = httpStatusMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the HTTP status code that resulted in this exception.
|
||||||
|
* @return the HTTP status code that resulted in this exception.
|
||||||
|
*/
|
||||||
|
public int getStatusCode()
|
||||||
|
{
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the status message returned by the HTTP server.
|
||||||
|
* @return the status message returned by the HTTP server.
|
||||||
|
*/
|
||||||
|
public String getStatusMessage()
|
||||||
|
{
|
||||||
|
return statusMessage;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/** This is a subclass of {@link IOException}, which
|
||||||
|
* allows to attach a linked exception. Throwing this
|
||||||
|
* particular instance of {@link IOException} allows
|
||||||
|
* to catch it and throw the linked exception instead.
|
||||||
|
*/
|
||||||
|
public class XmlRpcIOException extends IOException {
|
||||||
|
private static final long serialVersionUID = -7704704099502077919L;
|
||||||
|
private final Throwable linkedException;
|
||||||
|
|
||||||
|
/** Creates a new instance of {@link XmlRpcIOException}
|
||||||
|
* with the given cause.
|
||||||
|
*/
|
||||||
|
public XmlRpcIOException(Throwable t) {
|
||||||
|
super(t.getMessage());
|
||||||
|
linkedException = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the linked exception, which is the actual
|
||||||
|
* cause for this exception.
|
||||||
|
*/
|
||||||
|
public Throwable getLinkedException() {
|
||||||
|
return linkedException;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A "light" HTTP transport implementation for Java 1.4.
|
||||||
|
*/
|
||||||
|
public class XmlRpcLite14HttpTransport extends XmlRpcLiteHttpTransport {
|
||||||
|
private SSLSocketFactory sslSocketFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance.
|
||||||
|
* @param pClient The client controlling this instance.
|
||||||
|
*/
|
||||||
|
public XmlRpcLite14HttpTransport(XmlRpcClient pClient) {
|
||||||
|
super(pClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the SSL Socket Factory to use for https connections.
|
||||||
|
*/
|
||||||
|
public SSLSocketFactory getSSLSocketFactory() {
|
||||||
|
return sslSocketFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the SSL Socket Factory to use for https connections.
|
||||||
|
*/
|
||||||
|
public void setSSLSocketFactory(SSLSocketFactory pSSLSocketFactory) {
|
||||||
|
sslSocketFactory = pSSLSocketFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Socket newSocket(boolean pSSL, String pHostName, int pPort) throws UnknownHostException, IOException {
|
||||||
|
if (pSSL) {
|
||||||
|
SSLSocketFactory sslSockFactory = getSSLSocketFactory();
|
||||||
|
if (sslSockFactory == null) {
|
||||||
|
sslSockFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
|
||||||
|
}
|
||||||
|
return sslSockFactory.createSocket(pHostName, pPort);
|
||||||
|
} else {
|
||||||
|
return super.newSocket(pSSL, pHostName, pPort);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 1.4 specific factory for the lite HTTP transport,
|
||||||
|
* {@link XmlRpcLiteHttpTransport}.
|
||||||
|
*/
|
||||||
|
public class XmlRpcLite14HttpTransportFactory extends XmlRpcLiteHttpTransportFactory {
|
||||||
|
private SSLSocketFactory sslSocketFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance.
|
||||||
|
* @param pClient The client, which will invoke the factory.
|
||||||
|
*/
|
||||||
|
public XmlRpcLite14HttpTransportFactory(XmlRpcClient pClient) {
|
||||||
|
super(pClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the SSL Socket Factory to use for https connections.
|
||||||
|
*/
|
||||||
|
public SSLSocketFactory getSSLSocketFactory() {
|
||||||
|
return sslSocketFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the SSL Socket Factory to use for https connections.
|
||||||
|
*/
|
||||||
|
public void setSSLSocketFactory(SSLSocketFactory pSSLSocketFactory) {
|
||||||
|
sslSocketFactory = pSSLSocketFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public XmlRpcTransport getTransport() {
|
||||||
|
XmlRpcLite14HttpTransport transport = new XmlRpcLite14HttpTransport(getClient());
|
||||||
|
transport.setSSLSocketFactory(sslSocketFactory);
|
||||||
|
return transport;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,260 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.ConnectException;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcException;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcRequest;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcStreamRequestConfig;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.util.HttpUtil;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.util.LimitedInputStream;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A "light" HTTP transport implementation.
|
||||||
|
*/
|
||||||
|
public class XmlRpcLiteHttpTransport extends XmlRpcHttpTransport {
|
||||||
|
private static final String userAgent = USER_AGENT + " (Lite HTTP Transport)";
|
||||||
|
private boolean ssl;
|
||||||
|
private String hostname;
|
||||||
|
private String host;
|
||||||
|
private int port;
|
||||||
|
private String uri;
|
||||||
|
private Socket socket;
|
||||||
|
private OutputStream output;
|
||||||
|
private InputStream input;
|
||||||
|
private final Map<String, Object> headers = new HashMap<>();
|
||||||
|
private boolean responseGzipCompressed = false;
|
||||||
|
private XmlRpcHttpClientConfig config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance.
|
||||||
|
* @param pClient The client controlling this instance.
|
||||||
|
*/
|
||||||
|
public XmlRpcLiteHttpTransport(XmlRpcClient pClient) {
|
||||||
|
super(pClient, userAgent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object sendRequest(XmlRpcRequest pRequest) throws XmlRpcException {
|
||||||
|
config = (XmlRpcHttpClientConfig) pRequest.getConfig();
|
||||||
|
URL url = config.getServerURL();
|
||||||
|
ssl = "https".equals(url.getProtocol());
|
||||||
|
hostname = url.getHost();
|
||||||
|
int p = url.getPort();
|
||||||
|
port = p < 1 ? 80 : p;
|
||||||
|
String u = url.getFile();
|
||||||
|
uri = (u == null || "".equals(u)) ? "/" : u;
|
||||||
|
host = port == 80 ? hostname : hostname + ":" + port;
|
||||||
|
headers.put("Host", host);
|
||||||
|
return super.sendRequest(pRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
protected void setRequestHeader(String pHeader, String pValue) {
|
||||||
|
Object value = headers.get(pHeader);
|
||||||
|
if (value == null) {
|
||||||
|
headers.put(pHeader, pValue);
|
||||||
|
} else {
|
||||||
|
List<Object> list;
|
||||||
|
if (value instanceof String) {
|
||||||
|
list = new ArrayList<>();
|
||||||
|
list.add(value);
|
||||||
|
headers.put(pHeader, list);
|
||||||
|
} else {
|
||||||
|
list = (List) value;
|
||||||
|
}
|
||||||
|
list.add(pValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void close() throws XmlRpcClientException {
|
||||||
|
IOException e = null;
|
||||||
|
if (input != null) {
|
||||||
|
try {
|
||||||
|
input.close();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
e = ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (output != null) {
|
||||||
|
try {
|
||||||
|
output.close();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
if (e != null) {
|
||||||
|
e = ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (socket != null) {
|
||||||
|
try {
|
||||||
|
socket.close();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
if (e != null) {
|
||||||
|
e = ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (e != null) {
|
||||||
|
throw new XmlRpcClientException("Failed to close connection: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private OutputStream getOutputStream() throws XmlRpcException {
|
||||||
|
try {
|
||||||
|
final int retries = 3;
|
||||||
|
final int delayMillis = 100;
|
||||||
|
|
||||||
|
for (int tries = 0; ; tries++) {
|
||||||
|
try {
|
||||||
|
socket = newSocket(ssl, hostname, port);
|
||||||
|
output = new BufferedOutputStream(socket.getOutputStream()){
|
||||||
|
/** Closing the output stream would close the whole socket, which we don't want,
|
||||||
|
* because the don't want until the request is processed completely.
|
||||||
|
* A close will later occur within
|
||||||
|
* {@link XmlRpcLiteHttpTransport#close()}.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
flush();
|
||||||
|
socket.shutdownOutput();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
} catch (ConnectException e) {
|
||||||
|
if (tries >= retries) {
|
||||||
|
throw new XmlRpcException("Failed to connect to "
|
||||||
|
+ hostname + ":" + port + ": " + e.getMessage(), e);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
Thread.sleep(delayMillis);
|
||||||
|
} catch (InterruptedException ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sendRequestHeaders(output);
|
||||||
|
return output;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new XmlRpcException("Failed to open connection to "
|
||||||
|
+ hostname + ":" + port + ": " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Socket newSocket(boolean pSSL, String pHostName, int pPort) throws UnknownHostException, IOException {
|
||||||
|
if (pSSL) {
|
||||||
|
throw new IOException("Unable to create SSL connections, use the XmlRpcLite14HttpTransportFactory.");
|
||||||
|
}
|
||||||
|
return new Socket(pHostName, pPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] toHTTPBytes(String pValue) throws UnsupportedEncodingException {
|
||||||
|
return pValue.getBytes(StandardCharsets.US_ASCII);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendHeader(OutputStream pOut, String pKey, String pValue) throws IOException {
|
||||||
|
pOut.write(toHTTPBytes(pKey + ": " + pValue + "\r\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private void sendRequestHeaders(OutputStream pOut) throws IOException {
|
||||||
|
pOut.write(("POST " + uri + " HTTP/1.0\r\n").getBytes(StandardCharsets.US_ASCII));
|
||||||
|
for (Object o : headers.entrySet()) {
|
||||||
|
Map.Entry<String, Object> entry = (Map.Entry) o;
|
||||||
|
String key = entry.getKey();
|
||||||
|
Object value = entry.getValue();
|
||||||
|
if (value instanceof String) {
|
||||||
|
sendHeader(pOut, key, (String) value);
|
||||||
|
} else {
|
||||||
|
List<Object> list = (List) value;
|
||||||
|
for (Object item : list) {
|
||||||
|
sendHeader(pOut, key, (String) item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pOut.write(toHTTPBytes("\r\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isResponseGzipCompressed(XmlRpcStreamRequestConfig pConfig) {
|
||||||
|
return responseGzipCompressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected InputStream getInputStream() throws XmlRpcException {
|
||||||
|
final byte[] buffer = new byte[2048];
|
||||||
|
try {
|
||||||
|
// If reply timeout specified, set the socket timeout accordingly
|
||||||
|
if (config.getReplyTimeout() != 0)
|
||||||
|
socket.setSoTimeout(config.getReplyTimeout());
|
||||||
|
input = new BufferedInputStream(socket.getInputStream());
|
||||||
|
// start reading server response headers
|
||||||
|
String line = HttpUtil.readLine(input, buffer);
|
||||||
|
StringTokenizer tokens = new StringTokenizer(line);
|
||||||
|
tokens.nextToken(); // Skip HTTP version
|
||||||
|
String statusCode = tokens.nextToken();
|
||||||
|
String statusMsg = tokens.nextToken("\n\r");
|
||||||
|
final int code;
|
||||||
|
try {
|
||||||
|
code = Integer.parseInt(statusCode);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new XmlRpcClientException("Server returned invalid status code: "
|
||||||
|
+ statusCode + " " + statusMsg, null);
|
||||||
|
}
|
||||||
|
if (code < 200 || code > 299) {
|
||||||
|
throw new XmlRpcHttpTransportException(code, statusMsg);
|
||||||
|
}
|
||||||
|
int contentLength = -1;
|
||||||
|
for (;;) {
|
||||||
|
line = HttpUtil.readLine(input, buffer);
|
||||||
|
if ("".equals(line)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
line = line.toLowerCase();
|
||||||
|
if (line.startsWith("content-length:")) {
|
||||||
|
contentLength = Integer.parseInt(line.substring("content-length:".length()).trim());
|
||||||
|
} else if (line.startsWith("content-encoding:")) {
|
||||||
|
responseGzipCompressed = HttpUtil.isUsingGzipEncoding(line.substring("content-encoding:".length()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InputStream result;
|
||||||
|
if (contentLength == -1) {
|
||||||
|
result = input;
|
||||||
|
} else {
|
||||||
|
result = new LimitedInputStream(input, contentLength);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new XmlRpcClientException("Failed to read server response: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isUsingByteArrayOutput(XmlRpcHttpClientConfig pConfig) {
|
||||||
|
boolean result = super.isUsingByteArrayOutput(pConfig);
|
||||||
|
if (!result) {
|
||||||
|
throw new IllegalStateException("The Content-Length header is required with HTTP/1.0, and HTTP/1.1 is unsupported by the Lite HTTP Transport.");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeRequest(ReqWriter pWriter) throws XmlRpcException, IOException, SAXException {
|
||||||
|
pWriter.write(getOutputStream());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
/** Factory for the lite HTTP transport,
|
||||||
|
* {@link XmlRpcLiteHttpTransport}.
|
||||||
|
*/
|
||||||
|
public class XmlRpcLiteHttpTransportFactory extends XmlRpcTransportFactoryImpl {
|
||||||
|
/**
|
||||||
|
* Creates a new instance.
|
||||||
|
* @param pClient The client, which will invoke the factory.
|
||||||
|
*/
|
||||||
|
public XmlRpcLiteHttpTransportFactory(XmlRpcClient pClient) {
|
||||||
|
super(pClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
public XmlRpcTransport getTransport() { return new XmlRpcLiteHttpTransport(getClient()); }
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcRequestProcessorFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface of a client configuration for local rpc calls. Local
|
||||||
|
* rpc calls are mainly useful for testing, because you don't need
|
||||||
|
* a running server.
|
||||||
|
*/
|
||||||
|
public interface XmlRpcLocalClientConfig extends XmlRpcClientConfig,
|
||||||
|
XmlRpcRequestProcessorFactory {
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.LocalStreamConnection;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcException;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcRequest;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcStreamRequestConfig;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcStreamRequestProcessor;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
/** Another local transport for debugging and testing. This one is
|
||||||
|
* similar to the {@link XmlRpcLocalTransport},
|
||||||
|
* except that it adds request serialization. In other words, it is
|
||||||
|
* particularly well suited for development and testing of XML serialization
|
||||||
|
* and parsing.
|
||||||
|
*/
|
||||||
|
public class XmlRpcLocalStreamTransport extends XmlRpcStreamTransport {
|
||||||
|
private final XmlRpcStreamRequestProcessor localServer;
|
||||||
|
private LocalStreamConnection conn;
|
||||||
|
private XmlRpcRequest request;
|
||||||
|
|
||||||
|
/** Creates a new instance.
|
||||||
|
* @param pClient The client, which is controlling the transport.
|
||||||
|
* @param pServer An instance of {@link XmlRpcStreamRequestProcessor}.
|
||||||
|
*/
|
||||||
|
public XmlRpcLocalStreamTransport(XmlRpcClient pClient,
|
||||||
|
XmlRpcStreamRequestProcessor pServer) {
|
||||||
|
super(pClient);
|
||||||
|
localServer = pServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isResponseGzipCompressed(XmlRpcStreamRequestConfig pConfig) {
|
||||||
|
return pConfig.isGzipRequesting();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void close() throws XmlRpcClientException {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected InputStream getInputStream() throws XmlRpcException {
|
||||||
|
localServer.execute(conn.getConfig(), conn.getServerStreamConnection());
|
||||||
|
return new ByteArrayInputStream(conn.getResponse().toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ReqWriter newReqWriter(XmlRpcRequest pRequest)
|
||||||
|
throws XmlRpcException, IOException, SAXException {
|
||||||
|
request = pRequest;
|
||||||
|
return super.newReqWriter(pRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void writeRequest(ReqWriter pWriter)
|
||||||
|
throws XmlRpcException, IOException, SAXException {
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
pWriter.write(baos);
|
||||||
|
XmlRpcStreamRequestConfig config = (XmlRpcStreamRequestConfig) request.getConfig();
|
||||||
|
conn = new LocalStreamConnection(config, new ByteArrayInputStream(baos.toByteArray()));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcStreamRequestProcessor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Another local transport factory for debugging and testing. This one is
|
||||||
|
* similar to the {@link XmlRpcLocalTransportFactory},
|
||||||
|
* except that it adds request serialization. In other words, it is
|
||||||
|
* particularly well suited for development and testing of XML serialization
|
||||||
|
* and parsing.
|
||||||
|
*/
|
||||||
|
public class XmlRpcLocalStreamTransportFactory extends XmlRpcStreamTransportFactory {
|
||||||
|
|
||||||
|
private final XmlRpcStreamRequestProcessor server;
|
||||||
|
|
||||||
|
/** Creates a new instance.
|
||||||
|
* @param pClient The client controlling the factory.
|
||||||
|
* @param pServer An instance of {@link XmlRpcStreamRequestProcessor}.
|
||||||
|
*/
|
||||||
|
public XmlRpcLocalStreamTransportFactory(XmlRpcClient pClient,
|
||||||
|
XmlRpcStreamRequestProcessor pServer) {
|
||||||
|
super(pClient);
|
||||||
|
server = pServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public XmlRpcTransport getTransport() {
|
||||||
|
return new XmlRpcLocalStreamTransport(getClient(), server);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.TypeConverter;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.TypeConverterFactory;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcConfig;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcException;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcExtensionException;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcRequest;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcRequestProcessor;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default implementation of a local transport.
|
||||||
|
*/
|
||||||
|
public class XmlRpcLocalTransport extends XmlRpcTransportImpl {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance.
|
||||||
|
* @param pClient The client, which creates the transport.
|
||||||
|
*/
|
||||||
|
public XmlRpcLocalTransport(XmlRpcClient pClient) {
|
||||||
|
super(pClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private boolean isExtensionType(Object pObject) {
|
||||||
|
if (pObject == null) {
|
||||||
|
return true;
|
||||||
|
} else if (pObject instanceof Object[]) {
|
||||||
|
Object[] objects = (Object[]) pObject;
|
||||||
|
for (Object object : objects) {
|
||||||
|
if (isExtensionType(object)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} else if (pObject instanceof Collection) {
|
||||||
|
for (Object o : ((Collection) pObject)) {
|
||||||
|
if (isExtensionType(o)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} else if (pObject instanceof Map) {
|
||||||
|
Map<String, Object> map = (Map) pObject;
|
||||||
|
for (Object o : map.entrySet()) {
|
||||||
|
Map.Entry<String, Object> entry = (Map.Entry) o;
|
||||||
|
if (isExtensionType(entry.getKey()) || isExtensionType(entry.getValue())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return !(pObject instanceof Integer
|
||||||
|
|| pObject instanceof Date
|
||||||
|
|| pObject instanceof String
|
||||||
|
|| pObject instanceof byte[]
|
||||||
|
|| pObject instanceof Double);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object sendRequest(XmlRpcRequest pRequest) throws XmlRpcException {
|
||||||
|
XmlRpcConfig config = pRequest.getConfig();
|
||||||
|
if (!config.isEnabledForExtensions()) {
|
||||||
|
for (int i = 0; i < pRequest.getParameterCount(); i++) {
|
||||||
|
if (isExtensionType(pRequest.getParameter(i))) {
|
||||||
|
throw new XmlRpcExtensionException("Parameter " + i + " has invalid type, if isEnabledForExtensions() == false");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final XmlRpcRequestProcessor server = ((XmlRpcLocalClientConfig) config).getXmlRpcServer();
|
||||||
|
Object result;
|
||||||
|
try {
|
||||||
|
result = server.execute(pRequest);
|
||||||
|
} catch (XmlRpcException t) {
|
||||||
|
throw t;
|
||||||
|
} catch (Throwable t) {
|
||||||
|
throw new XmlRpcClientException("Failed to invoke method " + pRequest.getMethodName()
|
||||||
|
+ ": " + t.getMessage(), t);
|
||||||
|
}
|
||||||
|
if (!config.isEnabledForExtensions()) {
|
||||||
|
if (isExtensionType(result)) {
|
||||||
|
throw new XmlRpcExtensionException("Result has invalid type, if isEnabledForExtensions() == false");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final TypeConverterFactory typeConverterFactory = server.getTypeConverterFactory();
|
||||||
|
final TypeConverter typeConverter = typeConverterFactory.getTypeConverter(result.getClass());
|
||||||
|
return typeConverter.backConvert(result);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>A transport factory being used for local XML-RPC calls. Local XML-RPC
|
||||||
|
* calls are mainly useful for development and unit testing: Both client
|
||||||
|
* and server are runing within the same JVM and communication is implemented
|
||||||
|
* in simple method invokcations.</p>
|
||||||
|
* <p>This class is thread safe and the returned instance of
|
||||||
|
* {@link XmlRpcTransport} will always return the
|
||||||
|
* same object, an instance of {@link XmlRpcLocalTransport}</p>
|
||||||
|
*/
|
||||||
|
public class XmlRpcLocalTransportFactory extends XmlRpcTransportFactoryImpl {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance, operated by the given client.
|
||||||
|
* @param pClient The client, which will invoke the factory.
|
||||||
|
*/
|
||||||
|
public XmlRpcLocalTransportFactory(XmlRpcClient pClient) {
|
||||||
|
super(pClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final XmlRpcTransport LOCAL_TRANSPORT = new XmlRpcLocalTransport(getClient());
|
||||||
|
|
||||||
|
public XmlRpcTransport getTransport() { return LOCAL_TRANSPORT; }
|
||||||
|
}
|
|
@ -0,0 +1,195 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
import java.util.zip.GZIPOutputStream;
|
||||||
|
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcException;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcRequest;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcStreamConfig;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcStreamRequestConfig;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.parser.XmlRpcResponseParser;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.serializer.XmlRpcWriter;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.util.SAXParsers;
|
||||||
|
import org.xml.sax.ContentHandler;
|
||||||
|
import org.xml.sax.InputSource;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
import org.xml.sax.XMLReader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of a transport class, which is based on an output
|
||||||
|
* stream for sending the request and an input stream for receiving
|
||||||
|
* the response,
|
||||||
|
*/
|
||||||
|
public abstract class XmlRpcStreamTransport extends XmlRpcTransportImpl {
|
||||||
|
|
||||||
|
protected interface ReqWriter {
|
||||||
|
/**
|
||||||
|
* Writes the requests data to the given output stream.
|
||||||
|
* The method ensures, that the target is being closed.
|
||||||
|
*/
|
||||||
|
void write(OutputStream pStream) throws XmlRpcException, IOException, SAXException;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class ReqWriterImpl implements ReqWriter {
|
||||||
|
private final XmlRpcRequest request;
|
||||||
|
|
||||||
|
protected ReqWriterImpl(XmlRpcRequest pRequest) {
|
||||||
|
request = pRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the requests uncompressed XML data to the given
|
||||||
|
* output stream. Ensures, that the output stream is being
|
||||||
|
* closed.
|
||||||
|
*/
|
||||||
|
public void write(OutputStream pStream)
|
||||||
|
throws XmlRpcException, IOException, SAXException {
|
||||||
|
final XmlRpcStreamConfig config = (XmlRpcStreamConfig) request.getConfig();
|
||||||
|
try {
|
||||||
|
ContentHandler h = getClient().getXmlWriterFactory().getXmlWriter(config, pStream);
|
||||||
|
XmlRpcWriter xw = new XmlRpcWriter(config, h, getClient().getTypeFactory());
|
||||||
|
xw.write(request);
|
||||||
|
pStream.close();
|
||||||
|
pStream = null;
|
||||||
|
} finally {
|
||||||
|
if (pStream != null) { try { pStream.close(); } catch (Throwable ignore) {} }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class GzipReqWriter implements ReqWriter {
|
||||||
|
private final ReqWriter reqWriter;
|
||||||
|
protected GzipReqWriter(ReqWriter pReqWriter) {
|
||||||
|
reqWriter = pReqWriter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(OutputStream pStream) throws XmlRpcException, IOException, SAXException {
|
||||||
|
try {
|
||||||
|
GZIPOutputStream gStream = new GZIPOutputStream(pStream);
|
||||||
|
reqWriter.write(gStream);
|
||||||
|
pStream.close();
|
||||||
|
pStream = null;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new XmlRpcException("Failed to write request: " + e.getMessage(), e);
|
||||||
|
} finally {
|
||||||
|
if (pStream != null) { try { pStream.close(); } catch (Throwable ignore) {} }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance on behalf of the given client.
|
||||||
|
*/
|
||||||
|
protected XmlRpcStreamTransport(XmlRpcClient pClient) {
|
||||||
|
super(pClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the connection and ensures, that all resources are being
|
||||||
|
* released.
|
||||||
|
*/
|
||||||
|
protected abstract void close() throws XmlRpcClientException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns, whether the response is gzip compressed.
|
||||||
|
* @param pConfig The clients configuration.
|
||||||
|
* @return Whether the response stream is gzip compressed.
|
||||||
|
*/
|
||||||
|
protected abstract boolean isResponseGzipCompressed(XmlRpcStreamRequestConfig pConfig);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the input stream, from which the response is
|
||||||
|
* being read.
|
||||||
|
*/
|
||||||
|
protected abstract InputStream getInputStream() throws XmlRpcException;
|
||||||
|
|
||||||
|
protected boolean isCompressingRequest(XmlRpcStreamRequestConfig pConfig) {
|
||||||
|
return pConfig.isEnabledForExtensions()
|
||||||
|
&& pConfig.isGzipCompressing();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of {@link ReqWriter}.
|
||||||
|
* @throws XmlRpcException Creating the instance failed.
|
||||||
|
* @throws IOException Creating the instance failed, because
|
||||||
|
* an {@link IOException} occurs.
|
||||||
|
* @throws SAXException Creating the instance failed, because
|
||||||
|
* the request could not be parsed.
|
||||||
|
*/
|
||||||
|
protected ReqWriter newReqWriter(XmlRpcRequest pRequest)
|
||||||
|
throws XmlRpcException, IOException, SAXException {
|
||||||
|
ReqWriter reqWriter = new ReqWriterImpl(pRequest);
|
||||||
|
if (isCompressingRequest((XmlRpcStreamRequestConfig) pRequest.getConfig())) {
|
||||||
|
reqWriter = new GzipReqWriter(reqWriter);
|
||||||
|
}
|
||||||
|
return reqWriter;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void writeRequest(ReqWriter pWriter)
|
||||||
|
throws XmlRpcException, IOException, SAXException;
|
||||||
|
|
||||||
|
public Object sendRequest(XmlRpcRequest pRequest) throws XmlRpcException {
|
||||||
|
XmlRpcStreamRequestConfig config = (XmlRpcStreamRequestConfig) pRequest.getConfig();
|
||||||
|
boolean closed = false;
|
||||||
|
try {
|
||||||
|
ReqWriter reqWriter = newReqWriter(pRequest);
|
||||||
|
writeRequest(reqWriter);
|
||||||
|
InputStream istream = getInputStream();
|
||||||
|
if (isResponseGzipCompressed(config)) {
|
||||||
|
istream = new GZIPInputStream(istream);
|
||||||
|
}
|
||||||
|
Object result = readResponse(config, istream);
|
||||||
|
closed = true;
|
||||||
|
close();
|
||||||
|
return result;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new XmlRpcException("Failed to read server's response: "
|
||||||
|
+ e.getMessage(), e);
|
||||||
|
} catch (SAXException e) {
|
||||||
|
Exception ex = e.getException();
|
||||||
|
if (ex instanceof XmlRpcException) {
|
||||||
|
throw (XmlRpcException) ex;
|
||||||
|
}
|
||||||
|
throw new XmlRpcException("Failed to generate request: "
|
||||||
|
+ e.getMessage(), e);
|
||||||
|
} finally {
|
||||||
|
if (!closed) { try { close(); } catch (Throwable ignore) {} }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected XMLReader newXMLReader() throws XmlRpcException {
|
||||||
|
return SAXParsers.newXMLReader();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Object readResponse(XmlRpcStreamRequestConfig pConfig, InputStream pStream) throws XmlRpcException {
|
||||||
|
InputSource isource = new InputSource(pStream);
|
||||||
|
XMLReader xr = newXMLReader();
|
||||||
|
XmlRpcResponseParser xp;
|
||||||
|
try {
|
||||||
|
xp = new XmlRpcResponseParser(pConfig, getClient().getTypeFactory());
|
||||||
|
xr.setContentHandler(xp);
|
||||||
|
xr.parse(isource);
|
||||||
|
} catch (SAXException e) {
|
||||||
|
throw new XmlRpcClientException("Failed to parse server's response: " + e.getMessage(), e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new XmlRpcClientException("Failed to read server's response: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
if (xp.isSuccess()) {
|
||||||
|
return xp.getResult();
|
||||||
|
}
|
||||||
|
Throwable t = xp.getErrorCause();
|
||||||
|
if (t == null) {
|
||||||
|
throw new XmlRpcException(xp.getErrorCode(), xp.getErrorMessage());
|
||||||
|
}
|
||||||
|
if (t instanceof XmlRpcException) {
|
||||||
|
throw (XmlRpcException) t;
|
||||||
|
}
|
||||||
|
if (t instanceof RuntimeException) {
|
||||||
|
throw (RuntimeException) t;
|
||||||
|
}
|
||||||
|
throw new XmlRpcException(xp.getErrorCode(), xp.getErrorMessage(), t);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract base implementation of a factory for stream transports.
|
||||||
|
*/
|
||||||
|
public abstract class XmlRpcStreamTransportFactory extends XmlRpcTransportFactoryImpl {
|
||||||
|
protected XmlRpcStreamTransportFactory(XmlRpcClient pClient) {
|
||||||
|
super(pClient);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import javax.net.ssl.HttpsURLConnection;
|
||||||
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation of an HTTP transport in Java 1.4, based on the
|
||||||
|
* {@link java.net.HttpURLConnection} class. Adds support for the
|
||||||
|
* {@link SSLSocketFactory}.
|
||||||
|
*/
|
||||||
|
public class XmlRpcSun14HttpTransport extends XmlRpcSunHttpTransport {
|
||||||
|
private SSLSocketFactory sslSocketFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance.
|
||||||
|
* @param pClient The client controlling this instance.
|
||||||
|
*/
|
||||||
|
public XmlRpcSun14HttpTransport(XmlRpcClient pClient) {
|
||||||
|
super(pClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the SSLSocketFactory used to create secure sockets.
|
||||||
|
* @param pSocketFactory The SSLSocketFactory to use.
|
||||||
|
*/
|
||||||
|
public void setSSLSocketFactory(SSLSocketFactory pSocketFactory) {
|
||||||
|
sslSocketFactory = pSocketFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the SSLSocketFactory used to create secure sockets.
|
||||||
|
*/
|
||||||
|
public SSLSocketFactory getSSLSocketFactory() {
|
||||||
|
return sslSocketFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected URLConnection newURLConnection(URL pURL) throws IOException {
|
||||||
|
final URLConnection conn = super.newURLConnection(pURL);
|
||||||
|
final SSLSocketFactory sslSockFactory = getSSLSocketFactory();
|
||||||
|
if ((sslSockFactory != null) && (conn instanceof HttpsURLConnection))
|
||||||
|
((HttpsURLConnection)conn).setSSLSocketFactory(sslSockFactory);
|
||||||
|
return conn;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation of an HTTP transport factory in Java 1.4, based
|
||||||
|
* on the {@link java.net.HttpURLConnection} class.
|
||||||
|
*/
|
||||||
|
public class XmlRpcSun14HttpTransportFactory extends XmlRpcTransportFactoryImpl {
|
||||||
|
private SSLSocketFactory sslSocketFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new factory, which creates transports for the given client.
|
||||||
|
* @param pClient The client, which is operating the factory.
|
||||||
|
*/
|
||||||
|
public XmlRpcSun14HttpTransportFactory(XmlRpcClient pClient) {
|
||||||
|
super(pClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the SSLSocketFactory to be used by transports.
|
||||||
|
* @param pSocketFactory The SSLSocketFactory to use.
|
||||||
|
*/
|
||||||
|
public void setSSLSocketFactory(SSLSocketFactory pSocketFactory) {
|
||||||
|
sslSocketFactory = pSocketFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the SSLSocketFactory to be used by transports.
|
||||||
|
*/
|
||||||
|
public SSLSocketFactory getSSLSocketFactory() {
|
||||||
|
return sslSocketFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public XmlRpcTransport getTransport() {
|
||||||
|
XmlRpcSun14HttpTransport transport = new XmlRpcSun14HttpTransport(getClient());
|
||||||
|
transport.setSSLSocketFactory(sslSocketFactory);
|
||||||
|
return transport;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcRequest;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.Proxy;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
|
||||||
|
import javax.net.ssl.HttpsURLConnection;
|
||||||
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation of an HTTP transport in Java 1.4, based on the
|
||||||
|
* {@link java.net.HttpURLConnection} class. Adds support for the
|
||||||
|
* {@link Proxy} class.
|
||||||
|
*/
|
||||||
|
public class XmlRpcSun15HttpTransport extends XmlRpcSun14HttpTransport {
|
||||||
|
/**
|
||||||
|
* Creates a new instance.
|
||||||
|
* @param pClient The client controlling this instance.
|
||||||
|
*/
|
||||||
|
public XmlRpcSun15HttpTransport(XmlRpcClient pClient) {
|
||||||
|
super(pClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Proxy proxy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the proxy to use.
|
||||||
|
*/
|
||||||
|
public void setProxy(Proxy pProxy) {
|
||||||
|
proxy = pProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the proxy to use.
|
||||||
|
*/
|
||||||
|
public Proxy getProxy() {
|
||||||
|
return proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void initHttpHeaders(XmlRpcRequest pRequest)
|
||||||
|
throws XmlRpcClientException {
|
||||||
|
final XmlRpcHttpClientConfig config = (XmlRpcHttpClientConfig) pRequest.getConfig();
|
||||||
|
int connectionTimeout = config.getConnectionTimeout();
|
||||||
|
if (connectionTimeout > 0) {
|
||||||
|
getURLConnection().setConnectTimeout(connectionTimeout);
|
||||||
|
}
|
||||||
|
int replyTimeout = config.getReplyTimeout();
|
||||||
|
if (replyTimeout > 0) {
|
||||||
|
getURLConnection().setReadTimeout(replyTimeout);
|
||||||
|
}
|
||||||
|
super.initHttpHeaders(pRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected URLConnection newURLConnection(URL pURL) throws IOException {
|
||||||
|
final Proxy prox = getProxy();
|
||||||
|
final URLConnection conn = prox == null ? pURL.openConnection() : pURL.openConnection(prox);
|
||||||
|
final SSLSocketFactory sslSockFactory = getSSLSocketFactory();
|
||||||
|
if (sslSockFactory != null && conn instanceof HttpsURLConnection) {
|
||||||
|
((HttpsURLConnection)conn).setSSLSocketFactory(sslSockFactory);
|
||||||
|
}
|
||||||
|
return conn;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.Proxy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation of an HTTP transport in Java 1.5, based on the
|
||||||
|
* {@link java.net.HttpURLConnection} class.
|
||||||
|
*/
|
||||||
|
public class XmlRpcSun15HttpTransportFactory extends XmlRpcSun14HttpTransportFactory {
|
||||||
|
private Proxy proxy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new factory, which creates transports for the given client.
|
||||||
|
* @param pClient The client, which is operating the factory.
|
||||||
|
*/
|
||||||
|
public XmlRpcSun15HttpTransportFactory(XmlRpcClient pClient) {
|
||||||
|
super(pClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the proxy to use.
|
||||||
|
* @param proxyHost The proxy hostname.
|
||||||
|
* @param proxyPort The proxy port number.
|
||||||
|
* @throws IllegalArgumentException if the proxyHost parameter is null or if
|
||||||
|
* the proxyPort parameter is outside the range of valid port values.
|
||||||
|
*/
|
||||||
|
public void setProxy(String proxyHost, int proxyPort) {
|
||||||
|
setProxy(new Proxy(Proxy.Type.HTTP,new InetSocketAddress(proxyHost,proxyPort)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the proxy to use.
|
||||||
|
* @param pProxy The proxy settings.
|
||||||
|
*/
|
||||||
|
public void setProxy(Proxy pProxy) {
|
||||||
|
proxy = pProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public XmlRpcTransport getTransport() {
|
||||||
|
XmlRpcSun15HttpTransport transport = new XmlRpcSun15HttpTransport(getClient());
|
||||||
|
transport.setSSLSocketFactory(getSSLSocketFactory());
|
||||||
|
transport.setProxy(proxy);
|
||||||
|
return transport;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcException;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcRequest;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcStreamRequestConfig;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.util.HttpUtil;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation of an HTTP transport, based on the
|
||||||
|
* {@link HttpURLConnection} class.
|
||||||
|
*/
|
||||||
|
public class XmlRpcSunHttpTransport extends XmlRpcHttpTransport {
|
||||||
|
private static final String userAgent = USER_AGENT + " (Sun HTTP Transport)";
|
||||||
|
private URLConnection conn;
|
||||||
|
|
||||||
|
/** Creates a new instance.
|
||||||
|
* @param pClient The client controlling this instance.
|
||||||
|
*/
|
||||||
|
public XmlRpcSunHttpTransport(XmlRpcClient pClient) {
|
||||||
|
super(pClient, userAgent);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected URLConnection newURLConnection(URL pURL) throws IOException {
|
||||||
|
return pURL.openConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For use by subclasses.
|
||||||
|
*/
|
||||||
|
protected URLConnection getURLConnection() {
|
||||||
|
return conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object sendRequest(XmlRpcRequest pRequest) throws XmlRpcException {
|
||||||
|
XmlRpcHttpClientConfig config = (XmlRpcHttpClientConfig) pRequest.getConfig();
|
||||||
|
try {
|
||||||
|
final URLConnection c = conn = newURLConnection(config.getServerURL());
|
||||||
|
c.setUseCaches(false);
|
||||||
|
c.setDoInput(true);
|
||||||
|
c.setDoOutput(true);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new XmlRpcException("Failed to create URLConnection: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
return super.sendRequest(pRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setRequestHeader(String pHeader, String pValue) {
|
||||||
|
getURLConnection().setRequestProperty(pHeader, pValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void close() throws XmlRpcClientException {
|
||||||
|
final URLConnection c = getURLConnection();
|
||||||
|
if (c instanceof HttpURLConnection) {
|
||||||
|
((HttpURLConnection) c).disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isResponseGzipCompressed(XmlRpcStreamRequestConfig pConfig) {
|
||||||
|
return HttpUtil.isUsingGzipEncoding(getURLConnection().getHeaderField("Content-Encoding"));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected InputStream getInputStream() throws XmlRpcException {
|
||||||
|
try {
|
||||||
|
URLConnection connection = getURLConnection();
|
||||||
|
if ( connection instanceof HttpURLConnection ) {
|
||||||
|
HttpURLConnection httpConnection = (HttpURLConnection) connection;
|
||||||
|
int responseCode = httpConnection.getResponseCode();
|
||||||
|
if (responseCode < 200 || responseCode > 299) {
|
||||||
|
throw new XmlRpcHttpTransportException(responseCode, httpConnection.getResponseMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return connection.getInputStream();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new XmlRpcException("Failed to create input stream: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void writeRequest(ReqWriter pWriter) throws IOException, XmlRpcException, SAXException {
|
||||||
|
pWriter.write(getURLConnection().getOutputStream());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
/** Default implementation of a HTTP transport factory, based on the
|
||||||
|
* {@link java.net.HttpURLConnection} class.
|
||||||
|
*/
|
||||||
|
public class XmlRpcSunHttpTransportFactory extends XmlRpcTransportFactoryImpl {
|
||||||
|
/** Creates a new factory, which creates transports for the given client.
|
||||||
|
* @param pClient The client, which is operating the factory.
|
||||||
|
*/
|
||||||
|
public XmlRpcSunHttpTransportFactory(XmlRpcClient pClient) {
|
||||||
|
super(pClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
public XmlRpcTransport getTransport() {
|
||||||
|
return new XmlRpcSunHttpTransport(getClient());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcException;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Interface from XML-RPC to an underlying transport, most likely based on HTTP.</p>
|
||||||
|
*/
|
||||||
|
public interface XmlRpcTransport {
|
||||||
|
|
||||||
|
/** Send an XML-RPC message. This method is called to send a message to the
|
||||||
|
* other party.
|
||||||
|
* @param pRequest The request being performed.
|
||||||
|
* @return Result object, if invoking the remote method was successfull.
|
||||||
|
* @throws XmlRpcException Performing the request failed.
|
||||||
|
*/
|
||||||
|
Object sendRequest(XmlRpcRequest pRequest) throws XmlRpcException;
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
/** Interface of an object creating instances of
|
||||||
|
* {@link XmlRpcTransport}. The implementation
|
||||||
|
* is typically based on singletons.
|
||||||
|
*/
|
||||||
|
public interface XmlRpcTransportFactory {
|
||||||
|
/** Returns an instance of {@link XmlRpcTransport}. This may
|
||||||
|
* be a singleton, but the caller should not depend on that:
|
||||||
|
* A new instance may as well be created for any request.
|
||||||
|
* @return The configured transport.
|
||||||
|
*/
|
||||||
|
public XmlRpcTransport getTransport();
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract base implementation of an {@link XmlRpcTransportFactory}.
|
||||||
|
*/
|
||||||
|
public abstract class XmlRpcTransportFactoryImpl implements XmlRpcTransportFactory {
|
||||||
|
private final XmlRpcClient client;
|
||||||
|
|
||||||
|
/** Creates a new instance.
|
||||||
|
* @param pClient The client, which will invoke the factory.
|
||||||
|
*/
|
||||||
|
protected XmlRpcTransportFactoryImpl(XmlRpcClient pClient) {
|
||||||
|
client = pClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the client operating this factory.
|
||||||
|
* @return The client.
|
||||||
|
*/
|
||||||
|
public XmlRpcClient getClient() { return client; }
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.xbib.netty.http.xmlrpc.client;
|
||||||
|
|
||||||
|
|
||||||
|
/** Abstract base implementation of an {@link org.apache.xmlrpc.client.XmlRpcTransport}.
|
||||||
|
*/
|
||||||
|
public abstract class XmlRpcTransportImpl implements XmlRpcTransport {
|
||||||
|
private final XmlRpcClient client;
|
||||||
|
|
||||||
|
/** Creates a new instance.
|
||||||
|
* @param pClient The client, which creates the transport.
|
||||||
|
*/
|
||||||
|
protected XmlRpcTransportImpl(XmlRpcClient pClient) {
|
||||||
|
client = pClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the client, which created this transport.
|
||||||
|
* @return The client.
|
||||||
|
*/
|
||||||
|
public XmlRpcClient getClient() { return client; }
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client.test;
|
||||||
|
|
||||||
|
import org.xbib.netty.http.xmlrpc.client.ClientFactory;
|
||||||
|
import org.xbib.netty.http.xmlrpc.client.XmlRpcClient;
|
||||||
|
import org.xbib.netty.http.xmlrpc.client.XmlRpcClientConfigImpl;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcException;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcHttpRequestConfig;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcRequest;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcRequestConfig;
|
||||||
|
import org.xbib.netty.http.xmlrpc.server.AbstractReflectiveHandlerMapping;
|
||||||
|
import org.xbib.netty.http.xmlrpc.server.XmlRpcHandlerMapping;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test case for supported authentication variants.
|
||||||
|
*/
|
||||||
|
public class AuthenticationTest extends XmlRpcTestCase {
|
||||||
|
private static final String PASSWORD = "98765432109876543210987654321098765432109876543210";
|
||||||
|
private static final String USER_NAME = "01234567890123456789012345678901234567890123456789"
|
||||||
|
+ "\u00C4\u00D6\u00DC\u00F6\u00FC\u00E4\u00DF";
|
||||||
|
|
||||||
|
/** An interface, which is being implemented by the
|
||||||
|
* server.
|
||||||
|
*/
|
||||||
|
public interface Adder {
|
||||||
|
/** Returns the sum of the given integers.
|
||||||
|
*/
|
||||||
|
public int add(int pNum1, int pNum2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Implementation of {@link DynamicProxyTest.Adder}, which is used by
|
||||||
|
* the server.
|
||||||
|
*/
|
||||||
|
public static class AdderImpl implements Adder {
|
||||||
|
public int add(int pNum1, int pNum2) {
|
||||||
|
return pNum1 + pNum2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected XmlRpcHandlerMapping getHandlerMapping() throws IOException, XmlRpcException {
|
||||||
|
XmlRpcHandlerMapping mapping = getHandlerMapping("AuthenticationTest.properties");
|
||||||
|
((AbstractReflectiveHandlerMapping) mapping).setAuthenticationHandler(new AbstractReflectiveHandlerMapping.AuthenticationHandler(){
|
||||||
|
public boolean isAuthorized(XmlRpcRequest pRequest)
|
||||||
|
throws XmlRpcException {
|
||||||
|
XmlRpcRequestConfig config = pRequest.getConfig();
|
||||||
|
if (config instanceof XmlRpcHttpRequestConfig) {
|
||||||
|
XmlRpcHttpRequestConfig httpRequestConfig = (XmlRpcHttpRequestConfig) config;
|
||||||
|
return USER_NAME.equals(httpRequestConfig.getBasicUserName())
|
||||||
|
&& PASSWORD.equals(httpRequestConfig.getBasicPassword());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected XmlRpcClientConfigImpl getConfig(ClientProvider pProvider)
|
||||||
|
throws Exception {
|
||||||
|
XmlRpcClientConfigImpl config = super.getConfig(pProvider);
|
||||||
|
config.setBasicUserName(USER_NAME);
|
||||||
|
config.setBasicPassword(PASSWORD);
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClientFactory getClientFactory(ClientProvider pProvider) throws Exception {
|
||||||
|
XmlRpcClient client = pProvider.getClient();
|
||||||
|
client.setConfig(getConfig(pProvider));
|
||||||
|
return new ClientFactory(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Tests calling the {@link Adder#add(int,int)} method
|
||||||
|
* by using an object, which has been created by the
|
||||||
|
* {@link ClientFactory}.
|
||||||
|
*/
|
||||||
|
public void testAdderCall() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testAdderCall(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testAdderCall(ClientProvider pProvider) throws Exception {
|
||||||
|
ClientFactory factory = getClientFactory(pProvider);
|
||||||
|
Adder adder = (Adder) factory.newInstance(Adder.class);
|
||||||
|
assertEquals(6, adder.add(2, 4));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,906 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client.test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import javax.xml.namespace.QName;
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
|
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
import org.w3c.dom.Node;
|
||||||
|
import org.xbib.netty.http.xmlrpc.client.XmlRpcClient;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcException;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcExtensionException;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcInvocationException;
|
||||||
|
import org.xbib.netty.http.xmlrpc.server.XmlRpcHandlerMapping;
|
||||||
|
import org.xml.sax.InputSource;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstract test case, to be implemented for the various
|
||||||
|
* transport classes.
|
||||||
|
*/
|
||||||
|
public class BaseTest extends XmlRpcTestCase {
|
||||||
|
|
||||||
|
/** The remote class being invoked by the test case.
|
||||||
|
*/
|
||||||
|
public static class Remote {
|
||||||
|
/** Returns the argument, multiplied by two.
|
||||||
|
* @param pArg The argument being doubled.
|
||||||
|
* @return The argument, multiplied by two.
|
||||||
|
*/
|
||||||
|
public int byteParam(byte pArg) { return pArg*2; }
|
||||||
|
/** Returns the argument, multiplied by two.
|
||||||
|
* @param pArg The argument being doubled.
|
||||||
|
* @return The argument, multiplied by two.
|
||||||
|
*/
|
||||||
|
public byte byteResult(byte pArg) { return (byte) (pArg*2); }
|
||||||
|
/** Returns the argument, multiplied by two.
|
||||||
|
* @param pArg The argument being doubled.
|
||||||
|
* @return The argument, multiplied by two.
|
||||||
|
*/
|
||||||
|
public int shortParam(short pArg) { return pArg*2; }
|
||||||
|
/** Returns the argument, multiplied by two.
|
||||||
|
* @param pArg The argument being doubled.
|
||||||
|
* @return The argument, multiplied by two.
|
||||||
|
*/
|
||||||
|
public short shortResult(short pArg) { return (short) (pArg*2); }
|
||||||
|
/** Returns the argument, multiplied by two.
|
||||||
|
* @param pArg The argument being doubled.
|
||||||
|
* @return The argument, multiplied by two.
|
||||||
|
*/
|
||||||
|
public int intParam(int pArg) { return pArg*2; }
|
||||||
|
/** Returns the argument, multiplied by two.
|
||||||
|
* @param pArg The argument being doubled.
|
||||||
|
* @return The argument, multiplied by two.
|
||||||
|
*/
|
||||||
|
public int longParam(long pArg) { return (int) (pArg*2); }
|
||||||
|
/** Returns the argument, multiplied by two.
|
||||||
|
* @param pArg The argument being doubled.
|
||||||
|
* @return The argument, multiplied by two.
|
||||||
|
*/
|
||||||
|
public long longResult(long pArg) { return pArg*2; }
|
||||||
|
/** Returns the argument, multiplied by two.
|
||||||
|
* @param pArg The argument being doubled.
|
||||||
|
* @return The argument, multiplied by two.
|
||||||
|
*/
|
||||||
|
public double floatParam(float pArg) { return pArg*2; }
|
||||||
|
/** Returns the argument, multiplied by two.
|
||||||
|
* @param pArg The argument being doubled.
|
||||||
|
* @return The argument, multiplied by two.
|
||||||
|
*/
|
||||||
|
public float floatResult(float pArg) { return pArg*2; }
|
||||||
|
/** Returns the argument, multiplied by two.
|
||||||
|
* @param pArg The argument being doubled.
|
||||||
|
* @return The argument, multiplied by two.
|
||||||
|
*/
|
||||||
|
public double doubleParam(double pArg) { return pArg*2; }
|
||||||
|
/** Returns the argument, multiplied by two.
|
||||||
|
* @param pArg The argument being doubled.
|
||||||
|
* @return The argument, multiplied by two.
|
||||||
|
*/
|
||||||
|
public double doubleResult(double pArg) { return pArg*2; }
|
||||||
|
/** Returns the argument, concatenated with itself.
|
||||||
|
* @param pArg The argument being concatenated.
|
||||||
|
* @return The argument, concatenated with itself.
|
||||||
|
*/
|
||||||
|
public String stringParam(String pArg) { return pArg+pArg; }
|
||||||
|
/**
|
||||||
|
* Throws a NullPointerException.
|
||||||
|
*/
|
||||||
|
public Object throwNPE() {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
/** Returns the argument, concatenated with itself.
|
||||||
|
* @param pArg The argument being concatenated.
|
||||||
|
* @return The argument, concatenated with itself.
|
||||||
|
*/
|
||||||
|
public String nullableStringParam(String pArg) {
|
||||||
|
if (pArg == null) {
|
||||||
|
pArg = "";
|
||||||
|
}
|
||||||
|
return pArg+pArg;
|
||||||
|
}
|
||||||
|
/** Returns the argument, concatenated with itself.
|
||||||
|
* @param pArg The argument being concatenated.
|
||||||
|
* @return The argument, concatenated with itself.
|
||||||
|
*/
|
||||||
|
public String nullableStringResult(String pArg) {
|
||||||
|
if (pArg == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return pArg+pArg;
|
||||||
|
}
|
||||||
|
/** Returns the sum of the bytes in the given byte array.
|
||||||
|
* @param pArg The array of bytes being added.
|
||||||
|
* @return Sum over the bytes in the array.
|
||||||
|
*/
|
||||||
|
public int byteArrayParam(byte[] pArg) {
|
||||||
|
int sum = 0;
|
||||||
|
for (int i = 0; i < pArg.length; i++) {
|
||||||
|
sum += pArg[i];
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
/** Returns an array with the bytes 0..pArg.
|
||||||
|
* @param pArg Requestes byte array length.
|
||||||
|
* @return Byte array with 0..pArg.
|
||||||
|
*/
|
||||||
|
public byte[] byteArrayResult(int pArg) {
|
||||||
|
byte[] result = new byte[pArg];
|
||||||
|
for (int i = 0; i < result.length; i++) {
|
||||||
|
result[i] = (byte) i;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
/** Returns the sum over the objects in the array.
|
||||||
|
* @param pArg Object array being added
|
||||||
|
* @return Sum over the objects in the array
|
||||||
|
*/
|
||||||
|
public int objectArrayParam(Object[] pArg) {
|
||||||
|
int sum = 0;
|
||||||
|
for (int i = 0; i < pArg.length; i++) {
|
||||||
|
if (pArg[i] instanceof Number) {
|
||||||
|
sum += ((Number) pArg[i]).intValue();
|
||||||
|
} else {
|
||||||
|
sum += Integer.parseInt((String) pArg[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
/** Returns an array of integers with the values
|
||||||
|
* 0..pArg.
|
||||||
|
* @param pArg Requested array length.
|
||||||
|
* @return Array of integers with the values 0..pArg
|
||||||
|
*/
|
||||||
|
public Object[] objectArrayResult(int pArg) {
|
||||||
|
Object[] result = new Object[pArg];
|
||||||
|
for (int i = 0; i < result.length; i++) {
|
||||||
|
result[i] = new Integer(i);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
/** Returns a sum over the entries in the map. Each
|
||||||
|
* key is multiplied with its value.
|
||||||
|
* @param pArg The map being iterated.
|
||||||
|
* @return Sum of keys, multiplied by their values.
|
||||||
|
*/
|
||||||
|
public int mapParam(Map pArg) {
|
||||||
|
int sum = 0;
|
||||||
|
for (Iterator iter = pArg.entrySet().iterator(); iter.hasNext(); ) {
|
||||||
|
Map.Entry entry = (Map.Entry) iter.next();
|
||||||
|
String key = (String) entry.getKey();
|
||||||
|
Integer value = (Integer) entry.getValue();
|
||||||
|
sum += Integer.parseInt(key) * value.intValue();
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
/** Returns a map with the stringified values 0..pArg as
|
||||||
|
* keys and the corresponding integers as values.
|
||||||
|
* @param pArg Requested map size.
|
||||||
|
* @return Map with the keys "0".."pArg" and
|
||||||
|
* 0..pArg as values.
|
||||||
|
*/
|
||||||
|
public Map mapResult(int pArg) {
|
||||||
|
Map result = new HashMap();
|
||||||
|
for (int i = 0; i < pArg; i++) {
|
||||||
|
result.put(Integer.toString(i), new Integer(i));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
/** Returns the sum of all "int" nodes in <code>pNode</code>.
|
||||||
|
* @param pNode The node being counted.
|
||||||
|
* @return The sum of the values of all "int" nodes.
|
||||||
|
*/
|
||||||
|
public int nodeParam(Node pNode) {
|
||||||
|
if (pNode.getNodeType() != Node.DOCUMENT_NODE) {
|
||||||
|
throw new IllegalStateException("Expected document node, got " + pNode);
|
||||||
|
}
|
||||||
|
Element e = ((Document) pNode).getDocumentElement();
|
||||||
|
if (!ROOT_TAG.equals(e.getLocalName()) || !INT_URI.equals(e.getNamespaceURI())) {
|
||||||
|
throw new IllegalStateException("Expected root element 'root', got "
|
||||||
|
+ new QName(e.getNamespaceURI(), e.getLocalName()));
|
||||||
|
}
|
||||||
|
return count(pNode);
|
||||||
|
}
|
||||||
|
private int count(Node pNode) {
|
||||||
|
if (INT_TAG.equals(pNode.getLocalName()) && INT_URI.equals(pNode.getNamespaceURI())) {
|
||||||
|
StringBuffer sb = new StringBuffer();
|
||||||
|
for (Node child = pNode.getFirstChild(); child != null; child = child.getNextSibling()) {
|
||||||
|
if (child.getNodeType() == Node.TEXT_NODE || child.getNodeType() == Node.CDATA_SECTION_NODE) {
|
||||||
|
sb.append(child.getNodeValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Integer.parseInt(sb.toString());
|
||||||
|
} else {
|
||||||
|
int result = 0;
|
||||||
|
for (Node child = pNode.getFirstChild(); child != null; child = child.getNextSibling()) {
|
||||||
|
if (child.getNodeType() == Node.ELEMENT_NODE) {
|
||||||
|
result += count(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Example of a Serializable instance.
|
||||||
|
*/
|
||||||
|
public static class CalendarWrapper implements Serializable {
|
||||||
|
private static final long serialVersionUID = 8153663910532549627L;
|
||||||
|
final Calendar cal;
|
||||||
|
CalendarWrapper(Calendar pCalendar) {
|
||||||
|
cal = pCalendar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the calendar value in milliseconds.
|
||||||
|
* @param pCal Calendar object
|
||||||
|
* @return <code>pCal.getTime().getTime()</code>.
|
||||||
|
*/
|
||||||
|
public long serializableParam(CalendarWrapper pCal) {
|
||||||
|
return pCal.cal.getTime().getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns midnight of the following day.
|
||||||
|
*/
|
||||||
|
public Calendar calendarParam(Calendar pCal) {
|
||||||
|
Calendar cal = (Calendar) pCal.clone();
|
||||||
|
cal.add(Calendar.DAY_OF_MONTH, 1);
|
||||||
|
cal.set(Calendar.HOUR_OF_DAY, 0);
|
||||||
|
cal.set(Calendar.MINUTE, 0);
|
||||||
|
cal.set(Calendar.SECOND, 0);
|
||||||
|
cal.set(Calendar.MILLISECOND, 0);
|
||||||
|
return cal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns midnight of the following day.
|
||||||
|
*/
|
||||||
|
public Date dateParam(Date pDate) {
|
||||||
|
Calendar cal = Calendar.getInstance();
|
||||||
|
cal.setTime(pDate);
|
||||||
|
return calendarParam(cal).getTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected XmlRpcHandlerMapping getHandlerMapping() throws IOException, XmlRpcException {
|
||||||
|
return getHandlerMapping("BaseTest.properties");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Test, whether we can invoke a method, passing a byte value.
|
||||||
|
* @throws Exception The test failed.
|
||||||
|
*/
|
||||||
|
public void testByteParam() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testByteParam(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testByteParam(ClientProvider pProvider) throws Exception {
|
||||||
|
final String methodName = "Remote.byteParam";
|
||||||
|
final Object[] params = new Object[]{new Byte((byte) 3)};
|
||||||
|
XmlRpcClient client = pProvider.getClient();
|
||||||
|
Object result = client.execute(getExConfig(pProvider), methodName, params);
|
||||||
|
assertEquals(new Integer(6), result);
|
||||||
|
boolean ok = false;
|
||||||
|
try {
|
||||||
|
client.execute(getConfig(pProvider), methodName, params);
|
||||||
|
} catch (XmlRpcExtensionException e) {
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
assertTrue(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Test, whether we can invoke a method, returning a byte.
|
||||||
|
* @throws Exception The test failed.
|
||||||
|
*/
|
||||||
|
public void testByteResult() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testByteResult(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testByteResult(ClientProvider pProvider) throws Exception {
|
||||||
|
final String methodName = "Remote.byteResult";
|
||||||
|
final Object[] params = new Object[]{new Byte((byte) 3)};
|
||||||
|
final XmlRpcClient client = pProvider.getClient();
|
||||||
|
Object result = client.execute(getExConfig(pProvider), methodName, params);
|
||||||
|
assertEquals(new Byte((byte) 6), result);
|
||||||
|
boolean ok = false;
|
||||||
|
try {
|
||||||
|
client.execute(getConfig(pProvider), methodName, params);
|
||||||
|
} catch (XmlRpcExtensionException e) {
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
assertTrue(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Test, whether we can invoke a method, passing a short value.
|
||||||
|
* @throws Exception The test failed.
|
||||||
|
*/
|
||||||
|
public void testShortParam() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testShortParam(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testShortParam(ClientProvider pProvider) throws Exception {
|
||||||
|
final String methodName = "Remote.shortParam";
|
||||||
|
final Object[] params = new Object[]{new Short((short) 4)};
|
||||||
|
final XmlRpcClient client = pProvider.getClient();
|
||||||
|
Object result = client.execute(getExConfig(pProvider), methodName, params);
|
||||||
|
assertEquals(new Integer(8), result);
|
||||||
|
boolean ok = false;
|
||||||
|
try {
|
||||||
|
client.execute(getConfig(pProvider), methodName, params);
|
||||||
|
} catch (XmlRpcExtensionException e) {
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
assertTrue(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Test, whether we can invoke a method, returning a short value.
|
||||||
|
* @throws Exception The test failed.
|
||||||
|
*/
|
||||||
|
public void testShortResult() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testShortResult(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testShortResult(ClientProvider pProvider) throws Exception {
|
||||||
|
final String methodName = "Remote.shortResult";
|
||||||
|
final Object[] params = new Object[]{new Short((short) 4)};
|
||||||
|
final XmlRpcClient client = pProvider.getClient();
|
||||||
|
Object result = client.execute(getExConfig(pProvider), methodName, params);
|
||||||
|
assertEquals(new Short((short) 8), result);
|
||||||
|
boolean ok = false;
|
||||||
|
try {
|
||||||
|
client.execute(getConfig(pProvider), methodName, params);
|
||||||
|
} catch (XmlRpcExtensionException e) {
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
assertTrue(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Test, whether we can invoke a method, passing an
|
||||||
|
* integer value.
|
||||||
|
* @throws Exception The test failed.
|
||||||
|
*/
|
||||||
|
public void testIntParam() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testIntParam(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testIntParam(ClientProvider pProvider) throws Exception {
|
||||||
|
final String methodName = "Remote.intParam";
|
||||||
|
final Object[] params = new Object[]{new Integer(5)};
|
||||||
|
final XmlRpcClient client = pProvider.getClient();
|
||||||
|
Object result = client.execute(getConfig(pProvider), methodName, params);
|
||||||
|
assertEquals(new Integer(10), result);
|
||||||
|
result = client.execute(getExConfig(pProvider), methodName, params);
|
||||||
|
assertEquals(new Integer(10), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Test, whether we can invoke a method, passing a long value.
|
||||||
|
* @throws Exception The test failed.
|
||||||
|
*/
|
||||||
|
public void testLongParam() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testLongParam(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testLongParam(ClientProvider pProvider) throws Exception {
|
||||||
|
final String methodName = "Remote.longParam";
|
||||||
|
final Object[] params = new Object[]{new Long(6L)};
|
||||||
|
final XmlRpcClient client = pProvider.getClient();
|
||||||
|
Object result = client.execute(getExConfig(pProvider), methodName, params);
|
||||||
|
assertEquals(new Integer(12), result);
|
||||||
|
boolean ok = false;
|
||||||
|
try {
|
||||||
|
client.execute(getConfig(pProvider), methodName, params);
|
||||||
|
} catch (XmlRpcExtensionException e) {
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
assertTrue(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Test, whether we can invoke a method, returning a long value.
|
||||||
|
* @throws Exception The test failed.
|
||||||
|
*/
|
||||||
|
public void testLongResult() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testLongResult(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testLongResult(ClientProvider pProvider) throws Exception {
|
||||||
|
final String methodName = "Remote.longResult";
|
||||||
|
final Object[] params = new Object[]{new Long(6L)};
|
||||||
|
final XmlRpcClient client = pProvider.getClient();
|
||||||
|
Object result = client.execute(getExConfig(pProvider), methodName, params);
|
||||||
|
assertEquals(new Long(12L), result);
|
||||||
|
boolean ok = false;
|
||||||
|
try {
|
||||||
|
client.execute(getConfig(pProvider), methodName, params);
|
||||||
|
} catch (XmlRpcExtensionException e) {
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
assertTrue(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Test, whether we can invoke a method, passing a
|
||||||
|
* string value.
|
||||||
|
* @throws Exception The test failed.
|
||||||
|
*/
|
||||||
|
public void testStringParam() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testStringParam(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testStringParam(ClientProvider pProvider) throws Exception {
|
||||||
|
final String methodName = "Remote.stringParam";
|
||||||
|
final Object[] params = new Object[]{"abc"};
|
||||||
|
final XmlRpcClient client = pProvider.getClient();
|
||||||
|
Object result = client.execute(getConfig(pProvider), methodName, params);
|
||||||
|
assertEquals("abcabc", result);
|
||||||
|
result = client.execute(getExConfig(pProvider), methodName, params);
|
||||||
|
assertEquals("abcabc", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Test, whether we can invoke a method, passing a
|
||||||
|
* string value or null.
|
||||||
|
* @throws Exception The test failed.
|
||||||
|
*/
|
||||||
|
public void testNullableStringParam() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testNullableStringParam(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testNullableStringParam(ClientProvider pProvider) throws Exception {
|
||||||
|
final String methodName = "Remote.nullableStringParam";
|
||||||
|
final Object[] params = new Object[]{"abc"};
|
||||||
|
final XmlRpcClient client = pProvider.getClient();
|
||||||
|
Object result = client.execute(getConfig(pProvider), methodName, params);
|
||||||
|
assertEquals("abcabc", result);
|
||||||
|
result = client.execute(getExConfig(pProvider), methodName, params);
|
||||||
|
assertEquals("abcabc", result);
|
||||||
|
final Object[] nullParams = new Object[]{null};
|
||||||
|
result = client.execute(getExConfig(pProvider), methodName, nullParams);
|
||||||
|
assertEquals("", result);
|
||||||
|
boolean ok = false;
|
||||||
|
try {
|
||||||
|
client.execute(getConfig(pProvider), methodName, nullParams);
|
||||||
|
} catch (XmlRpcExtensionException e) {
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
assertTrue(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Test, whether we can invoke a method, returning a
|
||||||
|
* string value or null.
|
||||||
|
* @throws Exception The test failed.
|
||||||
|
*/
|
||||||
|
public void testNullableStringResult() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testNullableStringResult(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testNullableStringResult(ClientProvider pProvider) throws Exception {
|
||||||
|
final String methodName = "Remote.nullableStringResult";
|
||||||
|
final Object[] params = new Object[]{"abc"};
|
||||||
|
final XmlRpcClient client = pProvider.getClient();
|
||||||
|
Object result = client.execute(getConfig(pProvider), methodName, params);
|
||||||
|
assertEquals("abcabc", result);
|
||||||
|
result = client.execute(getExConfig(pProvider), methodName, params);
|
||||||
|
assertEquals("abcabc", result);
|
||||||
|
final Object[] nullParams = new Object[]{null};
|
||||||
|
result = client.execute(getExConfig(pProvider), methodName, nullParams);
|
||||||
|
assertEquals(null, result);
|
||||||
|
boolean ok = false;
|
||||||
|
try {
|
||||||
|
client.execute(getConfig(pProvider), methodName, nullParams);
|
||||||
|
} catch (XmlRpcExtensionException e) {
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
assertTrue(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Test, whether we can invoke a method, passing a float value.
|
||||||
|
* @throws Exception The test failed.
|
||||||
|
*/
|
||||||
|
public void testFloatParam() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testFloatParam(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testFloatParam(ClientProvider pProvider) throws Exception {
|
||||||
|
final String methodName = "Remote.floatParam";
|
||||||
|
final Object[] params = new Object[]{new Float(0.4)};
|
||||||
|
final XmlRpcClient client = pProvider.getClient();
|
||||||
|
Object result = client.execute(getExConfig(pProvider), methodName, params);
|
||||||
|
assertEquals(8, Math.round(((Double) result).doubleValue()*10));
|
||||||
|
boolean ok = false;
|
||||||
|
try {
|
||||||
|
client.execute(getConfig(pProvider), methodName, params);
|
||||||
|
} catch (XmlRpcExtensionException e) {
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
assertTrue(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Test, whether we can invoke a method, returning a float value.
|
||||||
|
* @throws Exception The test failed.
|
||||||
|
*/
|
||||||
|
public void testFloatResult() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testFloatResult(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testFloatResult(ClientProvider pProvider) throws Exception {
|
||||||
|
final String methodName = "Remote.floatResult";
|
||||||
|
final Object[] params = new Object[]{new Float(0.4)};
|
||||||
|
final XmlRpcClient client = pProvider.getClient();
|
||||||
|
Object result = client.execute(getExConfig(pProvider), methodName, params);
|
||||||
|
assertEquals(new Float(0.8), result);
|
||||||
|
boolean ok = false;
|
||||||
|
try {
|
||||||
|
client.execute(getConfig(pProvider), methodName, params);
|
||||||
|
} catch (XmlRpcExtensionException e) {
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
assertTrue(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Test, whether we can invoke a method, passing a
|
||||||
|
* double value.
|
||||||
|
* @throws Exception The test failed.
|
||||||
|
*/
|
||||||
|
public void testDoubleParam() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testDoubleParam(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testDoubleParam(ClientProvider pProvider) throws Exception {
|
||||||
|
final String methodName = "Remote.doubleParam";
|
||||||
|
final Object[] params = new Object[]{new Double(0.6)};
|
||||||
|
final XmlRpcClient client = pProvider.getClient();
|
||||||
|
Object result = client.execute(getConfig(pProvider), methodName, params);
|
||||||
|
assertEquals(new Double(1.2), result);
|
||||||
|
result = client.execute(getExConfig(pProvider), methodName, params);
|
||||||
|
assertEquals(new Double(1.2), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Test, whether we can invoke a method, returning a
|
||||||
|
* double value.
|
||||||
|
* @throws Exception The test failed.
|
||||||
|
*/
|
||||||
|
public void testDoubleResult() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testDoubleResult(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testDoubleResult(ClientProvider pProvider) throws Exception {
|
||||||
|
final String methodName = "Remote.doubleResult";
|
||||||
|
final Object[] params = new Object[]{new Double(0.6)};
|
||||||
|
final XmlRpcClient client = pProvider.getClient();
|
||||||
|
Object result = client.execute(getConfig(pProvider), methodName, params);
|
||||||
|
assertEquals(new Double(1.2), result);
|
||||||
|
result = client.execute(getExConfig(pProvider), methodName, params);
|
||||||
|
assertEquals(new Double(1.2), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Test, whether we can invoke a method, passing a
|
||||||
|
* byte array.
|
||||||
|
* @throws Exception The test failed.
|
||||||
|
*/
|
||||||
|
public void testByteArrayParam() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testByteArrayParam(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testByteArrayParam(ClientProvider pProvider) throws Exception {
|
||||||
|
final byte[] bytes = new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||||
|
final String methodName = "Remote.byteArrayParam";
|
||||||
|
final Object[] params = new Object[]{bytes};
|
||||||
|
final XmlRpcClient client = pProvider.getClient();
|
||||||
|
Object result = client.execute(getConfig(pProvider), methodName, params);
|
||||||
|
assertEquals(new Integer(0+1+2+3+4+5+6+7+8+9), result);
|
||||||
|
result = client.execute(getExConfig(pProvider), methodName, params);
|
||||||
|
assertEquals(new Integer(0+1+2+3+4+5+6+7+8+9), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Test, whether we can invoke a method, returning a
|
||||||
|
* byte array.
|
||||||
|
* @throws Exception The test failed.
|
||||||
|
*/
|
||||||
|
public void testByteArrayResult() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testByteArrayResult(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testByteArrayResult(ClientProvider pProvider) throws Exception {
|
||||||
|
final byte[] bytes = new byte[]{0, 1, 2, 3, 4, 5, 6, 7};
|
||||||
|
final String methodName = "Remote.byteArrayResult";
|
||||||
|
final Object[] params = new Object[]{new Integer(8)};
|
||||||
|
final XmlRpcClient client = pProvider.getClient();
|
||||||
|
Object result = client.execute(getConfig(pProvider), methodName, params);
|
||||||
|
assertTrue(Arrays.equals(bytes, (byte[]) result));
|
||||||
|
result = client.execute(getExConfig(pProvider), methodName, params);
|
||||||
|
assertTrue(Arrays.equals(bytes, (byte[]) result));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Test, whether we can invoke a method, passing an
|
||||||
|
* object array.
|
||||||
|
* @throws Exception The test failed.
|
||||||
|
*/
|
||||||
|
public void testObjectArrayParam() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testObjectArrayParam(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testObjectArrayParam(ClientProvider pProvider) throws Exception {
|
||||||
|
final Object[] objects = new Object[]{new Byte((byte) 1), new Short((short) 2),
|
||||||
|
new Integer(3), new Long(4), "5"};
|
||||||
|
final String methodName = "Remote.objectArrayParam";
|
||||||
|
final Object[] params = new Object[]{objects};
|
||||||
|
final XmlRpcClient client = pProvider.getClient();
|
||||||
|
boolean ok = false;
|
||||||
|
try {
|
||||||
|
client.execute(getConfig(pProvider), methodName, params);
|
||||||
|
} catch (XmlRpcExtensionException e) {
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
assertTrue(ok);
|
||||||
|
Object result = client.execute(getExConfig(pProvider), methodName, params);
|
||||||
|
assertEquals(new Integer(15), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Test, whether we can invoke a method, returning an
|
||||||
|
* object array.
|
||||||
|
* @throws Exception The test failed.
|
||||||
|
*/
|
||||||
|
public void testObjectArrayResult() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testObjectArrayResult(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testObjectArrayResult(ClientProvider pProvider) throws Exception {
|
||||||
|
final Object[] objects = new Object[]{new Integer(0), new Integer(1),
|
||||||
|
new Integer(2), new Integer(3)};
|
||||||
|
final String methodName = "Remote.objectArrayResult";
|
||||||
|
final Object[] params = new Object[]{new Integer(4)};
|
||||||
|
final XmlRpcClient client = pProvider.getClient();
|
||||||
|
Object result = client.execute(getConfig(pProvider), methodName, params);
|
||||||
|
assertTrue(Arrays.equals(objects, (Object[]) result));
|
||||||
|
result = client.execute(getExConfig(pProvider), methodName, params);
|
||||||
|
assertTrue(Arrays.equals(objects, (Object[]) result));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Test, whether we can invoke a method, passing a map.
|
||||||
|
* @throws Exception The test failed.
|
||||||
|
*/
|
||||||
|
public void testMapParam() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testMapParam(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testMapParam(ClientProvider pProvider) throws Exception {
|
||||||
|
final Map map = new HashMap();
|
||||||
|
map.put("2", new Integer(3));
|
||||||
|
map.put("3", new Integer(5));
|
||||||
|
final String methodName = "Remote.mapParam";
|
||||||
|
final Object[] params = new Object[]{map};
|
||||||
|
final XmlRpcClient client = pProvider.getClient();
|
||||||
|
Object result = client.execute(getConfig(pProvider), methodName, params);
|
||||||
|
assertEquals(new Integer(21), result);
|
||||||
|
result = client.execute(getExConfig(pProvider), methodName, params);
|
||||||
|
assertEquals(new Integer(21), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkMap(Map pResult) {
|
||||||
|
assertEquals(4, pResult.size());
|
||||||
|
assertEquals(new Integer(0), pResult.get("0"));
|
||||||
|
assertEquals(new Integer(1), pResult.get("1"));
|
||||||
|
assertEquals(new Integer(2), pResult.get("2"));
|
||||||
|
assertEquals(new Integer(3), pResult.get("3"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Test, whether we can invoke a method, returning a map.
|
||||||
|
* @throws Exception The test failed.
|
||||||
|
*/
|
||||||
|
public void testMapResult() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testMapResult(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testMapResult(ClientProvider pProvider) throws Exception {
|
||||||
|
final String methodName = "Remote.mapResult";
|
||||||
|
final Object[] params = new Object[]{new Integer(4)};
|
||||||
|
final XmlRpcClient client = pProvider.getClient();
|
||||||
|
Object result = client.execute(getConfig(pProvider), methodName, params);
|
||||||
|
checkMap((Map) result);
|
||||||
|
result = client.execute(getExConfig(pProvider), methodName, params);
|
||||||
|
checkMap((Map) result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Test, whether we can invoke a method, passing a DOM
|
||||||
|
* node as parameter.
|
||||||
|
* @throws Exception The test failed.
|
||||||
|
*/
|
||||||
|
public void testNodeParam() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testNodeParam(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String ROOT_TAG = "root";
|
||||||
|
private static final String INT_TAG = "int";
|
||||||
|
private static final String INT_URI = "http://ws.apache.org/xmlrpc/namespaces/testNodeParam";
|
||||||
|
|
||||||
|
private void testNodeParam(ClientProvider pProvider) throws Exception {
|
||||||
|
final String xml =
|
||||||
|
"<" + ROOT_TAG + " xmlns='" + INT_URI +"'>" +
|
||||||
|
" <" + INT_TAG + ">1</" + INT_TAG + ">" +
|
||||||
|
" <" + INT_TAG + ">2</" + INT_TAG + ">" +
|
||||||
|
" <" + INT_TAG + ">3</" + INT_TAG + ">" +
|
||||||
|
" <" + INT_TAG + ">4</" + INT_TAG + ">" +
|
||||||
|
" <" + INT_TAG + ">5</" + INT_TAG + ">" +
|
||||||
|
"</" + ROOT_TAG + ">";
|
||||||
|
final String methodName = "Remote.nodeParam";
|
||||||
|
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||||
|
dbf.setValidating(false);
|
||||||
|
dbf.setNamespaceAware(true);
|
||||||
|
Document doc = dbf.newDocumentBuilder().parse(new InputSource(new StringReader(xml)));
|
||||||
|
final Object[] params = new Object[]{doc};
|
||||||
|
final XmlRpcClient client = pProvider.getClient();
|
||||||
|
Object result = client.execute(getExConfig(pProvider), methodName, params);
|
||||||
|
assertEquals(1 + 2 + 3 + 4 + 5, result);
|
||||||
|
boolean ok = false;
|
||||||
|
try {
|
||||||
|
client.execute(getConfig(pProvider), methodName, params);
|
||||||
|
} catch (XmlRpcExtensionException e) {
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
assertTrue(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Test, whether we can invoke a method, passing an instance of
|
||||||
|
* {@link Serializable} as a parameter.
|
||||||
|
* @throws Exception The test failed.
|
||||||
|
*/
|
||||||
|
public void testSerializableParam() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testSerializableParam(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testSerializableParam(ClientProvider pProvider) throws Exception {
|
||||||
|
final String methodName = "Remote.serializableParam";
|
||||||
|
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
|
||||||
|
cal.set(2005, 5, 23, 8, 4, 0);
|
||||||
|
cal.set(Calendar.MILLISECOND, 5);
|
||||||
|
final Object[] params = new Object[]{new Remote.CalendarWrapper(cal)};
|
||||||
|
final XmlRpcClient client = pProvider.getClient();
|
||||||
|
Object result = client.execute(getExConfig(pProvider), methodName, params);
|
||||||
|
assertEquals(new Long(cal.getTime().getTime()), result);
|
||||||
|
boolean ok = false;
|
||||||
|
try {
|
||||||
|
client.execute(getConfig(pProvider), methodName, params);
|
||||||
|
} catch (XmlRpcExtensionException e) {
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
assertTrue(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Tests, whether we can invoke a method, passing an instance of
|
||||||
|
* {@link Calendar} as a parameter.
|
||||||
|
* @throws Exception The test failed.
|
||||||
|
*/
|
||||||
|
public void testCalendarParam() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testCalendarParam(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testCalendarParam(ClientProvider pProvider) throws Exception {
|
||||||
|
final String methodName = "Remote.calendarParam";
|
||||||
|
Calendar cal1 = newCalendarParam();
|
||||||
|
Calendar cal2 = newCalendarResult();
|
||||||
|
final Object[] params = new Object[]{cal1};
|
||||||
|
final XmlRpcClient client = pProvider.getClient();
|
||||||
|
Object result = client.execute(getExConfig(pProvider), methodName, params);
|
||||||
|
assertEquals(cal2.getTime(), ((Calendar) result).getTime());
|
||||||
|
boolean ok = false;
|
||||||
|
try {
|
||||||
|
client.execute(getConfig(pProvider), methodName, params);
|
||||||
|
} catch (XmlRpcExtensionException e) {
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
assertTrue(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Calendar newCalendarResult() {
|
||||||
|
Calendar cal2 = Calendar.getInstance(TimeZone.getDefault());
|
||||||
|
cal2.set(2005, 5, 24, 0, 0, 0);
|
||||||
|
cal2.set(Calendar.MILLISECOND, 0);
|
||||||
|
return cal2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Calendar newCalendarParam() {
|
||||||
|
Calendar cal1 = Calendar.getInstance(TimeZone.getDefault());
|
||||||
|
cal1.set(2005, 5, 23, 8, 4, 0);
|
||||||
|
cal1.set(Calendar.MILLISECOND, 5);
|
||||||
|
return cal1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Tests, whether we can invoke a method, passing an instance of
|
||||||
|
* {@link Date} as a parameter.
|
||||||
|
* @throws Exception The test failed.
|
||||||
|
*/
|
||||||
|
public void testDateParam() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testDateParam(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testDateParam(ClientProvider pProvider) throws Exception {
|
||||||
|
final String methodName = "Remote.dateParam";
|
||||||
|
Date date1 = newCalendarParam().getTime();
|
||||||
|
Calendar cal2 = newCalendarResult();
|
||||||
|
final Object[] params = new Object[]{date1};
|
||||||
|
final XmlRpcClient client = pProvider.getClient();
|
||||||
|
Object result = client.execute(getExConfig(pProvider), methodName, params);
|
||||||
|
assertEquals(cal2.getTime(), result);
|
||||||
|
result = client.execute(getConfig(pProvider), methodName, params);
|
||||||
|
assertEquals(cal2.getTime(), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests, whether a NullPointerException, thrown by the server, can be
|
||||||
|
* trapped by the client.
|
||||||
|
*/
|
||||||
|
public void testCatchNPE() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testCatchNPE(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testCatchNPE(ClientProvider pProvider) throws Exception {
|
||||||
|
final XmlRpcClient client = pProvider.getClient();
|
||||||
|
final String methodName = "Remote.throwNPE";
|
||||||
|
try {
|
||||||
|
client.execute(getExConfig(pProvider), methodName, (Object[]) null);
|
||||||
|
} catch (XmlRpcInvocationException e) {
|
||||||
|
if (!(e.getCause() instanceof NullPointerException)) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,143 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.xbib.netty.http.xmlrpc.client.test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.apache.xmlrpc.XmlRpcException;
|
||||||
|
import org.apache.xmlrpc.XmlRpcHandler;
|
||||||
|
import org.apache.xmlrpc.XmlRpcRequest;
|
||||||
|
import org.apache.xmlrpc.client.XmlRpcClient;
|
||||||
|
import org.apache.xmlrpc.server.XmlRpcHandlerMapping;
|
||||||
|
import org.apache.xmlrpc.server.XmlRpcNoSuchHandlerException;
|
||||||
|
import org.apache.xmlrpc.webserver.XmlRpcServlet;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test case for reading the clients IP address.
|
||||||
|
*/
|
||||||
|
public class ClientIpTest extends XmlRpcTestCase {
|
||||||
|
/**
|
||||||
|
* An object, which provides additional information
|
||||||
|
* about the client to the user.
|
||||||
|
*/
|
||||||
|
public static class ClientInfo {
|
||||||
|
private final String ipAddress;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance.
|
||||||
|
*/
|
||||||
|
public ClientInfo(String pIpAddress) {
|
||||||
|
ipAddress = pIpAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the clients IP address.
|
||||||
|
*/
|
||||||
|
public String getIpAddress() {
|
||||||
|
return ipAddress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An extension of the {@link XmlRpcServlet}, which
|
||||||
|
* ensures the availability of a {@link ClientInfo}
|
||||||
|
* object.
|
||||||
|
*/
|
||||||
|
public static class ClientInfoServlet extends XmlRpcServlet {
|
||||||
|
private static final long serialVersionUID = 8210342625908021538L;
|
||||||
|
private static ThreadLocal clientInfo = new ThreadLocal();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current threads. client info object.
|
||||||
|
*/
|
||||||
|
public static ClientInfo getClientInfo() {
|
||||||
|
return (ClientInfo) clientInfo.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doPost(HttpServletRequest pRequest,
|
||||||
|
HttpServletResponse pResponse) throws IOException,
|
||||||
|
ServletException {
|
||||||
|
clientInfo.set(new ClientInfo(pRequest.getRemoteAddr()));
|
||||||
|
super.doPost(pRequest, pResponse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ClientIpTestProvider extends ServletWebServerProvider {
|
||||||
|
ClientIpTestProvider(XmlRpcHandlerMapping pMapping, boolean pContentLength)
|
||||||
|
throws ServletException, IOException {
|
||||||
|
super(pMapping, pContentLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected XmlRpcServlet newXmlRpcServlet() {
|
||||||
|
return new ClientInfoServlet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ClientProvider[] initProviders(XmlRpcHandlerMapping pMapping)
|
||||||
|
throws ServletException, IOException {
|
||||||
|
return new ClientProvider[]{
|
||||||
|
new ClientIpTestProvider(pMapping, false),
|
||||||
|
new ClientIpTestProvider(pMapping, true)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected XmlRpcHandlerMapping getHandlerMapping() throws IOException,
|
||||||
|
XmlRpcException {
|
||||||
|
final XmlRpcHandler handler = new XmlRpcHandler(){
|
||||||
|
public Object execute(XmlRpcRequest pRequest) throws XmlRpcException {
|
||||||
|
final ClientInfo clientInfo = ClientInfoServlet.getClientInfo();
|
||||||
|
if (clientInfo == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
final String ip = clientInfo.getIpAddress();
|
||||||
|
if (ip == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return new XmlRpcHandlerMapping(){
|
||||||
|
public XmlRpcHandler getHandler(String pHandlerName)
|
||||||
|
throws XmlRpcNoSuchHandlerException, XmlRpcException {
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testClientIpAddress(ClientProvider pProvider) throws Exception {
|
||||||
|
final XmlRpcClient client = pProvider.getClient();
|
||||||
|
client.setConfig(getConfig(pProvider));
|
||||||
|
final String ip = (String) client.execute("getIpAddress", new Object[]{});
|
||||||
|
assertEquals("127.0.0.1", ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Test, whether we can invoke a method, returning a byte.
|
||||||
|
* @throws Exception The test failed.
|
||||||
|
*/
|
||||||
|
public void testClientIpAddress() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testClientIpAddress(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client.test;
|
||||||
|
|
||||||
|
import org.xbib.netty.http.xmlrpc.client.XmlRpcClient;
|
||||||
|
import org.xbib.netty.http.xmlrpc.client.XmlRpcClientConfigImpl;
|
||||||
|
import org.xbib.netty.http.xmlrpc.server.XmlRpcServer;
|
||||||
|
|
||||||
|
/** This interface allows to perform a unit test with various
|
||||||
|
* transports. Basically, the implementation creates the client,
|
||||||
|
* including the transport, and the server, if required.
|
||||||
|
*/
|
||||||
|
public interface ClientProvider {
|
||||||
|
/** Returns the clients default configuration.
|
||||||
|
* @return The clients configuration.
|
||||||
|
* @throws Exception Creating the configuration failed.
|
||||||
|
*/
|
||||||
|
XmlRpcClientConfigImpl getConfig() throws Exception;
|
||||||
|
|
||||||
|
/** Returns a new client instance.
|
||||||
|
* @return A client being used for performing the test.
|
||||||
|
*/
|
||||||
|
XmlRpcClient getClient();
|
||||||
|
|
||||||
|
/** Returns the providers server instance.
|
||||||
|
* @return A server instance, which is being used for performing the test.
|
||||||
|
*/
|
||||||
|
XmlRpcServer getServer();
|
||||||
|
|
||||||
|
/** Performs a shutdown of the server.
|
||||||
|
*/
|
||||||
|
void shutdown();
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client.test;
|
||||||
|
|
||||||
|
|
||||||
|
import org.xbib.netty.http.xmlrpc.client.XmlRpcClient;
|
||||||
|
import org.xbib.netty.http.xmlrpc.client.XmlRpcClientConfigImpl;
|
||||||
|
import org.xbib.netty.http.xmlrpc.client.XmlRpcTransportFactory;
|
||||||
|
import org.xbib.netty.http.xmlrpc.server.XmlRpcHandlerMapping;
|
||||||
|
import org.xbib.netty.http.xmlrpc.server.XmlRpcServer;
|
||||||
|
|
||||||
|
/** Abstract base implementation of {@link ClientProvider}.
|
||||||
|
*/
|
||||||
|
public abstract class ClientProviderImpl implements ClientProvider {
|
||||||
|
protected final XmlRpcHandlerMapping mapping;
|
||||||
|
|
||||||
|
protected abstract XmlRpcTransportFactory getTransportFactory(XmlRpcClient pClient);
|
||||||
|
|
||||||
|
/** Creates a new instance.
|
||||||
|
* @param pMapping The test servers handler mapping.
|
||||||
|
*/
|
||||||
|
protected ClientProviderImpl(XmlRpcHandlerMapping pMapping) {
|
||||||
|
mapping = pMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected XmlRpcServer getXmlRpcServer() throws Exception {
|
||||||
|
XmlRpcServer server = new XmlRpcServer();
|
||||||
|
server.setHandlerMapping(mapping);
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
public XmlRpcClientConfigImpl getConfig() throws Exception {
|
||||||
|
XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public XmlRpcClient getClient() {
|
||||||
|
XmlRpcClient client = new XmlRpcClient();
|
||||||
|
client.setTransportFactory(getTransportFactory(client));
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client.test;
|
||||||
|
|
||||||
|
import org.xbib.netty.http.xmlrpc.client.XmlRpcClient;
|
||||||
|
import org.xbib.netty.http.xmlrpc.client.XmlRpcCommonsTransportFactory;
|
||||||
|
import org.xbib.netty.http.xmlrpc.client.XmlRpcTransportFactory;
|
||||||
|
import org.xbib.netty.http.xmlrpc.server.XmlRpcHandlerMapping;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provider for testing the
|
||||||
|
* {@link XmlRpcCommonsTransport}.
|
||||||
|
*/
|
||||||
|
public class CommonsProvider extends WebServerProvider {
|
||||||
|
/** Creates a new instance.
|
||||||
|
* @param pMapping The test servers handler mapping.
|
||||||
|
*/
|
||||||
|
public CommonsProvider(XmlRpcHandlerMapping pMapping) {
|
||||||
|
super(pMapping, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected XmlRpcTransportFactory getTransportFactory(XmlRpcClient pClient) {
|
||||||
|
return new XmlRpcCommonsTransportFactory(pClient);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.xbib.netty.http.xmlrpc.client.test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.text.Format;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.apache.ws.commons.util.NamespaceContextImpl;
|
||||||
|
import org.apache.xmlrpc.XmlRpcException;
|
||||||
|
import org.apache.xmlrpc.XmlRpcRequest;
|
||||||
|
import org.apache.xmlrpc.client.XmlRpcClient;
|
||||||
|
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
|
||||||
|
import org.apache.xmlrpc.client.XmlRpcClientRequestImpl;
|
||||||
|
import org.apache.xmlrpc.common.TypeFactory;
|
||||||
|
import org.apache.xmlrpc.common.TypeFactoryImpl;
|
||||||
|
import org.apache.xmlrpc.common.XmlRpcController;
|
||||||
|
import org.apache.xmlrpc.common.XmlRpcStreamConfig;
|
||||||
|
import org.apache.xmlrpc.parser.DateParser;
|
||||||
|
import org.apache.xmlrpc.parser.TypeParser;
|
||||||
|
import org.apache.xmlrpc.serializer.DateSerializer;
|
||||||
|
import org.apache.xmlrpc.serializer.TypeSerializer;
|
||||||
|
import org.apache.xmlrpc.server.PropertyHandlerMapping;
|
||||||
|
import org.apache.xmlrpc.server.XmlRpcHandlerMapping;
|
||||||
|
import org.apache.xmlrpc.server.XmlRpcServer;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test suite for working with custom types.
|
||||||
|
*/
|
||||||
|
public class CustomTypesTest extends XmlRpcTestCase {
|
||||||
|
/**
|
||||||
|
* Sample date converter
|
||||||
|
*/
|
||||||
|
public static class DateConverter {
|
||||||
|
/**
|
||||||
|
* Adds one day to the given date.
|
||||||
|
*/
|
||||||
|
public Date tomorrow(Date pDate) {
|
||||||
|
Calendar cal = Calendar.getInstance();
|
||||||
|
cal.setTime(pDate);
|
||||||
|
cal.add(Calendar.DAY_OF_MONTH, 1);
|
||||||
|
return cal.getTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected XmlRpcHandlerMapping getHandlerMapping() throws IOException, XmlRpcException {
|
||||||
|
PropertyHandlerMapping mapping = new PropertyHandlerMapping();
|
||||||
|
mapping.addHandler("DateConverter", DateConverter.class);
|
||||||
|
return mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Tests using a custom date format.
|
||||||
|
*/
|
||||||
|
public void testCustomDateFormat() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testCustomDateFormat(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private TypeFactory getCustomDateTypeFactory(XmlRpcController pController, final Format pFormat) {
|
||||||
|
return new TypeFactoryImpl(pController){
|
||||||
|
private TypeSerializer dateSerializer = new DateSerializer(pFormat);
|
||||||
|
|
||||||
|
public TypeParser getParser(XmlRpcStreamConfig pConfig, NamespaceContextImpl pContext, String pURI, String pLocalName) {
|
||||||
|
if (DateSerializer.DATE_TAG.equals(pLocalName)) {
|
||||||
|
return new DateParser(pFormat);
|
||||||
|
} else {
|
||||||
|
return super.getParser(pConfig, pContext, pURI, pLocalName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TypeSerializer getSerializer(XmlRpcStreamConfig pConfig, Object pObject) throws SAXException {
|
||||||
|
if (pObject instanceof Date) {
|
||||||
|
return dateSerializer;
|
||||||
|
} else {
|
||||||
|
return super.getSerializer(pConfig, pObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testCustomDateFormat(ClientProvider pProvider) throws Exception {
|
||||||
|
final DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
|
||||||
|
XmlRpcClient client = pProvider.getClient();
|
||||||
|
XmlRpcClientConfigImpl config = getConfig(pProvider);
|
||||||
|
client.setConfig(config);
|
||||||
|
TypeFactory typeFactory = getCustomDateTypeFactory(client, format);
|
||||||
|
client.setTypeFactory(typeFactory);
|
||||||
|
Calendar cal1 = Calendar.getInstance();
|
||||||
|
XmlRpcRequest request = new XmlRpcClientRequestImpl(config, "DateConverter.tomorrow", new Object[]{cal1.getTime()});
|
||||||
|
final String got = XmlRpcTestCase.writeRequest(client, request);
|
||||||
|
final String expect = "<?xml version=\"1.0\" encoding=\"US-ASCII\"?>"
|
||||||
|
+ "<methodCall><methodName>DateConverter.tomorrow</methodName>"
|
||||||
|
+ "<params><param><value><dateTime.iso8601>" + format.format(cal1.getTime())
|
||||||
|
+ "</dateTime.iso8601></value></param></params></methodCall>";
|
||||||
|
assertEquals(expect, got);
|
||||||
|
|
||||||
|
XmlRpcServer server = pProvider.getServer();
|
||||||
|
server.setTypeFactory(getCustomDateTypeFactory(server, format));
|
||||||
|
Date date = (Date) client.execute(request);
|
||||||
|
Calendar cal2 = Calendar.getInstance();
|
||||||
|
cal2.setTime(date);
|
||||||
|
cal1.add(Calendar.DAY_OF_MONTH, 1);
|
||||||
|
assertEquals(cal1, cal2);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,132 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.xbib.netty.http.xmlrpc.client.test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.apache.xmlrpc.XmlRpcException;
|
||||||
|
import org.apache.xmlrpc.client.XmlRpcClient;
|
||||||
|
import org.apache.xmlrpc.client.util.ClientFactory;
|
||||||
|
import org.apache.xmlrpc.server.XmlRpcHandlerMapping;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
|
||||||
|
/** Test case for the {@link ClientFactory}.
|
||||||
|
*/
|
||||||
|
public class DynamicProxyTest extends XmlRpcTestCase {
|
||||||
|
/** An interface, which is being implemented by the
|
||||||
|
* server.
|
||||||
|
*/
|
||||||
|
public interface Adder {
|
||||||
|
/** Returns the sum of the given integers.
|
||||||
|
*/
|
||||||
|
public int add(int pNum1, int pNum2);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws a SAXException.
|
||||||
|
*/
|
||||||
|
public Object parse(String pMessage) throws SAXException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A void method; these are disabled without support for
|
||||||
|
* extensions, but enabled when extensions are on.
|
||||||
|
*/
|
||||||
|
public void ping();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Implementation of {@link Adder}, which is used by
|
||||||
|
* the server.
|
||||||
|
*/
|
||||||
|
public static class AdderImpl implements Adder {
|
||||||
|
public int add(int pNum1, int pNum2) {
|
||||||
|
return pNum1 + pNum2;
|
||||||
|
}
|
||||||
|
public Object parse(String pMessage) throws SAXException {
|
||||||
|
throw new SAXException("Failed to parse message: " + pMessage);
|
||||||
|
}
|
||||||
|
public void ping() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected XmlRpcHandlerMapping getHandlerMapping() throws IOException, XmlRpcException {
|
||||||
|
return getHandlerMapping("DynamicProxyTest.properties");
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClientFactory getClientFactory(ClientProvider pProvider) throws Exception {
|
||||||
|
XmlRpcClient client = pProvider.getClient();
|
||||||
|
client.setConfig(getConfig(pProvider));
|
||||||
|
return new ClientFactory(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClientFactory getExClientFactory(ClientProvider pProvider) throws Exception {
|
||||||
|
XmlRpcClient client = pProvider.getClient();
|
||||||
|
client.setConfig(getExConfig(pProvider));
|
||||||
|
return new ClientFactory(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Tests calling the {@link Adder#add(int,int)} method
|
||||||
|
* by using an object, which has been created by the
|
||||||
|
* {@link ClientFactory}.
|
||||||
|
*/
|
||||||
|
public void testAdderCall() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testAdderCall(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testAdderCall(ClientProvider pProvider) throws Exception {
|
||||||
|
ClientFactory factory = getClientFactory(pProvider);
|
||||||
|
Adder adder = (Adder) factory.newInstance(Adder.class);
|
||||||
|
assertEquals(6, adder.add(2, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Tests trapping a SAXException.
|
||||||
|
*/
|
||||||
|
public void testParseCall() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testParseCall(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testParseCall(ClientProvider pProvider) throws Exception {
|
||||||
|
ClientFactory factory = getExClientFactory(pProvider);
|
||||||
|
Adder adder = (Adder) factory.newInstance(Adder.class);
|
||||||
|
try {
|
||||||
|
adder.parse("foo");
|
||||||
|
fail("Expected SAXException");
|
||||||
|
} catch (SAXException e) {
|
||||||
|
// Ok
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests invoking a "void" method.
|
||||||
|
*/
|
||||||
|
public void testVoidMethod() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testVoidMethod(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testVoidMethod(ClientProvider pProvider) throws Exception {
|
||||||
|
ClientFactory factory = getExClientFactory(pProvider);
|
||||||
|
Adder adder = (Adder) factory.newInstance(Adder.class);
|
||||||
|
adder.ping();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,402 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.xbib.netty.http.xmlrpc.client.test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.lang.reflect.UndeclaredThrowableException;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
import org.apache.xmlrpc.XmlRpcException;
|
||||||
|
import org.apache.xmlrpc.client.TimingOutCallback;
|
||||||
|
import org.apache.xmlrpc.client.XmlRpcClient;
|
||||||
|
import org.apache.xmlrpc.client.XmlRpcHttpClientConfig;
|
||||||
|
import org.apache.xmlrpc.client.util.ClientFactory;
|
||||||
|
import org.apache.xmlrpc.common.XmlRpcStreamRequestConfig;
|
||||||
|
import org.apache.xmlrpc.parser.XmlRpcResponseParser;
|
||||||
|
import org.apache.xmlrpc.server.XmlRpcHandlerMapping;
|
||||||
|
import org.apache.xmlrpc.util.SAXParsers;
|
||||||
|
import org.xml.sax.InputSource;
|
||||||
|
import org.xml.sax.XMLReader;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test case for various jira issues.
|
||||||
|
*/
|
||||||
|
public class JiraTest extends XmlRpcTestCase {
|
||||||
|
/** Interface of the handler for {@link JiraTest#testXMLRPC89()}
|
||||||
|
*/
|
||||||
|
public interface XMLRPC89Handler {
|
||||||
|
/**
|
||||||
|
* Returns the reversed vector.
|
||||||
|
*/
|
||||||
|
Vector reverse(Vector pVector);
|
||||||
|
/**
|
||||||
|
* Returns the same hashtable, but doubles the
|
||||||
|
* values.
|
||||||
|
*/
|
||||||
|
Hashtable doubledValues(Hashtable pMap);
|
||||||
|
/**
|
||||||
|
* Returns the same properties, but doubles the
|
||||||
|
* values.
|
||||||
|
*/
|
||||||
|
Properties doubledPropertyValues(Properties pMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for {@link JiraTest#testXMLRPC89()}
|
||||||
|
*/
|
||||||
|
public static class XMLRPC89HandlerImpl implements XMLRPC89Handler {
|
||||||
|
public Vector reverse(Vector pVector) {
|
||||||
|
Vector result = new Vector(pVector.size());
|
||||||
|
result.addAll(pVector);
|
||||||
|
Collections.reverse(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
public Hashtable doubledValues(Hashtable pMap) {
|
||||||
|
final Hashtable result;
|
||||||
|
if (pMap instanceof Properties) {
|
||||||
|
result = new Properties();
|
||||||
|
} else {
|
||||||
|
result = new Hashtable();
|
||||||
|
}
|
||||||
|
result.putAll(pMap);
|
||||||
|
for (Iterator iter = result.entrySet().iterator(); iter.hasNext(); ) {
|
||||||
|
Map.Entry entry = (Map.Entry) iter.next();
|
||||||
|
Object value = entry.getValue();
|
||||||
|
final Integer i;
|
||||||
|
if (pMap instanceof Properties) {
|
||||||
|
i = Integer.valueOf((String) value);
|
||||||
|
} else {
|
||||||
|
i = (Integer) value;
|
||||||
|
}
|
||||||
|
Integer iDoubled = new Integer(i.intValue()*2);
|
||||||
|
if (pMap instanceof Properties) {
|
||||||
|
entry.setValue(iDoubled.toString());
|
||||||
|
} else {
|
||||||
|
entry.setValue(iDoubled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
public Properties doubledPropertyValues(Properties pProperties) {
|
||||||
|
return (Properties) doubledValues(pProperties);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected XmlRpcHandlerMapping getHandlerMapping() throws IOException,
|
||||||
|
XmlRpcException {
|
||||||
|
return getHandlerMapping("JiraTest.properties");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test case for <a href="http://issues.apache.org/jira/browse/XMLRPC-89">
|
||||||
|
* XMLRPC-89</a>
|
||||||
|
*/
|
||||||
|
public void testXMLRPC89() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testXMLRPC89Vector(providers[i]);
|
||||||
|
testXMLRPC89Hashtable(providers[i]);
|
||||||
|
testXMLRPC89Properties(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testXMLRPC89Vector(ClientProvider pProvider) throws Exception {
|
||||||
|
Vector values = new Vector();
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
values.add(new Integer(i));
|
||||||
|
}
|
||||||
|
Vector params = new Vector();
|
||||||
|
params.add(values);
|
||||||
|
XmlRpcClient client = pProvider.getClient();
|
||||||
|
client.setConfig(getConfig(pProvider));
|
||||||
|
Object res = client.execute(XMLRPC89Handler.class.getName() + ".reverse", params);
|
||||||
|
Object[] result = (Object[]) res;
|
||||||
|
assertNotNull(result);
|
||||||
|
assertEquals(3, result.length);
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
assertEquals(new Integer(2-i), result[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientFactory factory = new ClientFactory(client);
|
||||||
|
XMLRPC89Handler handler = (XMLRPC89Handler) factory.newInstance(XMLRPC89Handler.class);
|
||||||
|
Vector resultVector = handler.reverse(values);
|
||||||
|
assertNotNull(resultVector);
|
||||||
|
assertEquals(3, resultVector.size());
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
assertEquals(new Integer(2-i), resultVector.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyXMLRPC89Hashtable(Map pMap) {
|
||||||
|
assertNotNull(pMap);
|
||||||
|
assertEquals(3, pMap.size());
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
Integer j = (Integer) pMap.get(String.valueOf(i));
|
||||||
|
assertEquals(i*2, j.intValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testXMLRPC89Hashtable(ClientProvider pProvider) throws Exception {
|
||||||
|
Hashtable values = new Hashtable();
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
values.put(String.valueOf(i), new Integer(i));
|
||||||
|
}
|
||||||
|
XmlRpcClient client = pProvider.getClient();
|
||||||
|
client.setConfig(getConfig(pProvider));
|
||||||
|
Object res = client.execute(XMLRPC89Handler.class.getName() + ".doubledValues", new Object[]{values});
|
||||||
|
verifyXMLRPC89Hashtable((Map) res);
|
||||||
|
|
||||||
|
ClientFactory factory = new ClientFactory(client);
|
||||||
|
XMLRPC89Handler handler = (XMLRPC89Handler) factory.newInstance(XMLRPC89Handler.class);
|
||||||
|
Hashtable result = handler.doubledValues(values);
|
||||||
|
verifyXMLRPC89Hashtable(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyXMLRPC89Properties(Map pMap) {
|
||||||
|
assertNotNull(pMap);
|
||||||
|
assertEquals(3, pMap.size());
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
String j = (String) pMap.get(String.valueOf(i));
|
||||||
|
assertEquals(i*2, Integer.parseInt(j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testXMLRPC89Properties(ClientProvider pProvider) throws Exception {
|
||||||
|
Properties values = new Properties();
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
values.put(String.valueOf(i), String.valueOf(i));
|
||||||
|
}
|
||||||
|
XmlRpcClient client = pProvider.getClient();
|
||||||
|
client.setConfig(getConfig(pProvider));
|
||||||
|
Object res = client.execute(XMLRPC89Handler.class.getName() + ".doubledPropertyValues", new Object[]{values});
|
||||||
|
verifyXMLRPC89Properties((Map) res);
|
||||||
|
|
||||||
|
ClientFactory factory = new ClientFactory(client);
|
||||||
|
XMLRPC89Handler handler = (XMLRPC89Handler) factory.newInstance(XMLRPC89Handler.class);
|
||||||
|
Properties result = handler.doubledPropertyValues(values);
|
||||||
|
verifyXMLRPC89Properties(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Handler for XMLRPC-96
|
||||||
|
*/
|
||||||
|
public static class XMLRPC96Handler {
|
||||||
|
/** Returns the "Hello, world!" string.
|
||||||
|
*/
|
||||||
|
public String getHelloWorld() {
|
||||||
|
return "Hello, world!";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test case for <a href="http://issues.apache.org/jira/browse/XMLRPC-96">
|
||||||
|
* XMLRPC-96</a>
|
||||||
|
*/
|
||||||
|
public void testXMLRPC96() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testXMLRPC96(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testXMLRPC96(ClientProvider pProvider) throws Exception {
|
||||||
|
XmlRpcClient client = pProvider.getClient();
|
||||||
|
client.setConfig(getConfig(pProvider));
|
||||||
|
String s = (String) client.execute(XMLRPC96Handler.class.getName() + ".getHelloWorld", new Object[0]);
|
||||||
|
assertEquals("Hello, world!", s);
|
||||||
|
s = (String) client.execute(XMLRPC96Handler.class.getName() + ".getHelloWorld", (Object[]) null);
|
||||||
|
assertEquals("Hello, world!", s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test case for <a href="http://issues.apache.org/jira/browse/XMLRPC-112">
|
||||||
|
* XMLRPC-112</a>
|
||||||
|
*/
|
||||||
|
public void testXMLRPC112() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testXMLRPC112(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test case for <a href="http://issues.apache.org/jira/browse/XMLRPC-113">
|
||||||
|
* XMLRPC-113</a>
|
||||||
|
*/
|
||||||
|
public void testXMLRPC113() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testXMLRPC113(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void testXMLRPC112(ClientProvider pProvider) throws Exception {
|
||||||
|
XmlRpcClient client = pProvider.getClient();
|
||||||
|
client.setConfig(getConfig(pProvider));
|
||||||
|
TimingOutCallback toc = new TimingOutCallback(5000);
|
||||||
|
final String methodName = XMLRPC89Handler.class.getName() + ".reverse";
|
||||||
|
client.executeAsync(methodName, new Object[]{new Object[]{"1", "2", "3"}}, toc);
|
||||||
|
Object o;
|
||||||
|
try {
|
||||||
|
o = toc.waitForResponse();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Throwable t) {
|
||||||
|
throw new UndeclaredThrowableException(t);
|
||||||
|
}
|
||||||
|
checkXMLRPC112Result(o);
|
||||||
|
checkXMLRPC112Result(client.execute(methodName, new Object[]{new Object[]{"1", "2", "3"}}));
|
||||||
|
checkXMLRPC112Result(client.execute(methodName, new Object[]{new Object[]{"1", "2", "3"}}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkXMLRPC112Result(Object pObject) {
|
||||||
|
Object[] args = (Object[]) pObject;
|
||||||
|
assertEquals(3, args.length);
|
||||||
|
assertEquals("3", args[0]);
|
||||||
|
assertEquals("2", args[1]);
|
||||||
|
assertEquals("1", args[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler interface for {@link JiraTest#testXMLRPC113()}
|
||||||
|
*/
|
||||||
|
public interface XMLRPC113Handler {
|
||||||
|
/**
|
||||||
|
* Throws an {@link XmlRpcException} with the given error code.
|
||||||
|
*/
|
||||||
|
Object throwCode(int pCode) throws XmlRpcException;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for {@link JiraTest#testXMLRPC113()}
|
||||||
|
*/
|
||||||
|
public static class XMLRPC113HandlerImpl implements XMLRPC113Handler {
|
||||||
|
public Object throwCode(int pCode) throws XmlRpcException {
|
||||||
|
throw new XmlRpcException(pCode, "Message: " + pCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testXMLRPC113(ClientProvider pProvider) throws Exception {
|
||||||
|
XmlRpcClient client = pProvider.getClient();
|
||||||
|
client.setConfig(getConfig(pProvider));
|
||||||
|
XMLRPC113Handler handler = (XMLRPC113Handler) new ClientFactory(client).newInstance(XMLRPC113Handler.class);
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
try {
|
||||||
|
client.execute(XMLRPC113Handler.class.getName() + ".throwCode", new Object[]{new Integer(i)});
|
||||||
|
fail("Excpected exception");
|
||||||
|
} catch (XmlRpcException e) {
|
||||||
|
assertEquals(i, e.code);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
handler.throwCode(i);
|
||||||
|
} catch (XmlRpcException e) {
|
||||||
|
assertEquals(i, e.code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for {@link JiraTest#testXMLRPC115()}
|
||||||
|
*/
|
||||||
|
public static class XMLRPC115Handler {
|
||||||
|
/**
|
||||||
|
* Does nothing, just for checking, whether the server is alive.
|
||||||
|
*/
|
||||||
|
public Object[] ping() {
|
||||||
|
return new Object[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test case for <a href="http://issues.apache.org/jira/browse/XMLRPC-115">
|
||||||
|
* XMLRPC-115</a>
|
||||||
|
*/
|
||||||
|
public void testXMLRPC115() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testXMLRPC115(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testXMLRPC115(ClientProvider pProvider) throws Exception {
|
||||||
|
if (pProvider instanceof SunHttpTransportProvider) {
|
||||||
|
XmlRpcClient client = pProvider.getClient();
|
||||||
|
client.setConfig(getConfig(pProvider));
|
||||||
|
URL url = ((XmlRpcHttpClientConfig) client.getConfig()).getServerURL();
|
||||||
|
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||||
|
conn.setDoInput(true);
|
||||||
|
conn.setDoOutput(true);
|
||||||
|
conn.setRequestMethod("POST");
|
||||||
|
conn.setRequestProperty("content-type", "text/xml");
|
||||||
|
OutputStream ostream = conn.getOutputStream();
|
||||||
|
Writer w = new OutputStreamWriter(ostream, "UTF-8");
|
||||||
|
w.write("<methodCall><methodName>" + XMLRPC115Handler.class.getName() + ".ping"
|
||||||
|
+ "</methodName></methodCall>");
|
||||||
|
w.close();
|
||||||
|
InputStream istream = conn.getInputStream();
|
||||||
|
XmlRpcResponseParser parser = new XmlRpcResponseParser((XmlRpcStreamRequestConfig) client.getClientConfig(), client.getTypeFactory());
|
||||||
|
XMLReader xr = SAXParsers.newXMLReader();
|
||||||
|
xr.setContentHandler(parser);
|
||||||
|
xr.parse(new InputSource(istream));
|
||||||
|
istream.close();
|
||||||
|
assertTrue(parser.getResult() instanceof Object[]);
|
||||||
|
assertEquals(0, ((Object[]) parser.getResult()).length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test case for <a href="http://issues.apache.org/jira/browse/XMLRPC-119">
|
||||||
|
* XMLRPC-119</a>
|
||||||
|
*/
|
||||||
|
public void testXMLRPC119() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testXMLRPC119(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Handler for XMLRPC-119
|
||||||
|
*/
|
||||||
|
public static class XMLRPC119Handler {
|
||||||
|
/** Returns a string with a length of "num" Kilobytes.
|
||||||
|
*/
|
||||||
|
public String getString(int pSize) {
|
||||||
|
StringBuffer sb = new StringBuffer(pSize*1024);
|
||||||
|
for (int i = 0; i < pSize*1024; i++) {
|
||||||
|
sb.append('&');
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testXMLRPC119(ClientProvider pProvider) throws Exception {
|
||||||
|
XmlRpcClient client = pProvider.getClient();
|
||||||
|
client.setConfig(getConfig(pProvider));
|
||||||
|
for (int i = 0; i < 100; i+= 10) {
|
||||||
|
String s = (String) client.execute(XMLRPC119Handler.class.getName() + ".getString", new Object[]{new Integer(i)});
|
||||||
|
assertEquals(i*1024, s.length());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.xbib.netty.http.xmlrpc.client.test;
|
||||||
|
|
||||||
|
import org.apache.xmlrpc.client.XmlRpcClient;
|
||||||
|
import org.apache.xmlrpc.client.XmlRpcLiteHttpTransportFactory;
|
||||||
|
import org.apache.xmlrpc.client.XmlRpcTransportFactory;
|
||||||
|
import org.apache.xmlrpc.server.XmlRpcHandlerMapping;
|
||||||
|
|
||||||
|
|
||||||
|
/** Provider for testing the
|
||||||
|
* {@link org.apache.xmlrpc.client.XmlRpcLiteHttpTransport}.
|
||||||
|
*/
|
||||||
|
public class LiteTransportProvider extends WebServerProvider {
|
||||||
|
/** Creates a new instance.
|
||||||
|
* @param pMapping The test servers handler mapping.
|
||||||
|
* @param pContentLength Whether a Content-Length header is required.
|
||||||
|
*/
|
||||||
|
public LiteTransportProvider(XmlRpcHandlerMapping pMapping,
|
||||||
|
boolean pContentLength) {
|
||||||
|
super(pMapping, pContentLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected XmlRpcTransportFactory getTransportFactory(XmlRpcClient pClient) {
|
||||||
|
return new XmlRpcLiteHttpTransportFactory(pClient);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.xbib.netty.http.xmlrpc.client.test;
|
||||||
|
|
||||||
|
import org.apache.xmlrpc.client.XmlRpcClient;
|
||||||
|
import org.apache.xmlrpc.client.XmlRpcLocalStreamTransportFactory;
|
||||||
|
import org.apache.xmlrpc.client.XmlRpcTransportFactory;
|
||||||
|
import org.apache.xmlrpc.server.XmlRpcHandlerMapping;
|
||||||
|
import org.apache.xmlrpc.server.XmlRpcLocalStreamServer;
|
||||||
|
import org.apache.xmlrpc.server.XmlRpcServer;
|
||||||
|
|
||||||
|
|
||||||
|
/** Implementation of {@link org.apache.xmlrpc.test.BaseTest}
|
||||||
|
* for testing the {@link org.apache.xmlrpc.client.XmlRpcLocalStreamTransport}.
|
||||||
|
*/
|
||||||
|
public class LocalStreamTransportProvider extends LocalTransportProvider {
|
||||||
|
private XmlRpcLocalStreamServer server;
|
||||||
|
|
||||||
|
/** Creates a new instance.
|
||||||
|
* @param pMapping The test servers handler mapping.
|
||||||
|
*/
|
||||||
|
public LocalStreamTransportProvider(XmlRpcHandlerMapping pMapping) {
|
||||||
|
super(pMapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected XmlRpcTransportFactory getTransportFactory(XmlRpcClient pClient) {
|
||||||
|
server = new XmlRpcLocalStreamServer();
|
||||||
|
XmlRpcLocalStreamTransportFactory factory
|
||||||
|
= new XmlRpcLocalStreamTransportFactory(pClient, server);
|
||||||
|
return factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public XmlRpcServer getServer() {
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.xbib.netty.http.xmlrpc.client.test;
|
||||||
|
|
||||||
|
import org.apache.xmlrpc.client.XmlRpcClient;
|
||||||
|
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
|
||||||
|
import org.apache.xmlrpc.client.XmlRpcLocalTransportFactory;
|
||||||
|
import org.apache.xmlrpc.client.XmlRpcTransportFactory;
|
||||||
|
import org.apache.xmlrpc.server.XmlRpcHandlerMapping;
|
||||||
|
import org.apache.xmlrpc.server.XmlRpcServer;
|
||||||
|
|
||||||
|
|
||||||
|
/** Implementation of {@link org.apache.xmlrpc.test.BaseTest}
|
||||||
|
* for testing the {@link org.apache.xmlrpc.client.XmlRpcLocalTransport}.
|
||||||
|
*/
|
||||||
|
public class LocalTransportProvider extends ClientProviderImpl {
|
||||||
|
private XmlRpcServer server;
|
||||||
|
|
||||||
|
/** Creates a new instance.
|
||||||
|
* @param pMapping The test servers handler mapping.
|
||||||
|
*/
|
||||||
|
public LocalTransportProvider(XmlRpcHandlerMapping pMapping) {
|
||||||
|
super(pMapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected XmlRpcTransportFactory getTransportFactory(XmlRpcClient pClient) {
|
||||||
|
XmlRpcLocalTransportFactory factory = new XmlRpcLocalTransportFactory(pClient);
|
||||||
|
return factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public XmlRpcClientConfigImpl getConfig() throws Exception {
|
||||||
|
XmlRpcClientConfigImpl config = super.getConfig();
|
||||||
|
server = getXmlRpcServer();
|
||||||
|
config.setXmlRpcServer(server);
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public XmlRpcServer getServer() {
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void shutdown() {
|
||||||
|
// Does nothing
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.xbib.netty.http.xmlrpc.client.test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.text.Collator;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.apache.xmlrpc.XmlRpcException;
|
||||||
|
import org.apache.xmlrpc.client.XmlRpcClient;
|
||||||
|
import org.apache.xmlrpc.client.XmlRpcClientConfig;
|
||||||
|
import org.apache.xmlrpc.metadata.XmlRpcSystemImpl;
|
||||||
|
import org.apache.xmlrpc.server.PropertyHandlerMapping;
|
||||||
|
import org.apache.xmlrpc.server.XmlRpcHandlerMapping;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test class for the introspection stuff.
|
||||||
|
*/
|
||||||
|
public class MetadataTest extends XmlRpcTestCase {
|
||||||
|
protected XmlRpcHandlerMapping getHandlerMapping() throws IOException,
|
||||||
|
XmlRpcException {
|
||||||
|
PropertyHandlerMapping mapping = new PropertyHandlerMapping();
|
||||||
|
mapping.addHandler("Adder", AuthenticationTest.AdderImpl.class);
|
||||||
|
XmlRpcSystemImpl.addSystemHandler(mapping);
|
||||||
|
return mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test, whether the actual handlers are working.
|
||||||
|
*/
|
||||||
|
public void testAdder() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testAdder(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testAdder(ClientProvider pProvider) throws Exception {
|
||||||
|
XmlRpcClient client = pProvider.getClient();
|
||||||
|
XmlRpcClientConfig config = getConfig(pProvider);
|
||||||
|
client.setConfig(config);
|
||||||
|
Object o = client.execute("Adder.add", new Object[]{new Integer(3), new Integer(5)});
|
||||||
|
assertEquals(new Integer(8), o);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for system.listMethods.
|
||||||
|
*/
|
||||||
|
public void testListMethods() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testListMethods(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testListMethods(ClientProvider pProvider) throws Exception {
|
||||||
|
XmlRpcClient client = pProvider.getClient();
|
||||||
|
XmlRpcClientConfig config = getConfig(pProvider);
|
||||||
|
client.setConfig(config);
|
||||||
|
Object o = client.execute("system.listMethods", new Object[0]);
|
||||||
|
Object[] methodList = (Object[]) o;
|
||||||
|
Arrays.sort(methodList, Collator.getInstance(Locale.US));
|
||||||
|
assertEquals(4, methodList.length);
|
||||||
|
assertEquals("Adder.add", methodList[0]);
|
||||||
|
assertEquals("system.listMethods", methodList[1]);
|
||||||
|
assertEquals("system.methodHelp", methodList[2]);
|
||||||
|
assertEquals("system.methodSignature", methodList[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for system.methodHelp.
|
||||||
|
*/
|
||||||
|
public void testMethodHelp() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testMethodHelp(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testMethodHelp(ClientProvider pProvider) throws Exception {
|
||||||
|
XmlRpcClient client = pProvider.getClient();
|
||||||
|
XmlRpcClientConfig config = getConfig(pProvider);
|
||||||
|
client.setConfig(config);
|
||||||
|
String help = (String) client.execute("system.methodHelp", new Object[]{"Adder.add"});
|
||||||
|
assertEquals("Invokes the method org.apache.xmlrpc.test.AuthenticationTest$AdderImpl.add(int, int).", help);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for system.methodSignature.
|
||||||
|
*/
|
||||||
|
public void testMethodSignature() throws Exception {
|
||||||
|
for (int i = 0; i < providers.length; i++) {
|
||||||
|
testMethodSignature(providers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testMethodSignature(ClientProvider pProvider) throws Exception {
|
||||||
|
XmlRpcClient client = pProvider.getClient();
|
||||||
|
XmlRpcClientConfig config = getConfig(pProvider);
|
||||||
|
client.setConfig(config);
|
||||||
|
Object[] signatures = (Object[]) client.execute("system.methodSignature", new Object[]{"Adder.add"});
|
||||||
|
assertEquals(signatures.length, 1);
|
||||||
|
Object[] signature = (Object[]) signatures[0];
|
||||||
|
assertEquals(3, signature.length);
|
||||||
|
assertEquals("int", signature[0]);
|
||||||
|
assertEquals("int", signature[1]);
|
||||||
|
assertEquals("int", signature[2]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,168 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.xbib.netty.http.xmlrpc.client.test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.lang.reflect.UndeclaredThrowableException;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import org.apache.xmlrpc.XmlRpcException;
|
||||||
|
import org.apache.xmlrpc.client.XmlRpcClient;
|
||||||
|
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
|
||||||
|
import org.apache.xmlrpc.common.XmlRpcHttpRequestConfigImpl;
|
||||||
|
import org.apache.xmlrpc.common.XmlRpcStreamConfig;
|
||||||
|
import org.apache.xmlrpc.common.XmlRpcStreamRequestConfig;
|
||||||
|
import org.apache.xmlrpc.parser.DateParser;
|
||||||
|
import org.apache.xmlrpc.parser.XmlRpcRequestParser;
|
||||||
|
import org.apache.xmlrpc.parser.XmlRpcResponseParser;
|
||||||
|
import org.apache.xmlrpc.util.SAXParsers;
|
||||||
|
import org.apache.xmlrpc.util.XmlRpcDateTimeFormat;
|
||||||
|
import org.xml.sax.InputSource;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
import org.xml.sax.SAXParseException;
|
||||||
|
import org.xml.sax.XMLReader;
|
||||||
|
|
||||||
|
|
||||||
|
/** Test for the various parsers.
|
||||||
|
*/
|
||||||
|
public class ParserTest extends TestCase {
|
||||||
|
private Object parseResponse(final String s) throws XmlRpcException, IOException, SAXException {
|
||||||
|
return parseResponse(new InputSource(new StringReader(s)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object parseResponse(InputSource isource) throws XmlRpcException,
|
||||||
|
IOException, SAXException {
|
||||||
|
XmlRpcStreamRequestConfig config = new XmlRpcClientConfigImpl();
|
||||||
|
XmlRpcClient client = new XmlRpcClient();
|
||||||
|
XmlRpcResponseParser parser = new XmlRpcResponseParser(config, client.getTypeFactory());
|
||||||
|
XMLReader xr = SAXParsers.newXMLReader();
|
||||||
|
xr.setContentHandler(parser);
|
||||||
|
xr.parse(isource);
|
||||||
|
Object o = parser.getResult();
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
private XmlRpcRequestParser parseRequest(final String s) throws XmlRpcException, IOException, SAXException {
|
||||||
|
XmlRpcStreamConfig config = new XmlRpcHttpRequestConfigImpl();
|
||||||
|
XmlRpcClient client = new XmlRpcClient();
|
||||||
|
XmlRpcRequestParser parser = new XmlRpcRequestParser(config, client.getTypeFactory());
|
||||||
|
XMLReader xr = SAXParsers.newXMLReader();
|
||||||
|
xr.setContentHandler(parser);
|
||||||
|
xr.parse(new InputSource(new StringReader(s)));
|
||||||
|
return parser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Tests, whether strings can be parsed with,
|
||||||
|
* or without, the "string" tag.
|
||||||
|
*/
|
||||||
|
public void testStringType() throws Exception {
|
||||||
|
final String[] strings = new String[]{
|
||||||
|
"3", "<string>3</string>",
|
||||||
|
" <string>3</string> "
|
||||||
|
};
|
||||||
|
for (int i = 0; i < strings.length; i++) {
|
||||||
|
final String s =
|
||||||
|
"<?xml version='1.0' encoding='UTF-8'?>\n"
|
||||||
|
+ "<methodResponse><params><param>\n"
|
||||||
|
+ "<value>" + strings[i] + "</value></param>\n"
|
||||||
|
+ "</params></methodResponse>\n";
|
||||||
|
Object o = parseResponse(s);
|
||||||
|
assertEquals("3", o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Tests, whether nested arrays can be parsed.
|
||||||
|
*/
|
||||||
|
public void testNestedObjectArrays() throws Exception {
|
||||||
|
final String s =
|
||||||
|
"<?xml version='1.0' encoding='UTF-8'?>\n"
|
||||||
|
+ "<methodResponse><params><param>\n"
|
||||||
|
+ "<value><array><data><value><array>\n"
|
||||||
|
+ "<data><value>array</value>\n"
|
||||||
|
+ "<value>string</value></data></array>\n"
|
||||||
|
+ "</value></data></array></value></param>\n"
|
||||||
|
+ "</params></methodResponse>\n";
|
||||||
|
Object o = parseResponse(s);
|
||||||
|
assertTrue(o instanceof Object[]);
|
||||||
|
Object[] outer = (Object[]) o;
|
||||||
|
assertEquals(1, outer.length);
|
||||||
|
o = outer[0];
|
||||||
|
assertTrue(o instanceof Object[]);
|
||||||
|
Object[] inner = (Object[]) o;
|
||||||
|
assertEquals(2, inner.length);
|
||||||
|
assertEquals("array", inner[0]);
|
||||||
|
assertEquals("string", inner[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests, whether a request may omit the <params> tag.
|
||||||
|
*/
|
||||||
|
public void testOptionalParams() throws Exception {
|
||||||
|
final String s1 = "<methodResponse/>";
|
||||||
|
Object o1 = parseResponse(s1);
|
||||||
|
assertNull(o1);
|
||||||
|
|
||||||
|
final String s2 = "<methodResponse><params/></methodResponse>";
|
||||||
|
Object o2 = parseResponse(s2);
|
||||||
|
assertNull(o2);
|
||||||
|
|
||||||
|
final String s3 = "<methodCall><methodName>foo</methodName></methodCall>";
|
||||||
|
XmlRpcRequestParser p3 = parseRequest(s3);
|
||||||
|
assertEquals("foo", p3.getMethodName());
|
||||||
|
assertNull(p3.getParams());
|
||||||
|
|
||||||
|
final String s4 = "<methodCall><methodName>bar</methodName><params/></methodCall>";
|
||||||
|
XmlRpcRequestParser p4 = parseRequest(s4);
|
||||||
|
assertEquals("bar", p4.getMethodName());
|
||||||
|
assertNotNull(p4.getParams());
|
||||||
|
assertTrue(p4.getParams().size() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for XMLRPC-140.
|
||||||
|
*/
|
||||||
|
public void testXMLRPC140() throws Exception {
|
||||||
|
DateParser parser = new DateParser(new XmlRpcDateTimeFormat(){
|
||||||
|
private static final long serialVersionUID = 0L;
|
||||||
|
protected TimeZone getTimeZone() {
|
||||||
|
return TimeZone.getDefault();
|
||||||
|
}
|
||||||
|
}){
|
||||||
|
public void setResult(Object pObject){
|
||||||
|
try {
|
||||||
|
super.setResult((String) pObject);
|
||||||
|
} catch (SAXException e) {
|
||||||
|
throw new UndeclaredThrowableException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
parser.setResult("20070316T162808Z");
|
||||||
|
fail("Expected exception");
|
||||||
|
} catch (UndeclaredThrowableException e) {
|
||||||
|
SAXParseException spe = (SAXParseException) e.getUndeclaredThrowable();
|
||||||
|
ParseException pe = (ParseException) spe.getException();
|
||||||
|
assertEquals(11, pe.getErrorOffset());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,212 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.xbib.netty.http.xmlrpc.client.test;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServlet;
|
||||||
|
|
||||||
|
import org.apache.xmlrpc.XmlRpcException;
|
||||||
|
import org.apache.xmlrpc.client.XmlRpcClient;
|
||||||
|
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
|
||||||
|
import org.apache.xmlrpc.server.PropertyHandlerMapping;
|
||||||
|
import org.apache.xmlrpc.server.XmlRpcHandlerMapping;
|
||||||
|
import org.apache.xmlrpc.util.ThreadPool;
|
||||||
|
import org.apache.xmlrpc.webserver.ServletWebServer;
|
||||||
|
import org.apache.xmlrpc.webserver.WebServer;
|
||||||
|
import org.apache.xmlrpc.webserver.XmlRpcServlet;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the frameworks scalability.
|
||||||
|
*/
|
||||||
|
public class ScalabilityTest extends TestCase {
|
||||||
|
/**
|
||||||
|
* Primitive handler class
|
||||||
|
*/
|
||||||
|
public static class Adder {
|
||||||
|
/**
|
||||||
|
* Returns the sum of the numbers p1 and p2.
|
||||||
|
*/
|
||||||
|
public int add(int p1, int p2) {
|
||||||
|
return p1 + p2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MyServletWebServer extends ServletWebServer {
|
||||||
|
protected ThreadPool pool;
|
||||||
|
MyServletWebServer(HttpServlet pServlet, int pPort)
|
||||||
|
throws ServletException {
|
||||||
|
super(pServlet, pPort);
|
||||||
|
}
|
||||||
|
public ThreadPool newThreadPool(){
|
||||||
|
pool = new ThreadPool(getXmlRpcServer().getMaxThreads(), "XML-RPC"){
|
||||||
|
};
|
||||||
|
return pool;
|
||||||
|
}
|
||||||
|
int getNumThreads() {
|
||||||
|
return pool.getNumThreads();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MyWebServer extends WebServer {
|
||||||
|
protected ThreadPool pool;
|
||||||
|
MyWebServer(int pPort) {
|
||||||
|
super(pPort);
|
||||||
|
}
|
||||||
|
public ThreadPool newThreadPool(){
|
||||||
|
pool = new ThreadPool(getXmlRpcServer().getMaxThreads(), "XML-RPC"){
|
||||||
|
};
|
||||||
|
return pool;
|
||||||
|
}
|
||||||
|
int getNumThreads() {
|
||||||
|
return pool.getNumThreads();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int BASE = 1;
|
||||||
|
private static final Integer THREE = new Integer(3);
|
||||||
|
private static final Integer FIVE = new Integer(5);
|
||||||
|
private static final Integer EIGHT = new Integer(8);
|
||||||
|
private XmlRpcServlet servlet;
|
||||||
|
private MyServletWebServer server;
|
||||||
|
private MyWebServer webServer;
|
||||||
|
|
||||||
|
private XmlRpcHandlerMapping newXmlRpcHandlerMapping() throws XmlRpcException {
|
||||||
|
PropertyHandlerMapping mapping = new PropertyHandlerMapping();
|
||||||
|
mapping.addHandler("Adder", Adder.class);
|
||||||
|
return mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initServletWebServer() throws Exception {
|
||||||
|
servlet = new XmlRpcServlet(){
|
||||||
|
private static final long serialVersionUID = -2040521497373327817L;
|
||||||
|
protected XmlRpcHandlerMapping newXmlRpcHandlerMapping()
|
||||||
|
throws XmlRpcException {
|
||||||
|
return ScalabilityTest.this.newXmlRpcHandlerMapping();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
server = new MyServletWebServer(servlet, 0);
|
||||||
|
server.getXmlRpcServer().setMaxThreads(25);
|
||||||
|
server.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shutdownServletWebServer() {
|
||||||
|
server.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initWebServer() throws Exception {
|
||||||
|
webServer = new MyWebServer(0);
|
||||||
|
webServer.getXmlRpcServer().setHandlerMapping(newXmlRpcHandlerMapping());
|
||||||
|
webServer.getXmlRpcServer().setMaxThreads(25);
|
||||||
|
webServer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shutdownWebServer() {
|
||||||
|
webServer.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the test with a single client.
|
||||||
|
*/
|
||||||
|
public void testSingleClient() throws Exception {
|
||||||
|
initServletWebServer();
|
||||||
|
boolean ok = false;
|
||||||
|
try {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
servlet.getXmlRpcServletServer().setMaxThreads(1);
|
||||||
|
new Client(100*BASE, server.getPort()).run();
|
||||||
|
System.out.println("Single client: " + (System.currentTimeMillis()-now) + ", " + server.getNumThreads());
|
||||||
|
shutdownServletWebServer();
|
||||||
|
ok = true;
|
||||||
|
} finally {
|
||||||
|
if (!ok) { try { shutdownServletWebServer(); } catch (Throwable t) {} }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the web server test with a single client.
|
||||||
|
*/
|
||||||
|
public void testSingleWebServerClient() throws Exception {
|
||||||
|
initWebServer();
|
||||||
|
boolean ok = false;
|
||||||
|
try {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
webServer.getXmlRpcServer().setMaxThreads(1);
|
||||||
|
new Client(100*BASE, webServer.getPort()).run();
|
||||||
|
System.out.println("Single client: " + (System.currentTimeMillis()-now) + ", " + webServer.getNumThreads());
|
||||||
|
shutdownWebServer();
|
||||||
|
ok = true;
|
||||||
|
} finally {
|
||||||
|
if (!ok) { try { shutdownWebServer(); } catch (Throwable t) {} }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Client implements Runnable {
|
||||||
|
private final int iterations;
|
||||||
|
private final int port;
|
||||||
|
Client(int pIterations, int pPort) {
|
||||||
|
iterations = pIterations;
|
||||||
|
port = pPort;
|
||||||
|
}
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
XmlRpcClient client = new XmlRpcClient();
|
||||||
|
XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
|
||||||
|
config.setServerURL(new URL("http://127.0.0.1:" + port + "/"));
|
||||||
|
client.setConfig(config);
|
||||||
|
for (int i = 0; i < iterations; i++) {
|
||||||
|
assertEquals(EIGHT, client.execute("Adder.add", new Object[]{THREE, FIVE}));
|
||||||
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
t.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the test with ten clients.
|
||||||
|
*/
|
||||||
|
public void testTenClient() throws Exception {
|
||||||
|
initServletWebServer();
|
||||||
|
boolean ok = false;
|
||||||
|
try {
|
||||||
|
final Thread[] threads = new Thread[10];
|
||||||
|
servlet.getXmlRpcServletServer().setMaxThreads(10);
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
for (int i = 0; i < threads.length; i++) {
|
||||||
|
threads[i] = new Thread(new Client(10*BASE, server.getPort()));
|
||||||
|
threads[i].start();
|
||||||
|
}
|
||||||
|
for (int i = 0; i < threads.length; i++) {
|
||||||
|
threads[i].join();
|
||||||
|
}
|
||||||
|
System.out.println("Ten clients: " + (System.currentTimeMillis() - now) + ", " + server.getNumThreads());
|
||||||
|
shutdownServletWebServer();
|
||||||
|
ok = false;
|
||||||
|
} finally {
|
||||||
|
if (!ok) { try { shutdownServletWebServer(); } catch (Throwable t) {} }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,187 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client.test;
|
||||||
|
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import javax.xml.parsers.SAXParserFactory;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import org.xbib.netty.http.xmlrpc.client.XmlRpcClient;
|
||||||
|
import org.xbib.netty.http.xmlrpc.client.XmlRpcClientConfig;
|
||||||
|
import org.xbib.netty.http.xmlrpc.client.XmlRpcClientConfigImpl;
|
||||||
|
import org.xbib.netty.http.xmlrpc.client.XmlRpcClientRequestImpl;
|
||||||
|
import org.xbib.netty.http.xmlrpc.client.XmlRpcSunHttpTransportFactory;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.TypeFactoryImpl;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcRequest;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.XmlRpcStreamRequestConfig;
|
||||||
|
import org.xbib.netty.http.xmlrpc.common.parser.XmlRpcRequestParser;
|
||||||
|
import org.xbib.netty.http.xmlrpc.server.XmlRpcServer;
|
||||||
|
import org.xbib.netty.http.xmlrpc.server.XmlRpcServerConfigImpl;
|
||||||
|
import org.xml.sax.InputSource;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
import org.xml.sax.XMLReader;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
|
||||||
|
/** A test case for the various serializers.
|
||||||
|
*/
|
||||||
|
public class SerializerTest extends TestCase {
|
||||||
|
private final XmlRpcClient client;
|
||||||
|
|
||||||
|
/** Creates a new instance.
|
||||||
|
*/
|
||||||
|
public SerializerTest() {
|
||||||
|
client = new XmlRpcClient();
|
||||||
|
client.setTransportFactory(new XmlRpcSunHttpTransportFactory(client));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected XmlRpcClientConfigImpl getConfig() {
|
||||||
|
XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected XmlRpcStreamRequestConfig getExConfig() {
|
||||||
|
XmlRpcClientConfigImpl config = getConfig();
|
||||||
|
config.setEnabledForExtensions(true);
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String writeRequest(XmlRpcStreamRequestConfig pConfig, XmlRpcRequest pRequest)
|
||||||
|
throws SAXException {
|
||||||
|
client.setConfig((XmlRpcClientConfig) pConfig);
|
||||||
|
return XmlRpcTestCase.writeRequest(client, pRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Test serialization of a byte parameter.
|
||||||
|
* @throws Exception The test failed.
|
||||||
|
*/
|
||||||
|
public void testByteParam() throws Exception {
|
||||||
|
XmlRpcStreamRequestConfig config = getExConfig();
|
||||||
|
XmlRpcRequest request = new XmlRpcClientRequestImpl(config, "byteParam", new Object[]{new Byte((byte)3)});
|
||||||
|
String got = writeRequest(config, request);
|
||||||
|
String expect =
|
||||||
|
"<?xml version=\"1.0\" encoding=\"US-ASCII\"?>"
|
||||||
|
+ "<methodCall xmlns:ex=\"http://ws.apache.org/xmlrpc/namespaces/extensions\">"
|
||||||
|
+ "<methodName>byteParam</methodName><params><param><value><ex:i1>3</ex:i1></value></param></params></methodCall>";
|
||||||
|
assertEquals(expect, got);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Test serialization of an integer parameter.
|
||||||
|
* @throws Exception The test failed.
|
||||||
|
*/
|
||||||
|
public void testIntParam() throws Exception {
|
||||||
|
XmlRpcStreamRequestConfig config = getConfig();
|
||||||
|
XmlRpcRequest request = new XmlRpcClientRequestImpl(config, "intParam", new Object[]{new Integer(3)});
|
||||||
|
String got = writeRequest(config, request);
|
||||||
|
String expect =
|
||||||
|
"<?xml version=\"1.0\" encoding=\"US-ASCII\"?>"
|
||||||
|
+ "<methodCall>"
|
||||||
|
+ "<methodName>intParam</methodName><params><param><value><i4>3</i4></value></param></params></methodCall>";
|
||||||
|
assertEquals(expect, got);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Test serialization of a byte array.
|
||||||
|
* @throws Exception The test failed.
|
||||||
|
*/
|
||||||
|
public void testByteArrayParam() throws Exception {
|
||||||
|
byte[] bytes = new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||||
|
XmlRpcStreamRequestConfig config = getConfig();
|
||||||
|
XmlRpcRequest request = new XmlRpcClientRequestImpl(config, "byteArrayParam", new Object[]{bytes});
|
||||||
|
String got = writeRequest(config, request);
|
||||||
|
String expect =
|
||||||
|
"<?xml version=\"1.0\" encoding=\"US-ASCII\"?>"
|
||||||
|
+ "<methodCall>"
|
||||||
|
+ "<methodName>byteArrayParam</methodName><params><param><value><base64>AAECAwQFBgcICQ==</base64></value></param></params></methodCall>";
|
||||||
|
assertEquals(expect, got);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Test serialization of a map.
|
||||||
|
* @throws Exception The test failed.
|
||||||
|
*/
|
||||||
|
public void testMapParam() throws Exception {
|
||||||
|
final Map map = new HashMap();
|
||||||
|
map.put("2", new Integer(3));
|
||||||
|
map.put("3", new Integer(5));
|
||||||
|
final Object[] params = new Object[]{map};
|
||||||
|
XmlRpcStreamRequestConfig config = getConfig();
|
||||||
|
XmlRpcRequest request = new XmlRpcClientRequestImpl(config, "mapParam", params);
|
||||||
|
String got = writeRequest(config, request);
|
||||||
|
String expect =
|
||||||
|
"<?xml version=\"1.0\" encoding=\"US-ASCII\"?>"
|
||||||
|
+ "<methodCall><methodName>mapParam</methodName>"
|
||||||
|
+ "<params><param><value><struct>"
|
||||||
|
+ "<member><name>3</name><value><i4>5</i4></value></member>"
|
||||||
|
+ "<member><name>2</name><value><i4>3</i4></value></member>"
|
||||||
|
+ "</struct></value></param></params></methodCall>";
|
||||||
|
assertEquals(expect, got);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Tests serialization of a calendar instance.
|
||||||
|
*/
|
||||||
|
public void testCalendarParam() throws Exception {
|
||||||
|
TimeZone tz = TimeZone.getTimeZone("GMT");
|
||||||
|
Calendar cal1 = Calendar.getInstance(tz);
|
||||||
|
cal1.set(1933, 5, 12, 11, 7, 21);
|
||||||
|
cal1.set(Calendar.MILLISECOND, 311);
|
||||||
|
Calendar cal2 = Calendar.getInstance(TimeZone.getDefault());
|
||||||
|
cal2.set(1933, 5, 12, 11, 7, 21);
|
||||||
|
cal2.set(Calendar.MILLISECOND, 311);
|
||||||
|
XmlRpcStreamRequestConfig config = getExConfig();
|
||||||
|
XmlRpcRequest request = new XmlRpcClientRequestImpl(config, "dateParam", new Object[]{cal1, cal2.getTime()});
|
||||||
|
String got = writeRequest(config, request);
|
||||||
|
String expect =
|
||||||
|
"<?xml version=\"1.0\" encoding=\"US-ASCII\"?>"
|
||||||
|
+ "<methodCall xmlns:ex=\"http://ws.apache.org/xmlrpc/namespaces/extensions\">"
|
||||||
|
+ "<methodName>dateParam</methodName><params>"
|
||||||
|
+ "<param><value><ex:dateTime>1933-06-12T11:07:21.311Z</ex:dateTime></value></param>"
|
||||||
|
+ "<param><value><dateTime.iso8601>19330612T11:07:21</dateTime.iso8601></value></param>"
|
||||||
|
+ "</params></methodCall>";
|
||||||
|
assertEquals(expect, got);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for XMLRPC-127: Is it possible to transmit a
|
||||||
|
* map with integers as the keys?
|
||||||
|
*/
|
||||||
|
public void testIntegerKeyMap() throws Exception {
|
||||||
|
Map map = new HashMap();
|
||||||
|
map.put(new Integer(1), "one");
|
||||||
|
XmlRpcStreamRequestConfig config = getExConfig();
|
||||||
|
XmlRpcRequest request = new XmlRpcClientRequestImpl(config, "integerKeyMap", new Object[]{map});
|
||||||
|
String got = writeRequest(config, request);
|
||||||
|
String expect =
|
||||||
|
"<?xml version=\"1.0\" encoding=\"US-ASCII\"?>"
|
||||||
|
+ "<methodCall xmlns:ex=\"http://ws.apache.org/xmlrpc/namespaces/extensions\">"
|
||||||
|
+ "<methodName>integerKeyMap</methodName><params>"
|
||||||
|
+ "<param><value><struct><member>"
|
||||||
|
+ "<name><value><i4>1</i4></value></name>"
|
||||||
|
+ "<value>one</value></member>"
|
||||||
|
+ "</struct></value></param>"
|
||||||
|
+ "</params></methodCall>";
|
||||||
|
assertEquals(expect, got);
|
||||||
|
|
||||||
|
XmlRpcServer server = new XmlRpcServer();
|
||||||
|
XmlRpcServerConfigImpl serverConfig = new XmlRpcServerConfigImpl();
|
||||||
|
serverConfig.setEnabledForExtensions(true);
|
||||||
|
server.setConfig(serverConfig);
|
||||||
|
XmlRpcRequestParser parser = new XmlRpcRequestParser(serverConfig, new TypeFactoryImpl(server));
|
||||||
|
SAXParserFactory spf = SAXParserFactory.newInstance();
|
||||||
|
spf.setValidating(false);
|
||||||
|
spf.setNamespaceAware(true);
|
||||||
|
XMLReader xr = spf.newSAXParser().getXMLReader();
|
||||||
|
xr.setContentHandler(parser);
|
||||||
|
xr.parse(new InputSource(new StringReader(expect)));
|
||||||
|
assertEquals("integerKeyMap", parser.getMethodName());
|
||||||
|
List params = parser.getParams();
|
||||||
|
assertEquals(1, params.size());
|
||||||
|
Map paramMap = (Map) params.get(0);
|
||||||
|
assertEquals(1, paramMap.size());
|
||||||
|
assertEquals("one", paramMap.get(new Integer(1)));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client.test;
|
||||||
|
|
||||||
|
import org.xbib.netty.http.xmlrpc.client.XmlRpcClient;
|
||||||
|
import org.xbib.netty.http.xmlrpc.client.XmlRpcClientConfigImpl;
|
||||||
|
import org.xbib.netty.http.xmlrpc.client.XmlRpcSunHttpTransportFactory;
|
||||||
|
import org.xbib.netty.http.xmlrpc.client.XmlRpcTransportFactory;
|
||||||
|
import org.xbib.netty.http.xmlrpc.server.XmlRpcHandlerMapping;
|
||||||
|
import org.xbib.netty.http.xmlrpc.server.XmlRpcServer;
|
||||||
|
import org.xbib.netty.http.xmlrpc.server.XmlRpcServerConfigImpl;
|
||||||
|
import org.xbib.netty.http.xmlrpc.servlet.ServletWebServer;
|
||||||
|
import org.xbib.netty.http.xmlrpc.servlet.XmlRpcServlet;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A provider class for testing the {@link ServletWebServer}.
|
||||||
|
*/
|
||||||
|
public class ServletWebServerProvider extends ClientProviderImpl {
|
||||||
|
protected final ServletWebServer webServer;
|
||||||
|
protected final XmlRpcServlet servlet;
|
||||||
|
private final boolean contentLength;
|
||||||
|
private final int port;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of {@link XmlRpcServlet}.
|
||||||
|
*/
|
||||||
|
protected XmlRpcServlet newXmlRpcServlet() {
|
||||||
|
return new XmlRpcServlet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a new instance.
|
||||||
|
* @param pMapping The test servers handler mapping.
|
||||||
|
* @throws ServletException
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
protected ServletWebServerProvider(XmlRpcHandlerMapping pMapping, boolean pContentLength) throws ServletException, IOException {
|
||||||
|
super(pMapping);
|
||||||
|
contentLength = pContentLength;
|
||||||
|
servlet = newXmlRpcServlet();
|
||||||
|
webServer = new ServletWebServer(servlet, 0);
|
||||||
|
XmlRpcServer server = servlet.getXmlRpcServletServer();
|
||||||
|
server.setHandlerMapping(mapping);
|
||||||
|
XmlRpcServerConfigImpl serverConfig = (XmlRpcServerConfigImpl) server.getConfig();
|
||||||
|
serverConfig.setEnabledForExtensions(true);
|
||||||
|
serverConfig.setContentLengthOptional(!contentLength);
|
||||||
|
serverConfig.setEnabledForExceptions(true);
|
||||||
|
webServer.start();
|
||||||
|
port = webServer.getPort();
|
||||||
|
}
|
||||||
|
|
||||||
|
public final XmlRpcClientConfigImpl getConfig() throws Exception {
|
||||||
|
return getConfig(new URL("http://127.0.0.1:" + port + "/"));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected XmlRpcClientConfigImpl getConfig(URL pServerURL) throws Exception {
|
||||||
|
XmlRpcClientConfigImpl config = super.getConfig();
|
||||||
|
config.setServerURL(pServerURL);
|
||||||
|
config.setContentLengthOptional(!contentLength);
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected XmlRpcTransportFactory getTransportFactory(XmlRpcClient pClient) {
|
||||||
|
return new XmlRpcSunHttpTransportFactory(pClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
public XmlRpcServer getServer() {
|
||||||
|
return servlet.getXmlRpcServletServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void shutdown() {
|
||||||
|
webServer.shutdown();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package org.xbib.netty.http.xmlrpc.client.test;
|
||||||
|
|
||||||
|
import org.xbib.netty.http.xmlrpc.client.XmlRpcClient;
|
||||||
|
import org.xbib.netty.http.xmlrpc.client.XmlRpcSunHttpTransportFactory;
|
||||||
|
import org.xbib.netty.http.xmlrpc.client.XmlRpcTransportFactory;
|
||||||
|
import org.xbib.netty.http.xmlrpc.server.XmlRpcHandlerMapping;
|
||||||
|
|
||||||
|
/** Implementation of {@link BaseTest} for testing the
|
||||||
|
* {@link XmlRpcSunHttpTransport}.
|
||||||
|
*/
|
||||||
|
public class SunHttpTransportProvider extends WebServerProvider {
|
||||||
|
/** Creates a new instance.
|
||||||
|
* @param pMapping The test servers handler mapping.
|
||||||
|
* @param pContentLength Number of bytes being transmitted.
|
||||||
|
*/
|
||||||
|
public SunHttpTransportProvider(XmlRpcHandlerMapping pMapping, boolean pContentLength) {
|
||||||
|
super(pMapping, pContentLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected XmlRpcTransportFactory getTransportFactory(XmlRpcClient pClient) {
|
||||||
|
return new XmlRpcSunHttpTransportFactory(pClient);
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue