/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.cluster.integration;
import org.apache.commons.lang.SerializationUtils;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.CoverageInfo;
import org.geoserver.catalog.CoverageStoreInfo;
import org.geoserver.catalog.DataStoreInfo;
import org.geoserver.catalog.FeatureTypeInfo;
import org.geoserver.catalog.Info;
import org.geoserver.catalog.LayerGroupInfo;
import org.geoserver.catalog.LayerInfo;
import org.geoserver.catalog.NamespaceInfo;
import org.geoserver.catalog.StyleInfo;
import org.geoserver.catalog.WMSLayerInfo;
import org.geoserver.catalog.WMSStoreInfo;
import org.geoserver.catalog.WorkspaceInfo;
import org.geoserver.catalog.impl.ModificationProxy;
import org.geoserver.config.GeoServer;
import org.geoserver.config.GeoServerInfo;
import org.geoserver.config.LoggingInfo;
import org.geoserver.config.ServiceInfo;
import org.geoserver.config.SettingsInfo;
import org.geoserver.ows.util.ClassProperties;
import org.geoserver.ows.util.OwsUtils;
import java.io.Serializable;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
/**
* Contains some util methods used in JMS integration tests.
*/
public final class IntegrationTestsUtils {
private static final Logger LOGGER = Logger.getLogger(IntegrationTestsUtils.class.getName());
private IntegrationTestsUtils() {
}
/**
* Equalizes all GeoServer instances to the first instance.
*/
public static void equalizeInstances(GeoServerInstance... instances) {
if (instances.length <= 1) {
// only one instance, so nothing to equalize
return;
}
// get the instance that will be used as reference
GeoServerInstance instance = instances[0];
for (int i = 1; i < instances.length; i++) {
// equalize instance to the reference instance
equalize(instance, instances[i]);
}
}
/**
* Equalizes two GeoServer instances, the first instance will be used as reference.
*/
public static void equalize(GeoServerInstance instanceA, GeoServerInstance instanceB) {
// get the differences between the two instances
List<InfoDiff> differences = differences(instanceA, instanceB);
// equalize each difference
for (InfoDiff difference : differences) {
Info infoA = difference.getInfoA();
Info infoB = difference.getInfoB();
if (infoA == null) {
// this info doesn't exists in the reference instance, so it needs to be removed
remove(instanceB.getGeoServer(), instanceB.getCatalog(), infoB);
} else if (infoB == null) {
// this info exists only in the reference instance so it needs to be added
add(instanceB.getGeoServer(), instanceB.getCatalog(), (Info) SerializationUtils.clone(infoA));
} else {
// this info exists in both instances but is different
save(instanceB.getGeoServer(), instanceB.getCatalog(),
ModificationProxy.unwrap(infoA), ModificationProxy.unwrap(infoB));
}
}
}
/**
* Helper method that checks that provided instances are the same.
* The first instance is used as reference.
*/
public static void checkNoDifferences(GeoServerInstance... instances) {
if (instances.length <= 1) {
// only one instance, so noting to check
return;
}
// get the reference instance
GeoServerInstance instance = instances[0];
for (int i = 1; i < instances.length; i++) {
// get differences between this instance and the reference instance
List<InfoDiff> differences = differences(instance, instances[i]);
assertThat(differences.size(), is(0));
}
}
/**
* Returns the catalog and configuration differences between two GeoServer instance.
*/
public static List<InfoDiff> differences(GeoServerInstance instanceA, GeoServerInstance instanceB) {
List<InfoDiff> differences = new ArrayList<>();
// get catalog differences
differences.addAll(catalogDifferences(instanceA, instanceB));
// get configuration differences
differences.addAll(configurationDifferences(instanceA, instanceB));
return differences;
}
/**
* Returns the catalog differences between two GeoServer instances.
*/
public static List<InfoDiff> catalogDifferences(GeoServerInstance instanceA, GeoServerInstance instanceB) {
// instantiate the differences visitor
CatalogDiffVisitor visitor = new CatalogDiffVisitor(instanceB.getCatalog(),
instanceA.getDataDirectory(), instanceB.getDataDirectory());
// visit the two catalogs
instanceA.getCatalog().accept(visitor);
// return the found differences
return visitor.differences();
}
/**
* Return the configuration differences between two GeoServer instances.
*/
public static List<InfoDiff> configurationDifferences(GeoServerInstance instanceA, GeoServerInstance instanceB) {
ConfigurationDiffVisitor visitor = new ConfigurationDiffVisitor(
instanceA.getGeoServer(), instanceB.getGeoServer());
return visitor.differences();
}
/**
* Helper method that just reset the JMS configuration of the provided GeoServe instances.
*/
public static void resetJmsConfiguration(GeoServerInstance... instances) {
Arrays.stream(instances).forEach(GeoServerInstance::setJmsDefaultConfiguration);
}
/**
* Helper method that just reset the count of consumed events of the provided GeoServe instances.
*/
public static void resetEventsCount(GeoServerInstance... instances) {
Arrays.stream(instances).forEach(GeoServerInstance::resetConsumedEventsCount);
}
/**
* Equalizes the provided infos using info A as reference.
*/
private static void save(GeoServer geoServer, Catalog catalog, Info infoA, Info infoB) {
if (infoA instanceof WorkspaceInfo) {
catalog.save(updateInfoImpl(infoA, infoB, WorkspaceInfo.class));
} else if (infoA instanceof NamespaceInfo) {
catalog.save(updateInfoImpl(infoA, infoB, NamespaceInfo.class));
} else if (infoA instanceof DataStoreInfo) {
catalog.save(updateInfoImpl(infoA, infoB, DataStoreInfo.class));
} else if (infoA instanceof CoverageStoreInfo) {
catalog.save(updateInfoImpl(infoA, infoB, CoverageStoreInfo.class));
} else if (infoA instanceof WMSStoreInfo) {
catalog.save(updateInfoImpl(infoA, infoB, WMSStoreInfo.class));
} else if (infoA instanceof FeatureTypeInfo) {
catalog.save(updateInfoImpl(infoA, infoB, FeatureTypeInfo.class));
} else if (infoA instanceof CoverageInfo) {
catalog.save(updateInfoImpl(infoA, infoB, CoverageInfo.class));
} else if (infoA instanceof LayerInfo) {
catalog.save(updateInfoImpl(infoA, infoB, LayerInfo.class));
} else if (infoA instanceof StyleInfo) {
catalog.save(updateInfoImpl(infoA, infoB, StyleInfo.class));
} else if (infoA instanceof LayerGroupInfo) {
catalog.save(updateInfoImpl(infoA, infoB, LayerGroupInfo.class));
} else if (infoA instanceof WMSLayerInfo) {
catalog.save(updateInfoImpl(infoA, infoB, WMSLayerInfo.class));
} else if (infoA instanceof SettingsInfo) {
geoServer.save(updateInfoImpl(infoA, infoB, SettingsInfo.class));
} else if (infoA instanceof ServiceInfo) {
geoServer.save(updateInfoImpl(infoA, infoB, ServiceInfo.class));
} else if (infoA instanceof LoggingInfo) {
geoServer.save(updateInfoImpl(infoA, infoB, LoggingInfo.class));
} else if (infoA instanceof GeoServerInfo) {
geoServer.save(updateInfoImpl(infoA, infoB, GeoServerInfo.class));
} else {
throw new RuntimeException(String.format(
"Don't know how to handle info of type '%s'.",
infoA.getClass().getSimpleName()));
}
}
/**
* Updates the second info values using the first info values. Values are cloned when possible.
*/
private static <U> U updateInfoImpl(Info infoA, Info infoB, Class<U> type) {
// make sure that we are dealing with infos that are compatible
if (!type.isAssignableFrom(infoA.getClass())
|| !type.isAssignableFrom(infoB.getClass())) {
throw new RuntimeException(String.format(
"Info objects should be of type '%s', but are of types '%s' and '%s'.",
type.getSimpleName(), infoA.getClass().getSimpleName(), infoB.getClass().getSimpleName()));
}
// create a modification proxy for the second info
U proxy = ModificationProxy.create(type.cast(infoB), type);
// get infos properties
ClassProperties properties = OwsUtils.getClassProperties(type);
// update every property of the second info using first info value
for (String propertyName : properties.properties()) {
try {
// get first info value for the current property
Object propertyValue = OwsUtils.get(infoA, propertyName);
if (propertyValue instanceof Info) {
// we are dealing with an info object, check that both properties are compatible
Object otherPropertyValue = OwsUtils.get(infoB, propertyName);
if (otherPropertyValue instanceof Info) {
// recursively update this info
propertyValue = updateInfoImpl(
(Info) propertyValue, (Info) otherPropertyValue, getInfoInterface(propertyValue.getClass()));
}
}
// if the property value is not a info clone it if possible
if (propertyValue instanceof Serializable && !(propertyValue instanceof Proxy)) {
propertyValue = SerializationUtils.clone((Serializable) propertyValue);
}
// update second info value
OwsUtils.set(proxy, propertyName, propertyValue);
} catch (IllegalArgumentException exception) {
// ignore non existing property
LOGGER.log(Level.FINE, String.format(
"Error setting property '%s'.", propertyName), exception);
}
}
// return modification proxy of second info
return proxy;
}
/**
* Helper method that find the interface of an implementation.
*/
private static Class<?> getInfoInterface(Class<?> type) {
Class<?>[] classInterfaces = type.getInterfaces();
for (Class<?> classInterface : classInterfaces) {
if (Info.class.isAssignableFrom(classInterface)) {
// compatible interface found, let's use this one
return classInterface;
}
}
// no compatible interface found
return Info.class;
}
/**
* Add info object to the provided GeoServer instance.
*/
private static void add(GeoServer geoServer, Catalog catalog, Info info) {
if (info instanceof WorkspaceInfo) {
catalog.add((WorkspaceInfo) info);
} else if (info instanceof NamespaceInfo) {
catalog.add((NamespaceInfo) info);
} else if (info instanceof DataStoreInfo) {
catalog.add((DataStoreInfo) info);
} else if (info instanceof CoverageStoreInfo) {
catalog.add((CoverageStoreInfo) info);
} else if (info instanceof WMSStoreInfo) {
catalog.add((WMSStoreInfo) info);
} else if (info instanceof FeatureTypeInfo) {
catalog.add((FeatureTypeInfo) info);
} else if (info instanceof CoverageInfo) {
catalog.add((CoverageInfo) info);
} else if (info instanceof LayerInfo) {
catalog.add((LayerInfo) info);
} else if (info instanceof StyleInfo) {
catalog.add((StyleInfo) info);
} else if (info instanceof LayerGroupInfo) {
catalog.add((LayerGroupInfo) info);
} else if (info instanceof WMSLayerInfo) {
catalog.add((WMSLayerInfo) info);
} else if (info instanceof SettingsInfo) {
geoServer.add((SettingsInfo) info);
} else if (info instanceof ServiceInfo) {
geoServer.add((ServiceInfo) info);
} else {
throw new RuntimeException(String.format(
"Don't know how to handle info of type '%s'.",
info.getClass().getSimpleName()));
}
}
/**
* Remove info object from the provided GeoServer instance.
*/
private static void remove(GeoServer geoServer, Catalog catalog, Info info) {
if (info instanceof WorkspaceInfo) {
catalog.remove((WorkspaceInfo) info);
} else if (info instanceof NamespaceInfo) {
catalog.remove((NamespaceInfo) info);
} else if (info instanceof DataStoreInfo) {
catalog.remove((DataStoreInfo) info);
} else if (info instanceof CoverageStoreInfo) {
catalog.remove((CoverageStoreInfo) info);
} else if (info instanceof WMSStoreInfo) {
catalog.remove((WMSStoreInfo) info);
} else if (info instanceof FeatureTypeInfo) {
catalog.remove((FeatureTypeInfo) info);
} else if (info instanceof CoverageInfo) {
catalog.remove((CoverageInfo) info);
} else if (info instanceof LayerInfo) {
catalog.remove((LayerInfo) info);
} else if (info instanceof StyleInfo) {
catalog.remove((StyleInfo) info);
} else if (info instanceof LayerGroupInfo) {
catalog.remove((LayerGroupInfo) info);
} else if (info instanceof WMSLayerInfo) {
catalog.remove((WMSLayerInfo) info);
} else if (info instanceof SettingsInfo) {
geoServer.remove((SettingsInfo) info);
} else if (info instanceof ServiceInfo) {
geoServer.remove((ServiceInfo) info);
} else {
throw new RuntimeException(String.format(
"Don't know how to handle info of type '%s'.",
info.getClass().getSimpleName()));
}
}
}