package cz.cuni.mff.d3s.been.benchmarkapi; import java.io.ByteArrayInputStream; import java.io.StringWriter; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeoutException; import javax.xml.bind.JAXBException; import org.xml.sax.SAXException; import cz.cuni.mff.d3s.been.core.benchmark.ResubmitHistory; import cz.cuni.mff.d3s.been.core.benchmark.ResubmitHistoryItem; import cz.cuni.mff.d3s.been.core.benchmark.Storage; import cz.cuni.mff.d3s.been.core.benchmark.StorageItem; import cz.cuni.mff.d3s.been.core.jaxb.BindingComposer; import cz.cuni.mff.d3s.been.core.jaxb.BindingParser; import cz.cuni.mff.d3s.been.core.jaxb.ConvertorException; import cz.cuni.mff.d3s.been.core.jaxb.XSD; import cz.cuni.mff.d3s.been.core.task.TaskContextDescriptor; import cz.cuni.mff.d3s.been.core.task.TaskContextState; import cz.cuni.mff.d3s.been.core.task.TaskContextStateInfo; import cz.cuni.mff.d3s.been.mq.MessagingException; import cz.cuni.mff.d3s.been.socketworks.NamedSockets; import cz.cuni.mff.d3s.been.socketworks.twoway.Reply; import cz.cuni.mff.d3s.been.socketworks.twoway.ReplyType; import cz.cuni.mff.d3s.been.task.checkpoints.CheckpointRequest; import cz.cuni.mff.d3s.been.task.checkpoints.CheckpointRequestType; import cz.cuni.mff.d3s.been.taskapi.CheckpointController; import cz.cuni.mff.d3s.been.util.JSONUtils; import cz.cuni.mff.d3s.been.util.JsonException; /** * This class serves as a communicator between the benchmark generator task and * the host runtime. It provides various requests that the generator needs. * * @author Kuba Brecka */ public class BenchmarkRequestor { /** checkpoint request helper */ private final CheckpointController checkpointController; /** * Default constructor, creates a new instance with the specified checkpoint * controller. * * @param checkpointController * the checkpoint controller to use */ private BenchmarkRequestor(CheckpointController checkpointController) { this.checkpointController = checkpointController; } /** * Create a benchmark checkpoint checkpointController instance * * @return A new instance * @throws MessagingException * When the checkpointController cannot be created */ public static BenchmarkRequestor create() throws MessagingException { final CheckpointController checkpointController = CheckpointController.create(NamedSockets.TASK_CHECKPOINT_0MQ.getConnection()); return new BenchmarkRequestor(checkpointController); } /** * Closes the current instance. * * @throws MessagingException * when a messaging error occurrs */ public void close() throws MessagingException { checkpointController.close(); } /** * Serializes the passed task context descriptor into XML string * representation. * * @param entry * the descriptor to serialize * @return serialized descriptor * @throws IllegalArgumentException * when the descriptor is invalid or not serializable */ public static String taskContextToXml(TaskContextDescriptor entry) throws IllegalArgumentException { BindingComposer<TaskContextDescriptor> composer; StringWriter writer; try { composer = XSD.TASK_CONTEXT_DESCRIPTOR.createComposer(TaskContextDescriptor.class); writer = new StringWriter(); composer.compose(entry, writer); } catch (SAXException | JAXBException e) { throw new IllegalArgumentException("TaskContextDescriptor can't be converted to XML", e); } return writer.toString(); } /** * Serialized the passed key-value storage into XML representation. * * @param storage * the storage to serialize * @return serialized storage */ public static String storageToXml(Map<String, String> storage) { Storage s = new Storage(); for (Map.Entry<String, String> entry : storage.entrySet()) { StorageItem i = new StorageItem(); i.setKey(entry.getKey()); i.setValue(entry.getValue()); s.getStorageItem().add(i); } BindingComposer<Storage> composer; StringWriter writer; try { composer = XSD.STORAGE.createComposer(Storage.class); writer = new StringWriter(); composer.compose(s, writer); } catch (SAXException | JAXBException e) { throw new IllegalArgumentException("Storage can't be converted to XML", e); } return writer.toString(); } /** * Deserializes the passed XML string into a key-value storage. * * @param xml * the XML string to deserialize * @return the deserialized key-value storage */ public static Map<String, String> storageFromXml(String xml) { Storage s; try { BindingParser<Storage> bindingComposer = XSD.STORAGE.createParser(Storage.class); s = bindingComposer.parse(new ByteArrayInputStream(xml.getBytes())); } catch (ConvertorException | JAXBException | SAXException e) { throw new IllegalArgumentException("Cannot parse Storage xml", e); } Map<String, String> map = new HashMap<>(); for (StorageItem i : s.getStorageItem()) { map.put(i.getKey(), i.getValue()); } return map; } /** * Deserializes the resubmit history collection from XML string * representation. * * @param xml * the XML string to deserialize * @return deserialized resubmit history */ public Collection<ResubmitHistoryItem> resubmitHistoryFromXml(String xml) { try { BindingParser<ResubmitHistory> parser = XSD.STORAGE.createParser(ResubmitHistory.class); ResubmitHistory history = parser.parse(new ByteArrayInputStream(xml.getBytes())); return history.getResubmitHistoryItem(); } catch (SAXException | JAXBException | ConvertorException e) { throw new IllegalArgumentException("Cannot parse resubmit history XML.", e); } } /** * Checks whether the requestor reply is valid. If yes, it simply returns, if * no, an exception is thrown. * * @param reply * the reply to check * @param value * the reply value to check * @throws TimeoutException * when the request timed out */ private void assertValidReply(Reply reply, String value) throws TimeoutException { if (reply.getReplyType() == ReplyType.ERROR) { if (value.equals("TIMEOUT")) { throw new TimeoutException(String.format("Request timed out.")); } else { throw new RuntimeException(String.format("Request failed.")); } } } /** * Performs a request that will submit the specified task context within the * specified benchmark. * * @param taskContextDescriptor * the descriptor to submit * @param benchmarkId * the benchmark ID under which the descriptor is to be submitted * @return ID of the newly submitted task context * @throws TimeoutException * when the request times out */ public String contextSubmit(TaskContextDescriptor taskContextDescriptor, String benchmarkId) throws TimeoutException { final CheckpointRequest request = new CheckpointRequest(CheckpointRequestType.CONTEXT_SUBMIT, benchmarkId, taskContextToXml(taskContextDescriptor)); final Reply reply = checkpointController.request(request); String value = reply.getValue(); assertValidReply(reply, value); return value; } /** * Performs a request that will wait until the specified context finishes. * * @param taskContextEntryId * the ID of the context to wait for * @return the final state of the context * @throws TimeoutException * when the request times out */ public TaskContextState contextWait(String taskContextEntryId) throws TimeoutException { CheckpointRequest request = new CheckpointRequest(CheckpointRequestType.CONTEXT_WAIT, "", taskContextEntryId); Reply reply = checkpointController.request(request); String value = reply.getValue(); assertValidReply(reply, value); return TaskContextState.valueOf(value); } /** * Performs a request that will persist the current key-value storage of the * generator task. * * @param benchmarkId * the benchmark ID of the current benchmark * @param storage * the key-value storage to store * @throws TimeoutException * when the request times out */ public void storagePersist(String benchmarkId, Map<String, String> storage) throws TimeoutException { CheckpointRequest request = new CheckpointRequest(CheckpointRequestType.STORAGE_PERSIST, benchmarkId, storageToXml(storage)); Reply reply = checkpointController.request(request); String value = reply.getValue(); assertValidReply(reply, value); } /** * Performs a request that will retrieve the current key-value storage of the * generator task. * * @param benchmarkId * the benchmark ID of the current benchmark * @return the key-value storage of the benchmark * @throws TimeoutException * when the request times out */ public Map<String, String> storageRetrieve(String benchmarkId) throws TimeoutException { CheckpointRequest request = new CheckpointRequest(CheckpointRequestType.STORAGE_RETRIEVE, benchmarkId, ""); Reply reply = checkpointController.request(request); String value = reply.getValue(); assertValidReply(reply, value); return storageFromXml(value); } /** * Performs a request that will retrieve the current resubmit history of the * benchmark * * @param benchmarkId * the ID of the benchmark of which the history should be retrieved * @return the resubmit history of the benchmark * @throws TimeoutException * when the request times out */ public Collection<ResubmitHistoryItem> resubmitHistoryRetrieve(String benchmarkId) throws TimeoutException { CheckpointRequest request = new CheckpointRequest(CheckpointRequestType.RESUBMIT_HISTORY_RETRIEVE, benchmarkId, ""); Reply reply = checkpointController.request(request); String value = reply.getValue(); assertValidReply(reply, value); return resubmitHistoryFromXml(value); } /** * Performs a request that will retrieve all currently contained task contexts * within the benchmark. * * @param benchmarkId * the ID of the benchmark * @return the list of all contained task contexts and their states * @throws TimeoutException * when the request times out * @throws JsonException * when the response is invalid or not parsable */ public TaskContextStateInfo containedContextsRetrieve(String benchmarkId) throws TimeoutException, JsonException { CheckpointRequest request = new CheckpointRequest(CheckpointRequestType.CONTAINED_CONTEXTS_RETRIEVE, benchmarkId, ""); Reply reply = checkpointController.request(request); String value = reply.getValue(); assertValidReply(reply, value); return JSONUtils.newInstance().deserialize(value, TaskContextStateInfo.class); } }