move Pair class

This commit is contained in:
Jörg Prante 2019-07-15 22:41:23 +02:00
parent 8975cd0978
commit 81b1aad724
143 changed files with 10792 additions and 154 deletions

View file

@ -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

View file

@ -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')}"
} }

View file

@ -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;

View file

@ -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;
} }

View file

@ -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);
} }
} }

View file

@ -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");
} }
} }

View file

@ -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;
}
}

View 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'
}
}

View file

@ -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

View file

@ -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;

View file

@ -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)

View file

@ -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;

View file

@ -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'
}
} }

View file

@ -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);
} }
} }

View file

@ -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();
} }

View file

@ -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));
} }

View file

@ -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;
} }

View file

@ -0,0 +1,4 @@
package org.xbib.netty.http.server.endpoint;
public interface EndpointDescriptor {
}

View file

@ -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);

View file

@ -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());
} }

View file

@ -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);
} }
} }
} }

View file

@ -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);
} }
} }

View file

@ -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();
} }
} }

View file

@ -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 + "]";
} }

View file

@ -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());

View file

@ -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,7 +38,7 @@ 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);
@ -46,7 +46,7 @@ class EndpointTest {
}) })
.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,7 +78,7 @@ 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);
@ -86,7 +86,7 @@ class EndpointTest {
}) })
.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,7 +119,7 @@ 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())
@ -129,7 +129,7 @@ class EndpointTest {
}) })
.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,7 +185,7 @@ 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())
@ -196,7 +196,7 @@ class EndpointTest {
}) })
.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()

View file

@ -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());
}
}

View 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'
}

View file

@ -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);
}

View file

@ -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);
});
}
}

View file

@ -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();
}
}

View file

@ -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;
}
}

View file

@ -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 {
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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; }
}

View file

@ -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();
}
}

View file

@ -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);
}
}

View file

@ -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());
}
}
}

View file

@ -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;
}
}

View file

@ -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();
}

View file

@ -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);
}
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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);
}
}
}

View file

@ -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;
}
}

View file

@ -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());
}
}

View file

@ -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()); }
}

View file

@ -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 {
}

View file

@ -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()));
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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; }
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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());
}
}

View file

@ -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());
}
}

View file

@ -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;
}

View file

@ -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();
}

View file

@ -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; }
}

View file

@ -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; }
}

View file

@ -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));
}
}

View file

@ -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;
}
}
}
}

View file

@ -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]);
}
}
}

View file

@ -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();
}

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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();
}
}

View file

@ -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());
}
}
}

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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
}
}

View file

@ -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]);
}
}

View file

@ -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());
}
}
}

View file

@ -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) {} }
}
}
}

View file

@ -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)));
}
}

View file

@ -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();
}
}

View file

@ -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