package org.hivedb.test; import org.junit.Assert; import org.apache.cxf.Bus; import org.apache.cxf.BusException; import org.apache.cxf.BusFactory; import org.apache.cxf.aegis.databinding.AegisDatabinding; import org.apache.cxf.binding.BindingFactoryManager; import org.apache.cxf.binding.soap.SoapBindingFactory; import org.apache.cxf.binding.soap.SoapTransportFactory; import org.apache.cxf.jaxws.JaxWsProxyFactoryBean; import org.apache.cxf.jaxws.JaxWsServerFactoryBean; import org.apache.cxf.transport.ConduitInitiatorManager; import org.apache.cxf.transport.DestinationFactoryManager; import org.apache.cxf.transport.local.LocalTransportFactory; import org.apache.cxf.wsdl.WSDLManager; import org.apache.cxf.wsdl11.WSDLManagerImpl; import org.hibernate.shards.strategy.access.SequentialShardAccessStrategy; import org.hivedb.Schema; import org.hivedb.configuration.EntityConfig; import org.hivedb.configuration.EntityIndexConfig; import org.hivedb.hibernate.ConfigurationReader; import org.hivedb.hibernate.HiveSessionFactory; import org.hivedb.hibernate.HiveSessionFactoryBuilderImpl; import org.hivedb.services.Service; import org.hivedb.services.ServiceContainer; import org.hivedb.services.ServiceResponse; import org.hivedb.util.Lists; import org.hivedb.util.classgen.*; import org.hivedb.util.database.HiveDbDialect; import org.hivedb.util.database.test.HiveTest; import org.hivedb.util.functional.*; import org.junit.Test; import org.junit.BeforeClass; import java.io.Serializable; import java.lang.reflect.Array; import java.lang.reflect.Method; import java.util.*; public abstract class ClassServiceTest<T,S> extends HiveTest { final HiveDbDialect DATABASE_DIALECT = HiveDbDialect.H2; protected Class<T> clazz; protected Class serviceClass; protected Class responseClass; protected Class containerClass; protected String serviceUrl; protected Service client; protected Service server; protected HiveSessionFactory factory; public ClassServiceTest(Class<T> clazz, Class serviceClass, Class responseClass, Class containerClass, String serviceUrl) { this.clazz = clazz; this.serviceClass = serviceClass; this.responseClass = responseClass; this.containerClass = containerClass; this.serviceUrl = serviceUrl; } public void setup() { factory = getSessionFactory(); server = createService(configurationReader); startServer(server); } protected abstract Service createService(ConfigurationReader reader); @Test public void saveAndRetrieve() throws Exception { Object instance = getPersistentInstance(); final EntityConfig entityConfig = config.getEntityConfig(clazz); validate(createServiceResponse(Arrays.asList(instance)), invoke(getClient(), "get", entityConfig.getId(instance)), Arrays.asList(new String[] {})); } protected ServiceResponse invokeWithArrayArgument(Service client, String methodName, T[] args) { try { Method method = client.getClass().getMethod(methodName, args.getClass()); return (ServiceResponse) method.invoke(client, new Object[] {args}); } catch (Exception e) { throw new RuntimeException(e); } } protected ServiceResponse invoke(Service client, String methodName, Object... args) { try { Collection<Class> argClasses = Transform.map(new Unary<Object, Class>() { public Class f(Object arg) { return (arg instanceof GeneratedImplementation) ? ((GeneratedImplementation)arg).retrieveUnderlyingInterface() : arg.getClass(); } }, Arrays.asList(args)); Class[] argClassArray = new Class[argClasses.size()]; argClasses.toArray(argClassArray); Method method = client.getClass().getMethod(methodName, argClassArray); return (ServiceResponse) method.invoke(client, args); } catch (ClassCastException e) { return null; // There's a mysterious Long to Long Cast exception here } catch (Exception e) { throw new RuntimeException(e); } } protected Object invokeDelete(Service client, String methodName, Object... args) { try { Collection<Class> argClasses = Transform.map(new Unary<Object, Class>() { public Class f(Object arg) { return (arg instanceof GeneratedImplementation) ? ((GeneratedImplementation)arg).retrieveUnderlyingInterface() : arg.getClass(); } }, Arrays.asList(args)); Class[] argClassArray = new Class[argClasses.size()]; argClasses.toArray(argClassArray); Method method = client.getClass().getMethod(methodName, argClassArray); return method.invoke(client, args); } catch (Exception e) { throw new RuntimeException(e); } } protected Integer invokeByCount(Service client, String methodName, Object... args) { try { Collection<Class> argClasses = Transform.map(new Unary<Object, Class>() { public Class f(Object arg) { return (arg instanceof GeneratedImplementation) ? ((GeneratedImplementation)arg).retrieveUnderlyingInterface() : arg.getClass(); } }, Arrays.asList(args)); Class[] argClassArray = new Class[argClasses.size()]; argClasses.toArray(argClassArray); Method method = client.getClass().getMethod(methodName, argClassArray); return (Integer) method.invoke(client, args); } catch (Exception e) { throw new RuntimeException(e); } } protected boolean invokeExists(Service client, String method, Object arg) { try { return (Boolean) client.getClass().getMethod(method, new Class[] {arg.getClass()}).invoke(client, new Object[] {arg}); } catch (Exception e) { throw new RuntimeException(e); } } @Test public void saveAll() throws Exception { final EntityConfig entityConfig = config.getEntityConfig(clazz); Collection<T> instances = Lists.newArrayList(); for(int i=0; i<5; i++) { final T instance = getInstance(); instances.add(instance); } Service s = getClient(); invokeWithArrayArgument(s, "saveAll", collectionToArray(clazz, instances)); for(Object original : instances) validate( createServiceResponse(Arrays.asList(original)), invoke(s, "get", entityConfig.getId(original)), Arrays.asList(new String[] {})); } @Test public void update() throws Exception { final EntityConfig entityConfig = config.getEntityConfig(clazz); Collection<T> instances = Lists.newArrayList(); for(int i=0; i<5; i++) { final T instance = getInstance(); instances.add(instance); } Service s = getClient(); invokeWithArrayArgument(s, "saveAll", collectionToArray(clazz, instances)); for(Object original : instances) { for (EntityIndexConfig entityIndexConfig :config.getEntityConfig(clazz).getEntityIndexConfigs()) { Object newValue = ReflectionTools.isCollectionProperty(clazz, entityIndexConfig.getPropertyName()) ? new GenerateInstanceCollection(ReflectionTools.getCollectionItemType(clazz, entityIndexConfig.getPropertyName()), 3).generate() : new GenerateInstance(entityIndexConfig.getIndexClass()).generate(); GeneratedInstanceInterceptor.setProperty(original, entityIndexConfig.getPropertyName(), newValue); } } invokeWithArrayArgument(s, "saveAll", collectionToArray(clazz, instances)); for(Object updated : instances) { validate( createServiceResponse(Arrays.asList(updated)), invoke(s, "get", entityConfig.getId(updated)), Arrays.asList(new String[] {})); } } @Test public void delete() throws Exception { Object instance = getPersistentInstance(); final EntityConfig entityConfig = config.getEntityConfig(clazz); Service s = getClient(); final Serializable id = entityConfig.getId(instance); validate(createServiceResponse(Arrays.asList(instance)), invoke(s, "get",id), Arrays.asList(new String[] {})); Assert.assertEquals(id, invokeDelete(s, "delete", id)); Assert.assertFalse(invokeExists(s, "exists", id)); } @Test public void exists() throws Exception { final EntityConfig entityConfig = config.getEntityConfig(clazz); Service s = getClient(); Assert.assertFalse(invokeExists(s,"exists",new GeneratePrimitiveValue<Object>((Class<Object>) entityConfig.getIdClass()).generate())); // Make sure that the service generates an empty response when fetching missing items Assert.assertEquals(0, invoke(s, "get", new GeneratePrimitiveValue<Object>((Class<Object>) entityConfig.getIdClass()).generate()).getContainers().size()); Object p = getPersistentInstance(); Assert.assertTrue(invokeExists(s, "exists", entityConfig.getId(p))); } @Test public void findByProperty() throws Exception { Object instance = getPersistentInstance(); final EntityConfig entityConfig = config.getEntityConfig(clazz); for (EntityIndexConfig entityIndexConfig : getFilteredEntityIndexConfigs(entityConfig)) { if (entityIndexConfig.getIndexValues(instance).size() == 0) continue; validate( createServiceResponse(Arrays.asList(instance)), invoke( client, "findByProperty", entityIndexConfig.getPropertyName(), Atom.getFirstOrThrow(entityIndexConfig.getIndexValues(instance)).toString()), Arrays.asList(new String[] {entityIndexConfig.getPropertyName()})); Assert.assertEquals( (Integer)1, invokeByCount( client, "getCountByProperty", entityIndexConfig.getPropertyName(), Atom.getFirstOrThrow(entityIndexConfig.getIndexValues(instance)).toString())); } } @Test public void findByProperties() throws Exception { final Object instance = getPersistentInstance(); final EntityConfig entityConfig = config.getEntityConfig(clazz); Service s = getClient(); Collection<EntityIndexConfig> entityIndexConfigs = getFilteredEntityIndexConfigs(entityConfig); RingIteratorable<EntityIndexConfig> entityIndexConfigIterator2 = makeEntityIndexConfigRingIterable(instance, entityIndexConfigs); RingIteratorable<EntityIndexConfig> entityIndexConfigIterator3 = makeEntityIndexConfigRingIterable(instance, entityIndexConfigs); entityIndexConfigIterator2.next(); entityIndexConfigIterator3.next(); entityIndexConfigIterator3.next(); for (EntityIndexConfig entityIndexConfig1 : entityIndexConfigs) { if (entityIndexConfig1.getIndexValues(instance).size() == 0) continue; EntityIndexConfig entityIndexConfig2 = entityIndexConfigIterator2.next(); EntityIndexConfig entityIndexConfig3 = entityIndexConfigIterator3.next(); validate( createServiceResponse(Arrays.asList(instance)), invoke( client, "findByTwoProperties", entityIndexConfig1.getPropertyName(), Atom.getFirstOrThrow(entityIndexConfig1.getIndexValues(instance)).toString(), entityIndexConfig2.getPropertyName(), Atom.getFirstOrThrow(entityIndexConfig2.getIndexValues(instance)).toString() ), Arrays.asList(new String[] {entityIndexConfig1.getPropertyName(), entityIndexConfig2.getPropertyName()})); validate( createServiceResponse(Arrays.asList(instance)), invoke( client, "findByThreeProperties", entityIndexConfig1.getPropertyName(), Atom.getFirstOrThrow(entityIndexConfig1.getIndexValues(instance)).toString(), entityIndexConfig2.getPropertyName(), Atom.getFirstOrThrow(entityIndexConfig2.getIndexValues(instance)).toString(), entityIndexConfig3.getPropertyName(), Atom.getFirstOrThrow(entityIndexConfig3.getIndexValues(instance)).toString() ), Arrays.asList(new String[] {entityIndexConfig1.getPropertyName(), entityIndexConfig2.getPropertyName(), entityIndexConfig3.getPropertyName()})); } } private RingIteratorable<EntityIndexConfig> makeEntityIndexConfigRingIterable( final Object instance, Collection<EntityIndexConfig> entityIndexConfigs) { return new RingIteratorable<EntityIndexConfig>(Filter.grep(new Predicate<EntityIndexConfig>() { public boolean f(EntityIndexConfig entityIndexConfig) { return entityIndexConfig.getIndexValues(instance).size() > 0; } }, entityIndexConfigs)); } // Filter out EntityIndexConfig types that don't work private Collection<EntityIndexConfig> getFilteredEntityIndexConfigs(EntityConfig entityConfig) { return Filter.grep(new Predicate<EntityIndexConfig>() { public boolean f(EntityIndexConfig entityIndexConfig) { return !entityIndexConfig.getIndexClass().equals(Date.class); // I can't figure out what format CXF likes }}, entityConfig.getEntityIndexConfigs()); } abstract protected Collection<Schema> getSchemata(); abstract protected List<Class<?>> getEntityClasses(); protected void validate(ServiceResponse expected, ServiceResponse actual, Collection<String> arguments) { Assert.assertEquals( String.format("Testing using the following arguments: %s", arguments), expected.getContainers().size(), actual.getContainers().size()); Map<Object, ServiceContainer> expectedMap = getInstanceHashCodeMap(expected); Map<Object, ServiceContainer> actualMap = getInstanceHashCodeMap(actual); validate(expectedMap, actualMap, arguments); } protected void contains(ServiceResponse expected, ServiceResponse actual, Collection<String> arguments) { Assert.assertTrue(String.format("Testing using the following arguments: %s", arguments), expected.getContainers().size() >= actual.getContainers().size()); Map<Object, ServiceContainer> expectedMap = getInstanceHashCodeMap(expected); Map<Object, ServiceContainer> actualMap = getInstanceHashCodeMap(actual); validate(expectedMap, actualMap, arguments); } private void validate(Map<Object, ServiceContainer> expectedMap, Map<Object, ServiceContainer> actualMap, Collection<String> arguments) { for(Object key : actualMap.keySet()) { Assert.assertTrue( String.format("Expected results did not contian a ServiceContainer with hashCode %s", key), expectedMap.containsKey(key)); validate(expectedMap.get(key), actualMap.get(key), arguments); } } protected void validate(ServiceContainer expected, ServiceContainer actual, Collection<String> arguments) { final EntityConfig entityConfig = config.getEntityConfig(clazz); Assert.assertEquals(expected.getVersion(), actual.getVersion()); Assert.assertEquals( ReflectionTools.getDifferingFields(expected.getInstance(), actual.getInstance(), (Class<Object>)clazz).toString(), expected.getInstance().hashCode(), actual.getInstance().hashCode()); Assert.assertEquals(String.format("Testing using the following arguments: %s", arguments), entityConfig.getId(expected.getInstance()), entityConfig.getId(actual.getInstance())); //Assert.assertEquals(expected.getInstance().getDescription(), actual.getInstance().getDescription()); } @BeforeClass public static void initializeSOAPLocalTransport() throws BusException { String[] transports = new String[]{ "http://cxf.apache.org/transports/local", "http://schemas.xmlsoap.org/soap/http", "http://schemas.xmlsoap.org/wsdl/soap/http" }; LocalTransportFactory local = new LocalTransportFactory(); local.setTransportIds(Arrays.asList(transports)); Bus bus = BusFactory.newInstance().createBus(); SoapBindingFactory bindingFactory = new SoapBindingFactory(); bindingFactory.setBus(bus); bus.getExtension(BindingFactoryManager.class).registerBindingFactory("http://schemas.xmlsoap.org/wsdl/soap/", bindingFactory); bus.getExtension(BindingFactoryManager.class).registerBindingFactory("http://schemas.xmlsoap.org/wsdl/soap/http", bindingFactory); DestinationFactoryManager dfm = bus.getExtension(DestinationFactoryManager.class); SoapTransportFactory soap = new SoapTransportFactory(); soap.setBus(bus); dfm.registerDestinationFactory("http://schemas.xmlsoap.org/wsdl/soap/", soap); dfm.registerDestinationFactory("http://schemas.xmlsoap.org/soap/", soap); dfm.registerDestinationFactory("http://cxf.apache.org/transports/local", soap); LocalTransportFactory localTransport = new LocalTransportFactory(); dfm.registerDestinationFactory("http://schemas.xmlsoap.org/soap/http", localTransport); dfm.registerDestinationFactory("http://schemas.xmlsoap.org/wsdl/soap/http", localTransport); dfm.registerDestinationFactory("http://cxf.apache.org/bindings/xformat", localTransport); dfm.registerDestinationFactory("http://cxf.apache.org/transports/local", localTransport); ConduitInitiatorManager extension = bus.getExtension(ConduitInitiatorManager.class); extension.registerConduitInitiator(LocalTransportFactory.TRANSPORT_ID, localTransport); extension.registerConduitInitiator("http://schemas.xmlsoap.org/wsdl/soap/", localTransport); extension.registerConduitInitiator("http://schemas.xmlsoap.org/soap/http", localTransport); extension.registerConduitInitiator("http://schemas.xmlsoap.org/soap/", localTransport); WSDLManagerImpl manager = new WSDLManagerImpl(); manager.setBus(bus); bus.setExtension(manager, WSDLManager.class); } public JaxWsServerFactoryBean startServer(Service service) { JaxWsServerFactoryBean sf = new JaxWsServerFactoryBean(); sf.setAddress(serviceUrl); sf.setServiceBean(service); sf.setServiceClass(service.getClass()); sf.setDataBinding(new AegisDatabinding()); sf.getServiceFactory().setDataBinding(new AegisDatabinding()); sf.create().start(); return sf; } protected Object getPersistentInstance() { Object obj = Atom.getFirstOrThrow(getPersistentInstanceAsServiceResponse().getContainers()); return ((ServiceContainer) obj).getInstance(); //return ((ServiceContainer)Atom.getFirstOrThrow(getPersistentInstanceAsServiceResponse().getContainers())).getInstance(); } protected ServiceResponse getPersistentInstanceAsServiceResponse() { Assert.assertTrue(ReflectionTools.doesImplementOrExtend(getClient().getClass(), serviceClass)); return invoke(getClient(), "save", getInstance()); } protected T getInstance() { return new GenerateInstance<T>(clazz).generate(); } protected Service getClient() { if(client == null) client = createClient(); return client; } public Service createClient() { JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean(); factory.setServiceClass(serviceClass); factory.setAddress(serviceUrl); factory.setDataBinding(new AegisDatabinding()); factory.getServiceFactory().setDataBinding(new AegisDatabinding()); return (Service)factory.create(); } protected Map<Object, ServiceContainer> getInstanceHashCodeMap(ServiceResponse expected) { final GenerateInstance<T> g = new GenerateInstance<T>(clazz); final EntityConfig entityConfig = config.getEntityConfig(clazz); return Transform.toMap( new Unary<ServiceContainer, Object>(){ public Object f(ServiceContainer item) { return entityConfig.getId(item.getInstance()); }}, new Unary<ServiceContainer, ServiceContainer>(){ public ServiceContainer f(ServiceContainer item) { return createServiceContainer(g.generateAndCopyProperties(item.getInstance()), item.getVersion()); }}, expected.getContainers()); } private HiveSessionFactory getSessionFactory() { return new HiveSessionFactoryBuilderImpl( config, getHive(), new SequentialShardAccessStrategy()); } public ServiceResponse createServiceResponse(Collection instances) { ServiceResponse serviceResponse = (ServiceResponse) GeneratedClassFactory.newInstance(responseClass); GeneratedInstanceInterceptor.setProperty(serviceResponse, "containers", Transform.map(new Unary<Object, ServiceContainer>(){ public ServiceContainer f(Object item) { return createServiceContainer(item, config.getEntityConfig(clazz).getVersion(item)); }}, instances)); return serviceResponse; } public ServiceContainer createServiceContainer(Object instance, Integer version) { ServiceContainer serviceContainer = (ServiceContainer) GeneratedClassFactory.newInstance(containerClass); GeneratedInstanceInterceptor.setProperty(serviceContainer, "instance", instance); GeneratedInstanceInterceptor.setProperty(serviceContainer, "version", version); return serviceContainer; } protected T[] collectionToArray(Class<T> clazz, Collection c) { T[] array = (T[]) Array.newInstance(clazz, c.size()); c.toArray(array); return array; } }