blob: 1dfd535b03ce68128bdabf0565a78c8f3e604581 [file] [log] [blame]
/*
* 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.apache.calcite.avatica.remote;
import org.apache.calcite.avatica.ConnectionSpec;
import org.apache.calcite.avatica.Meta;
import org.apache.calcite.avatica.jdbc.JdbcMeta;
import org.apache.calcite.avatica.metrics.MetricsSystemConfiguration;
import org.apache.calcite.avatica.remote.Driver.Serialization;
import org.apache.calcite.avatica.server.AvaticaHandler;
import org.apache.calcite.avatica.server.AvaticaJsonHandler;
import org.apache.calcite.avatica.server.AvaticaProtobufHandler;
import org.apache.calcite.avatica.server.AvaticaServerConfiguration;
import org.apache.calcite.avatica.server.HandlerFactory;
import org.apache.calcite.avatica.server.HttpServer;
import org.apache.calcite.avatica.server.Main;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
/**
* Utility class which encapsulates the setup required to write Avatica tests that run against
* servers using each serialization approach.
*/
public class AvaticaServersForTest {
private static final ConnectionSpec CONNECTION_SPEC = ConnectionSpec.HSQLDB;
private static final String[] SERVER_ARGS = { FullyRemoteJdbcMetaFactory.class.getName() };
private final Map<Serialization, HttpServer> serversBySerialization;
public AvaticaServersForTest() {
serversBySerialization = new HashMap<>();
}
/**
* Starts an Avatica server for each serialization type.
*/
public void startServers() throws Exception {
// Bind to '0' to pluck an ephemeral port instead of expecting a certain one to be free
final HttpServer jsonServer = Main.start(SERVER_ARGS, 0, new Main.HandlerFactory() {
@Override public AvaticaJsonHandler createHandler(Service service) {
return new AvaticaJsonHandler(service);
}
});
serversBySerialization.put(Serialization.JSON, jsonServer);
final HttpServer protobufServer = Main.start(SERVER_ARGS, 0, new Main.HandlerFactory() {
@Override public AvaticaProtobufHandler createHandler(Service service) {
return new AvaticaProtobufHandler(service);
}
});
serversBySerialization.put(Serialization.PROTOBUF, protobufServer);
}
/**
* Starts Avatica servers for each serialization type with the properties.
*/
public void startServers(Properties properties) {
final HandlerFactory factory = new HandlerFactory();
// Construct the JSON server
Service jsonService =
new LocalService(PropertyRemoteJdbcMetaFactory.getInstance(properties));
startServer(factory, jsonService, Serialization.JSON, null, null);
// Construct the Protobuf server
Service protobufService =
new LocalService(PropertyRemoteJdbcMetaFactory.getInstance(properties));
startServer(factory, protobufService, Serialization.PROTOBUF, null, null);
}
/**
* Starts Avatica servers for each serialization type with the provided {@code serverConfig}.
*/
public void startServers(AvaticaServerConfiguration serverConfig) {
final HandlerFactory factory = new HandlerFactory();
// Construct the JSON server
Service jsonService = new LocalService(FullyRemoteJdbcMetaFactory.getInstance());
startServer(factory, jsonService, Serialization.JSON, null, serverConfig);
// Construct the Protobuf server
Service protobufService = new LocalService(FullyRemoteJdbcMetaFactory.getInstance());
startServer(factory, protobufService, Serialization.PROTOBUF, null, serverConfig);
}
/**
* Starts Avatica server and cache.
*/
public void startServer(HandlerFactory factory, Service service, Serialization serialization,
MetricsSystemConfiguration metricsConfig,
AvaticaServerConfiguration serverConfig) {
AvaticaHandler handler = factory.getHandler(service, serialization,
metricsConfig, serverConfig);
final HttpServer server = new HttpServer.Builder().withHandler(handler)
.withPort(0).build();
server.start();
serversBySerialization.put(serialization, server);
}
/**
* Stops the servers currently running.
*
* @throws Exception If there is an error stopping a server
*/
public void stopServers() throws Exception {
Iterator<Entry<Serialization, HttpServer>> servers =
serversBySerialization.entrySet().iterator();
while (servers.hasNext()) {
try {
servers.next().getValue().stop();
} finally {
// Still remove it if we failed to stop it
servers.remove();
}
}
}
/**
* Computes an array of parameters to support JUnit's parameterized tests. The Object array
* actually contains a {@link Serialization} and the {@link HttpServer} instance in that order.
*
* @return A list of arrays of Serialization and HttpServer pairs.
*/
public List<Object[]> getJUnitParameters() {
List<Object[]> params = new ArrayList<>(serversBySerialization.size());
for (Entry<Serialization, HttpServer> servers : serversBySerialization.entrySet()) {
params.add(new Object[]{ servers.getKey(), servers.getValue() });
}
return params;
}
/**
* Computes the JDBC url for the Avatica server running on localhost, bound to the given port,
* and using the given serialization.
*
* @param port The port the Avatica server is listening on.
* @param serialization The serialization the Avatica server is using.
* @return A JDBC URL to the local Avatica server.
*/
public String getJdbcUrl(int port, Serialization serialization) {
return getJdbcUrl(port, serialization, "");
}
/**
* Computes the JDBC url for the Avatica server running on localhost, bound to the given port,
* using the given serialization, with the user-provided suffix for the HTTP URL to the server.
*
* @param port The port the Avatica server is listening on.
* @param serialization The serialization the Avatica server is using.
* @param urlSuffix Extra information to apend to the HTTP URL to the Avatica server (optional).
* @return A JDBC URL to the local Avatica server.
*/
public String getJdbcUrl(int port, Serialization serialization, String urlSuffix) {
return "jdbc:avatica:remote:url=http://localhost:" + port + urlSuffix + ";serialization="
+ serialization.name();
}
/** Factory that provides a {@link JdbcMeta}. */
public static class FullyRemoteJdbcMetaFactory implements Meta.Factory {
private static JdbcMeta instance = null;
static JdbcMeta getInstance() {
if (instance == null) {
try {
instance = new JdbcMeta(CONNECTION_SPEC.url, CONNECTION_SPEC.username,
CONNECTION_SPEC.password);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
return instance;
}
@Override public Meta create(List<String> args) {
return getInstance();
}
}
/** Factory that provides a {@link JdbcMeta} with properties. */
public static class PropertyRemoteJdbcMetaFactory implements Meta.Factory {
private static Map<Properties, JdbcMeta> instances = new HashMap<>();
static JdbcMeta getInstance(Properties properties) {
try {
if (properties == null) {
return new JdbcMeta(CONNECTION_SPEC.url, CONNECTION_SPEC.username,
CONNECTION_SPEC.password);
}
if (instances.get(properties) == null) {
properties.put("user", CONNECTION_SPEC.username);
properties.put("password", CONNECTION_SPEC.password);
JdbcMeta instance = new JdbcMeta(CONNECTION_SPEC.url, properties);
instances.put(properties, instance);
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
return instances.get(properties);
}
@Override public Meta create(List<String> args) {
return getInstance(new Properties());
}
}
}
// End AvaticaServersForTest.java