added ahCrawler and finished discovery endpoint

This commit is contained in:
2021-12-06 03:32:15 +01:00
parent ec26b84dc9
commit 560f1ff34b
15 changed files with 647 additions and 102 deletions

View File

@@ -2,6 +2,7 @@ package ch.unisg.tapas;
import ch.unisg.tapas.auctionhouse.adapter.common.clients.TapasMqttClient;
import ch.unisg.tapas.auctionhouse.adapter.in.messaging.mqtt.AuctionEventsMqttDispatcher;
import ch.unisg.tapas.auctionhouse.domain.AuctionHouseDiscovery;
import ch.unisg.tapas.auctionhouse.adapter.common.clients.WebSubSubscriber;
import ch.unisg.tapas.common.AuctionHouseResourceDirectory;
import ch.unisg.tapas.common.ConfigProperties;
@@ -16,6 +17,9 @@ import org.springframework.core.env.ConfigurableEnvironment;
import java.net.URI;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* Main TAPAS Auction House application.
@@ -24,7 +28,7 @@ import java.util.List;
public class TapasAuctionHouseApplication {
private static final Logger LOGGER = LogManager.getLogger(TapasAuctionHouseApplication.class);
public static String RESOURCE_DIRECTORY = "https://api.interactions.ics.unisg.ch/auction-houses/";
public static String RESOURCE_DIRECTORY = "http://localhost:3500";
public static String DEFAULT_MQTT_BROKER = "tcp://broker.hivemq.com:1883";
private static ConfigurableEnvironment ENVIRONMENT;
@@ -36,20 +40,23 @@ public class TapasAuctionHouseApplication {
// TODO Set start up of message services with config
// We will use these bootstrap methods in Week 6:
// bootstrapMarketplaceWithWebSub();
bootstrapMarketplaceWithWebSub();
bootstrapMarketplaceWithMqtt();
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
executor.scheduleAtFixedRate(crawlerRunnable, 30, 30, TimeUnit.SECONDS);
}
/**
* Discovers auction houses and subscribes to WebSub notifications
*/
private static void bootstrapMarketplaceWithWebSub() {
List<String> auctionHouseEndpoints = discoverAuctionHouseEndpoints();
discoverAuctionHouseEndpoints();
WebSubSubscriber subscriber = new WebSubSubscriber();
// WebSubSubscriber subscriber = new WebSubSubscriber();
for (String endpoint : auctionHouseEndpoints) {
subscriber.subscribeToAuctionHouseEndpoint(URI.create(endpoint));
}
// for (String endpoint : auctionHouseEndpoints) {
// subscriber.subscribeToAuctionHouseEndpoint(URI.create(endpoint));
// }
}
/**
@@ -74,11 +81,22 @@ public class TapasAuctionHouseApplication {
}
}
private static List<String> discoverAuctionHouseEndpoints() {
private static void discoverAuctionHouseEndpoints() {
AuctionHouseResourceDirectory rd = new AuctionHouseResourceDirectory(
URI.create(RESOURCE_DIRECTORY)
);
return rd.retrieveAuctionHouseEndpoints();
AuctionHouseDiscovery.getInstance().addAuctionHouseDiscoveryInformation(rd.retrieveAuctionHouseEndpoints());
}
private static Runnable crawlerRunnable = new Runnable() {
public void run() {
AuctionHouseResourceDirectory rd = new AuctionHouseResourceDirectory(
URI.create(RESOURCE_DIRECTORY)
);
AuctionHouseDiscovery.getInstance().addAuctionHouseDiscoveryInformation(rd.retrieveAuctionHouseEndpoints());
}
};
}

View File

@@ -1,6 +1,9 @@
package ch.unisg.tapas.auctionhouse.adapter.common.formats;
import ch.unisg.tapas.auctionhouse.domain.AuctionHouseDiscoveryInformation;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -8,7 +11,7 @@ import lombok.Getter;
import lombok.Setter;
public class AuctionHouseDiscoveryJsonRepresentation {
public static final String MEDIA_TYPE = "application/auctionhousediscovery + json";
public static final String MEDIA_TYPE = "application/auctionhousediscovery+json";
@Getter @Setter
private String auctionHouseUri;
@@ -17,7 +20,7 @@ public class AuctionHouseDiscoveryJsonRepresentation {
private String webSubUri;
@Getter @Setter
private String taskTypes;
private List<String> taskTypes;
@Getter @Setter
private String timeStamp;
@@ -28,10 +31,10 @@ public class AuctionHouseDiscoveryJsonRepresentation {
public AuctionHouseDiscoveryJsonRepresentation() {}
public AuctionHouseDiscoveryJsonRepresentation(AuctionHouseDiscoveryInformation auctionHouseDiscoveryInformation){
this.auctionHouseUri = auctionHouseDiscoveryInformation.getAuctionHouseUri().getValue();
this.webSubUri = auctionHouseDiscoveryInformation.getWebSubUri().getValue();
this.taskTypes = auctionHouseDiscoveryInformation.getTaskTypes().toString();
this.timeStamp = auctionHouseDiscoveryInformation.getTimeStamp().getValue();
this.auctionHouseUri = auctionHouseDiscoveryInformation.getAuctionHouseUri().getValue().toString();
this.webSubUri = auctionHouseDiscoveryInformation.getWebSubUri().getValue().toString();
this.taskTypes = auctionHouseDiscoveryInformation.getTaskTypes().getValue();
this.timeStamp = auctionHouseDiscoveryInformation.getTimeStamp().getValue().toString();
this.groupName = auctionHouseDiscoveryInformation.getGroupName().getValue();
}

View File

@@ -2,17 +2,21 @@ package ch.unisg.tapas.auctionhouse.adapter.in.web;
import ch.unisg.tapas.auctionhouse.adapter.common.formats.AuctionHouseDiscoveryJsonRepresentation;
import ch.unisg.tapas.auctionhouse.application.port.in.AuctionHouseDiscoveryUseCase;
import ch.unisg.tapas.auctionhouse.application.port.in.AuctionHouseDiscoveryCommand;
import ch.unisg.tapas.auctionhouse.domain.AuctionHouseDiscoveryInformation;
import java.util.List;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;
/**
* Controller that handles HTTP requests for the auction house discovery. This controller implements
@@ -27,30 +31,21 @@ public class AuctionHouseDiscoveryWebController {
}
@GetMapping(path="/discovery/", consumes = AuctionHouseDiscoveryJsonRepresentation.MEDIA_TYPE)
public ResponseEntity<String> auctionHouseDiscovery(@RequestBody AuctionHouseDiscoveryJsonRepresentation payload) {
AuctionHouseDiscoveryInformation.GroupName groupName = (payload.getGroupName() == null) ?
null : new AuctionHouseDiscoveryInformation.GroupName(payload.getGroupName());
public ResponseEntity<String> auctionHouseDiscovery() {
List<AuctionHouseDiscoveryInformation> auctionHouseDiscoveryInformation = auctionHouseDiscoveryUseCase.auctionHouseDiscovery();
AuctionHouseDiscoveryCommand command = new AuctionHouseDiscoveryCommand(
new AuctionHouseDiscoveryInformation.AuctionHouseUri(payload.getAuctionHouseUri()),
new AuctionHouseDiscoveryInformation.WebSubUri(payload.getAuctionHouseUri()),
new AuctionHouseDiscoveryInformation.TaskTypes(payload.getTaskTypes()),
new AuctionHouseDiscoveryInformation.TimeStamp(payload.getTimeStamp()),
groupName
);
ObjectMapper mapper = new ObjectMapper();
ObjectNode response = mapper.createObjectNode();
ArrayNode array = response.putArray("auctionHouseInfo");
AuctionHouseDiscoveryInformation auctionHouseDiscoveryInformation = auctionHouseDiscoveryUseCase.auctionHouseDiscovery(command);
try {
AuctionHouseDiscoveryJsonRepresentation representation = new AuctionHouseDiscoveryJsonRepresentation(auctionHouseDiscoveryInformation);
String auctionHouseDiscoveryJson = AuctionHouseDiscoveryJsonRepresentation.serialize(auctionHouseDiscoveryInformation);
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.add(HttpHeaders.CONTENT_TYPE, AuctionHouseDiscoveryJsonRepresentation.MEDIA_TYPE);
return new ResponseEntity<>(auctionHouseDiscoveryJson, responseHeaders, HttpStatus.OK);
} catch (JsonProcessingException e) {
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR);
for (AuctionHouseDiscoveryInformation info : auctionHouseDiscoveryInformation) {
array.add(mapper.valueToTree(new AuctionHouseDiscoveryJsonRepresentation(info)));
}
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.add(HttpHeaders.CONTENT_TYPE, AuctionHouseDiscoveryJsonRepresentation.MEDIA_TYPE);
return new ResponseEntity<>(response.toString(), responseHeaders, HttpStatus.OK);
}
}

View File

@@ -13,7 +13,6 @@ import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
@@ -22,6 +21,9 @@ public class AuctionHouseDiscoveryHttpAdapter implements AuctionHouseDiscoveryPo
private static final Logger LOGGER = LogManager.getLogger(AuctionHouseDiscoveryHttpAdapter.class);
public List<AuctionHouseDiscoveryInformation> fetchAuctionHouseInformation(URI auctionHouseURI){
System.out.println(auctionHouseURI);
try{
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()

View File

@@ -1,40 +0,0 @@
package ch.unisg.tapas.auctionhouse.application.port.in;
import ch.unisg.tapas.auctionhouse.domain.Auction;
import ch.unisg.tapas.auctionhouse.domain.AuctionHouseDiscoveryInformation;
import ch.unisg.tapas.common.SelfValidating;
import lombok.Value;
import javax.validation.constraints.NotNull;
@Value
public class AuctionHouseDiscoveryCommand extends SelfValidating<AuctionHouseDiscoveryCommand> {
@NotNull
private final AuctionHouseDiscoveryInformation.AuctionHouseUri auctionHouseUri;
@NotNull
private final AuctionHouseDiscoveryInformation.WebSubUri webSubUri;
@NotNull
private final AuctionHouseDiscoveryInformation.TaskTypes taskTypes;
@NotNull
private final AuctionHouseDiscoveryInformation.TimeStamp timeStamp;
//Didn't put @NotNull here since the GroupName is not required
private final AuctionHouseDiscoveryInformation.GroupName groupName;
public AuctionHouseDiscoveryCommand(AuctionHouseDiscoveryInformation.AuctionHouseUri auctionHouseUri,
AuctionHouseDiscoveryInformation.WebSubUri webSubUri,
AuctionHouseDiscoveryInformation.TaskTypes taskTypes,
AuctionHouseDiscoveryInformation.TimeStamp timeStamp,
AuctionHouseDiscoveryInformation.GroupName groupName) {
this.auctionHouseUri = auctionHouseUri;
this.webSubUri = webSubUri;
this.taskTypes = taskTypes;
this.timeStamp = timeStamp;
this.groupName = groupName;
this.validateSelf();
}
}

View File

@@ -1,8 +1,9 @@
package ch.unisg.tapas.auctionhouse.application.port.in;
import java.util.List;
import ch.unisg.tapas.auctionhouse.domain.AuctionHouseDiscoveryInformation;
public interface AuctionHouseDiscoveryUseCase {
AuctionHouseDiscoveryInformation auctionHouseDiscovery(AuctionHouseDiscoveryCommand command);
List<AuctionHouseDiscoveryInformation> auctionHouseDiscovery();
}

View File

@@ -0,0 +1,19 @@
package ch.unisg.tapas.auctionhouse.application.service;
import java.util.List;
import org.springframework.stereotype.Component;
import ch.unisg.tapas.auctionhouse.application.port.in.AuctionHouseDiscoveryUseCase;
import ch.unisg.tapas.auctionhouse.domain.AuctionHouseDiscovery;
import ch.unisg.tapas.auctionhouse.domain.AuctionHouseDiscoveryInformation;
@Component
public class AuctionHouseDiscoveryService implements AuctionHouseDiscoveryUseCase {
@Override
public List<AuctionHouseDiscoveryInformation> auctionHouseDiscovery() {
return AuctionHouseDiscovery.getInstance().getAuctionHouseDiscoveryList();
}
}

View File

@@ -0,0 +1,73 @@
package ch.unisg.tapas.auctionhouse.domain;
import java.net.URI;
import java.net.URISyntaxException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import lombok.Getter;
public class AuctionHouseDiscovery {
private static final AuctionHouseDiscovery auctionHouseDiscovery = new AuctionHouseDiscovery();
private final List<String> tasktypes = new ArrayList<String>() {
{
add("computation");
add("smallrobot");
}
};
@Getter
private List<AuctionHouseDiscoveryInformation> auctionHouseDiscoveryList = new ArrayList<>() {
};
private AuctionHouseDiscovery() {
try {
// Add our information to list
auctionHouseDiscoveryList.add(new AuctionHouseDiscoveryInformation(
new AuctionHouseDiscoveryInformation.AuctionHouseUri(new URI("http://localhost:8086")),
new AuctionHouseDiscoveryInformation.WebSubUri(new URI("http://localhost:8086/websub")),
new AuctionHouseDiscoveryInformation.TaskTypes(tasktypes),
new AuctionHouseDiscoveryInformation.TimeStamp(new Timestamp(new Date().getTime())),
new AuctionHouseDiscoveryInformation.GroupName("Group 1")
));
} catch (URISyntaxException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static AuctionHouseDiscovery getInstance() {
return auctionHouseDiscovery;
}
public List<AuctionHouseDiscoveryInformation> getAuctionHouseDiscoveryInformation() {
return auctionHouseDiscoveryList;
}
public void addAuctionHouseDiscoveryInformation(List<AuctionHouseDiscoveryInformation> informations) {
outerloop:
for (AuctionHouseDiscoveryInformation discovery : informations) {
for (AuctionHouseDiscoveryInformation endpoint : auctionHouseDiscoveryList) {
// Check if discovery is already in our discovery list
if (endpoint.getAuctionHouseUri().getValue().toString().equals(discovery.getAuctionHouseUri().getValue().toString())) {
// Check if the new discovery is more recent than the current
if (endpoint.getTimeStamp().getValue().before(discovery.getTimeStamp().getValue())) {
// Endpoint information is older. Remove and add the new discovery to the list
auctionHouseDiscoveryList.remove(endpoint);
break;
} else {
continue outerloop;
}
}
}
auctionHouseDiscoveryList.add(discovery);
}
}
}

View File

@@ -3,8 +3,13 @@ package ch.unisg.tapas.common;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import ch.unisg.tapas.auctionhouse.adapter.out.web.AuctionHouseDiscoveryHttpAdapter;
import ch.unisg.tapas.auctionhouse.application.port.out.AuctionHouseDiscoveryPort;
import ch.unisg.tapas.auctionhouse.domain.AuctionHouseDiscoveryInformation;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
@@ -26,32 +31,68 @@ public class AuctionHouseResourceDirectory {
this.rdEndpoint = rdEndpoint;
}
private AuctionHouseDiscoveryPort auctionHouseDiscoveryport = new AuctionHouseDiscoveryHttpAdapter();
private List<AuctionHouseDiscoveryInformation> auctionHouseEndpoints = new ArrayList<>();
// List to keep track of already fetched endpoints
private List<URI> fetchedEndpoints = new ArrayList<>();
/**
* Retrieves the endpoints of all auctions houses registered with this directory.
* @return
*/
public List<String> retrieveAuctionHouseEndpoints() {
List<String> auctionHouseEndpoints = new ArrayList<>();
public List<AuctionHouseDiscoveryInformation> retrieveAuctionHouseEndpoints() {
try {
HttpRequest request = HttpRequest.newBuilder()
.uri(rdEndpoint).GET().build();
fetchedEndpoints.clear();
auctionHouseEndpoints.clear();
HttpResponse<String> response = HttpClient.newBuilder().build()
.send(request, HttpResponse.BodyHandlers.ofString());
// For simplicity, here we just hard code the current representation used by our
// resource directory for auction houses
ObjectMapper objectMapper = new ObjectMapper();
JsonNode payload = objectMapper.readTree(response.body());
for (JsonNode node : payload) {
auctionHouseEndpoints.add(node.get("endpoint").asText());
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
fetchedEndpoints.add(rdEndpoint);
// Start recusive fetching of auctionhouses
fetchEndpoints(getInformation(rdEndpoint));
return auctionHouseEndpoints;
}
// Recursive function to fetch all endpoints
private void fetchEndpoints(List<AuctionHouseDiscoveryInformation> auctionHouses) {
for (AuctionHouseDiscoveryInformation ah : auctionHouses) {
if (!fetchedEndpoints.contains(ah.getAuctionHouseUri().getValue())) {
fetchedEndpoints.add(ah.getAuctionHouseUri().getValue());
fetchEndpoints(getInformation(ah.getAuctionHouseUri().getValue()));
}
}
}
private List<AuctionHouseDiscoveryInformation> getInformation(URI uri) {
List<AuctionHouseDiscoveryInformation> discoveries;
try {
discoveries = auctionHouseDiscoveryport.fetchAuctionHouseInformation(new URI(uri.toString() + "/discovery/"));
outerloop:
for (AuctionHouseDiscoveryInformation discovery : discoveries) {
for (AuctionHouseDiscoveryInformation endpoint : auctionHouseEndpoints) {
// Check if discovery is already in our endpoint list
if (endpoint.getAuctionHouseUri().getValue().toString().equals(discovery.getAuctionHouseUri().getValue().toString())) {
// Check if the new discovery is more recent than the current
if (endpoint.getTimeStamp().getValue().before(discovery.getTimeStamp().getValue())) {
// Endpoint information is older. Remove and add the new discovery to the list
auctionHouseEndpoints.remove(endpoint);
break;
} else {
continue outerloop;
}
}
}
auctionHouseEndpoints.add(discovery);
}
return discoveries;
} catch (URISyntaxException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// When an error happens return empty list
return new ArrayList<AuctionHouseDiscoveryInformation>();
}
}