/*
* Copyright 2014 Avanza Bank AB
*
* Licensed 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 com.avanza.astrix.integration.tests;
import static com.avanza.astrix.integration.tests.TestLunchRestaurantBuilder.lunchRestaurant;
import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.openspaces.core.GigaSpace;
import com.avanza.astrix.beans.core.AstrixBeanKey;
import com.avanza.astrix.beans.core.AstrixSettings;
import com.avanza.astrix.beans.registry.AstrixServiceRegistry;
import com.avanza.astrix.beans.registry.ServiceRegistryClient;
import com.avanza.astrix.beans.registry.ServiceRegistryExporterClient;
import com.avanza.astrix.beans.service.ServiceProperties;
import com.avanza.astrix.config.DynamicConfig;
import com.avanza.astrix.config.GlobalConfigSourceRegistry;
import com.avanza.astrix.config.MapConfigSource;
import com.avanza.astrix.context.AstrixConfigurer;
import com.avanza.astrix.context.AstrixContext;
import com.avanza.astrix.core.RemoteServiceInvocationException;
import com.avanza.astrix.core.ServiceUnavailableException;
import com.avanza.astrix.gs.test.util.PuConfigurers;
import com.avanza.astrix.gs.test.util.RunningPu;
import com.avanza.astrix.integration.tests.common.Ping;
import com.avanza.astrix.integration.tests.domain.api.GetLunchRestaurantRequest;
import com.avanza.astrix.integration.tests.domain.api.LunchRestaurant;
import com.avanza.astrix.integration.tests.domain.api.LunchService;
import com.avanza.astrix.integration.tests.domain.api.LunchServiceAsync;
import com.avanza.astrix.integration.tests.domain.api.LunchUtil;
import com.avanza.astrix.integration.tests.domain.apiruntime.feeder.InternalLunchFeeder;
import com.avanza.astrix.integration.tests.domain.pu.LunchApplicationDescriptor;
import com.avanza.astrix.integration.tests.domain2.api.LunchRestaurantGrader;
import com.avanza.astrix.integration.tests.domain2.apiruntime.PublicLunchFeeder;
import com.avanza.astrix.provider.component.AstrixServiceComponentNames;
import com.avanza.astrix.test.util.AstrixTestUtil;
import com.avanza.astrix.test.util.AutoCloseableRule;
import com.avanza.astrix.test.util.Poller;
import com.avanza.astrix.test.util.Probe;
/**
*
* @author Elias Lindholm (elilin)
*
*/
public class AstrixIntegrationTest {
@ClassRule
public static RunningPu serviceRegistrypu = PuConfigurers.partitionedPu("classpath:/META-INF/spring/service-registry-pu.xml")
.numberOfPrimaries(1)
.numberOfBackups(0)
.beanProperties("space", new Properties() {{
// Run lease-manager thread every 200 ms.
setProperty("space-config.lease_manager.expiration_time_interval", "200");
}})
.startAsync(true)
.configure();
private static MapConfigSource config = new MapConfigSource() {{
set(AstrixSettings.SERVICE_REGISTRY_URI, AstrixServiceComponentNames.GS_REMOTING + ":jini://*/*/service-registry-space?groups=" + serviceRegistrypu.getLookupGroupName());
set(AstrixSettings.SERVICE_REGISTRY_EXPORT_RETRY_INTERVAL, 250);
set(AstrixSettings.BEAN_BIND_ATTEMPT_INTERVAL, 250);
}};
@ClassRule
public static RunningPu lunchPu = PuConfigurers.partitionedPu("classpath:/META-INF/spring/lunch-pu.xml")
.numberOfPrimaries(1)
.numberOfBackups(0)
.contextProperty("configSourceId", GlobalConfigSourceRegistry.register(config))
.startAsync(true)
.configure();
@ClassRule
public static RunningPu lunchGraderPu = PuConfigurers.partitionedPu("classpath:/META-INF/spring/lunch-grader-pu.xml")
.numberOfPrimaries(1)
.numberOfBackups(0)
.contextProperty("configSourceId", GlobalConfigSourceRegistry.register(config))
.startAsync(true)
.configure();
@Rule
public AutoCloseableRule autoClosables = new AutoCloseableRule();
private LunchService lunchService;
private LunchUtil lunchUtil;
private LunchRestaurantGrader lunchRestaurantGrader;
private LunchServiceAsync asyncLunchService;
private PublicLunchFeeder publicLunchFeeder;
private AstrixContext astrix;
private ServiceRegistryClient serviceRegistryClient;
private Ping lunchPing;
private AstrixServiceRegistry serviceRegistry;
@Before
public void setup() throws Exception {
GigaSpace proxy = lunchPu.getClusteredGigaSpace();
proxy.clear(null);
AstrixConfigurer configurer = new AstrixConfigurer();
configurer.enableFaultTolerance(true);
configurer.set(AstrixSettings.BEAN_BIND_ATTEMPT_INTERVAL, 250);
configurer.setConfig(DynamicConfig.create(config));
configurer.setSubsystem("test-sub-system");
astrix = autoClosables.add(configurer.configure());
this.lunchService = astrix.getBean(LunchService.class);
this.lunchUtil = astrix.getBean(LunchUtil.class);
this.lunchRestaurantGrader = astrix.getBean(LunchRestaurantGrader.class);
this.asyncLunchService = astrix.getBean(LunchServiceAsync.class);
this.publicLunchFeeder = astrix.getBean(PublicLunchFeeder.class);
this.serviceRegistryClient = astrix.getBean(ServiceRegistryClient.class);
this.serviceRegistry = astrix.getBean(AstrixServiceRegistry.class);
this.lunchPing = astrix.getBean(Ping.class, "lunch-ping");
astrix.waitForBean(LunchService.class, 5000);
astrix.waitForBean(AstrixServiceRegistry.class, 5000);
astrix.waitForBean(LunchUtil.class, 5000);
astrix.waitForBean(LunchRestaurantGrader.class, 5000);
astrix.waitForBean(LunchServiceAsync.class, 5000);
astrix.waitForBean(PublicLunchFeeder.class, 5000);
astrix.waitForBean(AstrixServiceRegistry.class, 5000);
astrix.waitForBean(Ping.class, "lunch-ping", 5000);
}
@Test
public void routedRemotingRequest() throws Exception {
lunchService.addLunchRestaurant(lunchRestaurant().withName("Martins Green Room").build());
GetLunchRestaurantRequest request = new GetLunchRestaurantRequest();
request.setName("Martins Green Room");
LunchRestaurant r = lunchService.getLunchRestaurant(request);
assertEquals("Martins Green Room", r.getName());
}
@Test
public void partitionedRemotingRequest() throws Exception {
lunchService.addLunchRestaurant(lunchRestaurant().withName("Martins Green Room").build());
lunchService.addLunchRestaurant(lunchRestaurant().withName("McDonalds").build());
lunchService.addLunchRestaurant(lunchRestaurant().withName("Max Burger").build());
List<LunchRestaurant> r = lunchService.getLunchRestaurants("McDonalds", "Max Burger");
assertEquals(2, r.size());
assertThat(r, hasItem(restaurantWithName("McDonalds")));
assertThat(r, hasItem(restaurantWithName("Max Burger")));
// It should be possible to send "empty" requests
r = lunchService.getLunchRestaurants();
assertEquals(0, r.size());
}
@Test
public void requestToQualifiedService() throws Exception {
assertEquals("hi", lunchPing.ping("hi"));
}
@Test
public void testPuThatConsumesAnotherService() throws Exception {
lunchService.addLunchRestaurant(lunchRestaurant().withName("Martins Green Room").build());
lunchRestaurantGrader.grade("Martins Green Room", 2);
lunchRestaurantGrader.grade("Martins Green Room", 4);
assertEquals(3.0, lunchRestaurantGrader.getAvarageGrade("Martins Green Room"), 0.01D);
}
@Test
public void broadcastedRemotingRequest() throws Exception {
lunchService.addLunchRestaurant(lunchRestaurant().withName("Martins Green Room").build());
LunchRestaurant r = lunchService.suggestRandomLunchRestaurant("vegetarian");
assertEquals("Martins Green Room", r.getName());
}
@Test
public void routedRemotingRequest_throwsException() throws Exception {
try {
GetLunchRestaurantRequest request = new GetLunchRestaurantRequest();
request.setName("throwException"); // LunchServiceImpl is hard-coded to throw exception for this name.
lunchService.getLunchRestaurant(request);
} catch (RemoteServiceInvocationException e) {
assertEquals(IllegalArgumentException.class.getName(), e.getExceptionType());
assertThat(e.getMessage(), startsWith("Remote service threw exception, see server log for details. [java.lang.IllegalArgumentException: Illegal restaurant: throwException]"));
}
}
@Test
public void libraryUsageTest() throws Exception {
lunchService.addLunchRestaurant(lunchRestaurant().withName("Martins Green Room").withFoodType("vegetarian").build());
LunchRestaurant r = lunchUtil.suggestVegetarianRestaurant();
assertEquals("Martins Green Room", r.getName());
}
@Test
public void asyncService() throws Exception {
lunchService.addLunchRestaurant(lunchRestaurant().withName("Martins Green Room").build());
GetLunchRestaurantRequest request = new GetLunchRestaurantRequest();
request.setName("Martins Green Room");
Future<LunchRestaurant> f = asyncLunchService.getLunchRestaurant(request);
LunchRestaurant r = f.get(300, TimeUnit.MILLISECONDS);
assertEquals("Martins Green Room", r.getName());
}
@Test
public void itsOkToInvokeUnversionedServicesWithinSameSubSystem() throws Exception {
// Lunch feeder indirectly invokes "internal" service
publicLunchFeeder.addLunchRestaurant(lunchRestaurant().withName("Martins Green Room").build());
GetLunchRestaurantRequest request = new GetLunchRestaurantRequest();
request.setName("Martins Green Room");
Future<LunchRestaurant> f = asyncLunchService.getLunchRestaurant(request);
LunchRestaurant r = f.get(300, TimeUnit.MILLISECONDS);
assertEquals("Martins Green Room", r.getName());
}
@Test
public void leasesServices() throws Exception {
ServiceProperties properties = new ServiceProperties();
properties.setApi(FooService.class);
ServiceRegistryExporterClient exporterClient = new ServiceRegistryExporterClient(serviceRegistry, "test-sub-system" , "foo-app-instance-id");
exporterClient.register(FooService.class, properties, 1000);
ServiceProperties props = serviceRegistryClient.lookup(AstrixBeanKey.create(FooService.class));
assertNotNull("Expected properties to exists after registration", props);
assertEventually(AstrixTestUtil.serviceInvocationResult(new Supplier<Object>() {
public Object get() {
return serviceRegistryClient.lookup(AstrixBeanKey.create(FooService.class));
};
}, is(nullValue())));
}
@Test
public void usesSpaceApplicationDescriptorNameAsdefaultApplicationInstanceIdForProcessingUnits() throws Exception {
ServiceProperties serviceProperties = serviceRegistryClient.lookup(AstrixBeanKey.create(LunchService.class));
assertEquals(LunchApplicationDescriptor.class.getName(), serviceProperties.getProperties().get(ServiceProperties.APPLICATION_INSTANCE_ID));
}
@Test(expected = ServiceUnavailableException.class)
public void itsNotPossibleToBindToNonPublishedServiceBeansProvidedByOtherSubsystems() throws Exception {
astrix.getBean(InternalLunchFeeder.class).addLunchRestaurant(lunchRestaurant().build());;
}
private TypeSafeMatcher<LunchRestaurant> restaurantWithName(final String restaurantName) {
return new TypeSafeMatcher<LunchRestaurant>() {
@Override
public void describeTo(Description description) {
description.appendText("LunchRestaurant with name: " + restaurantName);
}
@Override
protected boolean matchesSafely(LunchRestaurant item) {
return item.getName().equals(restaurantName);
}
};
}
private void assertEventually(Probe probe) throws InterruptedException {
new Poller(10_000, 10).check(probe);
}
public interface FooService {
}
}