diff --git a/pom.xml b/pom.xml index 596ba00..fd9474d 100644 --- a/pom.xml +++ b/pom.xml @@ -83,6 +83,11 @@ + + org.slf4j + slf4j-api + 2.0.17 + com.google.guava guava diff --git a/remote/pom.xml b/remote/pom.xml index 6f847d2..924eaa8 100644 --- a/remote/pom.xml +++ b/remote/pom.xml @@ -45,6 +45,10 @@ + + org.slf4j + slf4j-api + com.google.guava guava diff --git a/remote/src/main/java/tools/vitruv/framework/remote/client/VitruvClient.java b/remote/src/main/java/tools/vitruv/framework/remote/client/VitruvClient.java index d76cd82..3c13ac1 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/client/VitruvClient.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/client/VitruvClient.java @@ -4,8 +4,9 @@ import tools.vitruv.framework.views.ViewTypeProvider; /** - * A Vitruvius client can remotely access the available {@link tools.vitruv.framework.views.ViewType}s of a Vitruvius instance and query - * {@link tools.vitruv.framework.views.ViewSelector}s in order to create remotely editable {@link tools.vitruv.framework.views.View}s. + * A Vitruvius client can remotely access the available {@link + * tools.vitruv.framework.views.ViewType}s of a Vitruvius instance and query {@link + * tools.vitruv.framework.views.ViewSelector}s in order to create remotely editable {@link + * tools.vitruv.framework.views.View}s. */ -public interface VitruvClient extends ViewTypeProvider, ViewProvider { -} +public interface VitruvClient extends ViewTypeProvider, ViewProvider {} diff --git a/remote/src/main/java/tools/vitruv/framework/remote/client/VitruvClientFactory.java b/remote/src/main/java/tools/vitruv/framework/remote/client/VitruvClientFactory.java index 01d8dda..789b7be 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/client/VitruvClientFactory.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/client/VitruvClientFactory.java @@ -1,54 +1,56 @@ package tools.vitruv.framework.remote.client; +import java.nio.file.Path; import tools.vitruv.framework.remote.client.impl.VitruvRemoteConnection; import tools.vitruv.framework.remote.common.DefaultConnectionSettings; -import java.nio.file.Path; - +/** A factory for creating {@link VitruvClient} instances to connect to a Vitruvius server. */ public class VitruvClientFactory { - /** - * Private constructor to prevent instantiation of this utility class. - * - * @throws UnsupportedOperationException if an appempt is made to instantiate this class. - */ - private VitruvClientFactory() { - throw new UnsupportedOperationException("VitruvClientFactory is a utility class and cannot be instantiated."); - } + /** + * Private constructor to prevent instantiation of this utility class. + * + * @throws UnsupportedOperationException if an appempt is made to instantiate this class. + */ + private VitruvClientFactory() { + throw new UnsupportedOperationException( + "VitruvClientFactory is a utility class and cannot be instantiated."); + } + + /** + * Creates a new {@link VitruvClient} using the given host name or IP address and the standard + * port of 8080. + * + * @param url The host name or IP address of the Vitruvius server. + * @param temp A non-existing or empty directory for temporary files. + * @return A {@link VitruvClient}. + */ + public static VitruvClient create(String url, Path temp) { + return create(url, DefaultConnectionSettings.STD_PORT, temp); + } - /** - * Creates a new {@link VitruvClient} using the given host name or IP address and the standard port of 8080. - * - * @param url The host name or IP address of the Vitruvius server. - * @param temp A non-existing or empty directory for temporary files. - * @return A {@link VitruvClient}. - */ - public static VitruvClient create(String url, Path temp) { - return create(url, DefaultConnectionSettings.STD_PORT, temp); - } + /** + * Creates a new {@link VitruvClient} using the given host name or IP address and port. + * + * @param hostOrIp The host name or IP address of the Vitruvius server. + * @param port Port of the Vitruvius server. + * @param temp A non-existing or empty directory for temporary files. + * @return A {@link VitruvClient}. + */ + public static VitruvClient create(String hostOrIp, int port, Path temp) { + return create(DefaultConnectionSettings.STD_PROTOCOL, hostOrIp, port, temp); + } - /** - * Creates a new {@link VitruvClient} using the given host name or IP address and port. - * - * @param hostOrIp The host name or IP address of the Vitruvius server. - * @param port Port of the Vitruvius server. - * @param temp A non-existing or empty directory for temporary files. - * @return A {@link VitruvClient}. - */ - public static VitruvClient create(String hostOrIp, int port, Path temp) { - return create(DefaultConnectionSettings.STD_PROTOCOL, hostOrIp, port, temp); - } - - /** - * Creates a new {@link VitruvClient} using the given protocol, host name or IP address, and port. - * - * @param protocol The protocol. - * @param hostOrIp The host name of IP address of the Vitruvius server. - * @param port Port of the Vitruvius server. - * @param temp A non-existing or empty directory for temporary files. - * @return A {@link VitruvClient}. - */ - public static VitruvClient create(String protocol, String hostOrIp, int port, Path temp) { - return new VitruvRemoteConnection(protocol, hostOrIp, port, temp); - } + /** + * Creates a new {@link VitruvClient} using the given protocol, host name or IP address, and port. + * + * @param protocol The protocol. + * @param hostOrIp The host name of IP address of the Vitruvius server. + * @param port Port of the Vitruvius server. + * @param temp A non-existing or empty directory for temporary files. + * @return A {@link VitruvClient}. + */ + public static VitruvClient create(String protocol, String hostOrIp, int port, Path temp) { + return new VitruvRemoteConnection(protocol, hostOrIp, port, temp); + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/client/exception/BadClientResponseException.java b/remote/src/main/java/tools/vitruv/framework/remote/client/exception/BadClientResponseException.java index 50f896f..3787ec2 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/client/exception/BadClientResponseException.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/client/exception/BadClientResponseException.java @@ -1,19 +1,37 @@ package tools.vitruv.framework.remote.client.exception; +/** Exception thrown when the client receives a bad response from the server. */ public class BadClientResponseException extends RuntimeException { - public BadClientResponseException() { - super(); - } - - public BadClientResponseException(String msg) { - super(msg); - } - - public BadClientResponseException(String msg, Throwable cause) { - super(msg, cause); - } - - public BadClientResponseException(Throwable cause) { - super(cause); - } + /** Creates a new BadClientResponseException. */ + public BadClientResponseException() { + super(); + } + + /** + * Creates a new BadClientResponseException with the given message. + * + * @param msg the message of the exception + */ + public BadClientResponseException(String msg) { + super(msg); + } + + /** + * Creates a new BadClientResponseException with the given message and cause. + * + * @param msg the message of the exception + * @param cause the cause of the exception + */ + public BadClientResponseException(String msg, Throwable cause) { + super(msg, cause); + } + + /** + * Creates a new BadClientResponseException with the given cause. + * + * @param cause the cause of the exception + */ + public BadClientResponseException(Throwable cause) { + super(cause); + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/client/exception/BadServerResponseException.java b/remote/src/main/java/tools/vitruv/framework/remote/client/exception/BadServerResponseException.java index 2fdb116..9350313 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/client/exception/BadServerResponseException.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/client/exception/BadServerResponseException.java @@ -1,34 +1,63 @@ package tools.vitruv.framework.remote.client.exception; +/** Exception thrown when the client receives a bad response from the server. */ public class BadServerResponseException extends RuntimeException { - private final int statusCode; - - public BadServerResponseException() { - super(); + private final int statusCode; + + /** Constructs a new BadServerResponseException with various options. */ + public BadServerResponseException() { + super(); this.statusCode = -1; - } - - public BadServerResponseException(String msg) { - super(msg); + } + + /** + * Constructs a new BadServerResponseException with the specified detail message. + * + * @param msg the detail message + */ + public BadServerResponseException(String msg) { + super(msg); this.statusCode = -1; - } - - public BadServerResponseException(String msg, int statusCode) { - super(msg); - this.statusCode = statusCode; - } - - public BadServerResponseException(String msg, Throwable cause) { - super(msg, cause); + } + + /** + * Constructs a new BadServerResponseException with the specified detail message and status code. + * + * @param msg the detail message + * @param statusCode the HTTP status code of the bad response + */ + public BadServerResponseException(String msg, int statusCode) { + super(msg); + this.statusCode = statusCode; + } + + /** + * Constructs a new BadServerResponseException with the specified detail message and cause. + * + * @param msg the detail message + * @param cause the cause of the exception + */ + public BadServerResponseException(String msg, Throwable cause) { + super(msg, cause); this.statusCode = -1; - } - - public BadServerResponseException(Throwable cause) { - super(cause); + } + + /** + * Constructs a new BadServerResponseException with the specified cause. + * + * @param cause the cause of the exception + */ + public BadServerResponseException(Throwable cause) { + super(cause); this.statusCode = -1; - } - - public int getStatusCode() { - return statusCode; - } + } + + /** + * Gets the HTTP status code associated with the bad response. + * + * @return the HTTP status code + */ + public int getStatusCode() { + return statusCode; + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/client/exception/package-info.java b/remote/src/main/java/tools/vitruv/framework/remote/client/exception/package-info.java index 9cf0f71..6e5bd0e 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/client/exception/package-info.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/client/exception/package-info.java @@ -1,4 +1,2 @@ -/** - * This package defines exceptions for the Vitruvius client. - */ +/** This package defines exceptions for the Vitruvius client. */ package tools.vitruv.framework.remote.client.exception; diff --git a/remote/src/main/java/tools/vitruv/framework/remote/client/impl/ChangeDerivingRemoteView.java b/remote/src/main/java/tools/vitruv/framework/remote/client/impl/ChangeDerivingRemoteView.java index bbb522c..cc8246f 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/client/impl/ChangeDerivingRemoteView.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/client/impl/ChangeDerivingRemoteView.java @@ -1,17 +1,18 @@ package tools.vitruv.framework.remote.client.impl; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; + +import edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceCopier; +import edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil; import java.util.Collection; import java.util.LinkedList; import java.util.Map; - import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; - -import edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceCopier; -import edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil; import tools.vitruv.change.atomic.hid.HierarchicalId; import tools.vitruv.change.composite.description.VitruviusChange; import tools.vitruv.change.composite.description.VitruviusChangeFactory; @@ -21,124 +22,133 @@ import tools.vitruv.framework.views.ViewType; import tools.vitruv.framework.views.changederivation.StateBasedChangeResolutionStrategy; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; - /** - * A {@link RemoteView} that derives changes based on the changed state of its resources and allows to propagate them - * back to the Vitruvius server using the {@link #commitChanges} method. + * A {@link RemoteView} that derives changes based on the changed state of its resources and allows + * to propagate them back to the Vitruvius server using the {@link #commitChanges} method. */ public class ChangeDerivingRemoteView implements CommittableView { - private final RemoteView base; - private final StateBasedChangeResolutionStrategy resolutionStrategy; - - private Map originalResourceMapping; - - ChangeDerivingRemoteView(RemoteView base, StateBasedChangeResolutionStrategy resolutionStrategy) { - checkArgument(base != null, "base must not be null"); - checkState(!base.isModified(), "view must not be modified"); - checkState(!base.isOutdated(), "view must not be outdated"); - checkArgument(resolutionStrategy != null, "resolution strategy must not be null"); - this.base = base; - this.resolutionStrategy = resolutionStrategy; - - initializeResourceMapping(base.viewSource); - } - - private void initializeResourceMapping(ResourceSet source) { - originalResourceMapping = ResourceCopier.copyViewResources(source.getResources(), - ResourceSetUtil.withGlobalFactories(new ResourceSetImpl())); - } - - @Override - public void close() { - base.close(); - } - - @Override - public Collection getRootObjects() { - return base.getRootObjects(); - } - - @Override - public boolean isModified() { - return base.isModified(); - } - - @Override - public boolean isOutdated() { - return base.isOutdated(); - } - - @Override - public void update() { - base.update(); - initializeResourceMapping(base.viewSource); - } - - @Override - public boolean isClosed() { - return base.isClosed(); - } - - @Override - public void registerRoot(EObject object, URI persistAt) { - base.registerRoot(object, persistAt); - } - - @Override - public void moveRoot(EObject object, URI newLocation) { - base.moveRoot(object, newLocation); - } - - @Override - public ViewSelection getSelection() { - return base.getSelection(); - } - - @Override - public ViewType getViewType() { - return base.getViewType(); - } - - @Override - public CommittableView withChangeRecordingTrait() { - return base.withChangeRecordingTrait(); - } - - @Override - public CommittableView withChangeDerivingTrait(StateBasedChangeResolutionStrategy changeResolutionStrategy) { - return base.withChangeDerivingTrait(changeResolutionStrategy); - } - - /** - * Commits the changes made to the view and its containing elements. - * - * @throws IllegalStateException if called on a closed view - * @see #isClosed() - * @see #commitChangesAndUpdate() - */ - @Override - public void commitChanges() { - base.checkNotClosed(); - var allChanges = new LinkedList>(); - base.viewSource.getResources().forEach(it -> { - var changes = findChanges(originalResourceMapping.get(it), it); - if (changes.getEChanges().isEmpty()) { + private final RemoteView base; + private final StateBasedChangeResolutionStrategy resolutionStrategy; + + private Map originalResourceMapping; + + /** + * Creates a new {@link ChangeDerivingRemoteView}. + * + * @param base the base remote view + * @param resolutionStrategy the strategy used to derive changes + */ + ChangeDerivingRemoteView(RemoteView base, StateBasedChangeResolutionStrategy resolutionStrategy) { + checkArgument(base != null, "base must not be null"); + checkState(!base.isModified(), "view must not be modified"); + checkState(!base.isOutdated(), "view must not be outdated"); + checkArgument(resolutionStrategy != null, "resolution strategy must not be null"); + this.base = base; + this.resolutionStrategy = resolutionStrategy; + + initializeResourceMapping(base.viewSource); + } + + private void initializeResourceMapping(ResourceSet source) { + originalResourceMapping = + ResourceCopier.copyViewResources( + source.getResources(), ResourceSetUtil.withGlobalFactories(new ResourceSetImpl())); + } + + @Override + public void close() { + base.close(); + } + + @Override + public Collection getRootObjects() { + return base.getRootObjects(); + } + + @Override + public boolean isModified() { + return base.isModified(); + } + + @Override + public boolean isOutdated() { + return base.isOutdated(); + } + + @Override + public void update() { + base.update(); + initializeResourceMapping(base.viewSource); + } + + @Override + public boolean isClosed() { + return base.isClosed(); + } + + @Override + public void registerRoot(EObject object, URI persistAt) { + base.registerRoot(object, persistAt); + } + + @Override + public void moveRoot(EObject object, URI newLocation) { + base.moveRoot(object, newLocation); + } + + @Override + public ViewSelection getSelection() { + return base.getSelection(); + } + + @Override + public ViewType getViewType() { + return base.getViewType(); + } + + @Override + public CommittableView withChangeRecordingTrait() { + return base.withChangeRecordingTrait(); + } + + @Override + public CommittableView withChangeDerivingTrait( + StateBasedChangeResolutionStrategy changeResolutionStrategy) { + return base.withChangeDerivingTrait(changeResolutionStrategy); + } + + /** + * Commits the changes made to the view and its containing elements. + * + * @throws IllegalStateException if called on a closed view + * @see #isClosed() + * @see #commitChangesAndUpdate() + */ + @Override + public void commitChanges() { + base.checkNotClosed(); + var allChanges = new LinkedList>(); + base.viewSource + .getResources() + .forEach( + it -> { + var changes = findChanges(originalResourceMapping.get(it), it); + if (changes.getEChanges().isEmpty()) { allChanges.add(changes); - } - }); - base.remoteConnection.propagateChanges(base.uuid, VitruviusChangeFactory.getInstance().createCompositeChange(allChanges)); - base.modified = false; - } - - private VitruviusChange findChanges(Resource oldState, Resource newState) { - if (oldState == null) { - return resolutionStrategy.getChangeSequenceForCreated(newState); - } else if (newState == null) { - return resolutionStrategy.getChangeSequenceForDeleted(oldState); - } else { - return resolutionStrategy.getChangeSequenceBetween(newState, oldState); - } - } + } + }); + base.remoteConnection.propagateChanges( + base.uuid, VitruviusChangeFactory.getInstance().createCompositeChange(allChanges)); + base.modified = false; + } + + private VitruviusChange findChanges(Resource oldState, Resource newState) { + if (oldState == null) { + return resolutionStrategy.getChangeSequenceForCreated(newState); + } else if (newState == null) { + return resolutionStrategy.getChangeSequenceForDeleted(oldState); + } else { + return resolutionStrategy.getChangeSequenceBetween(newState, oldState); + } + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/client/impl/ChangeRecordingRemoteView.java b/remote/src/main/java/tools/vitruv/framework/remote/client/impl/ChangeRecordingRemoteView.java index e4369ea..5d87bb8 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/client/impl/ChangeRecordingRemoteView.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/client/impl/ChangeRecordingRemoteView.java @@ -1,10 +1,11 @@ package tools.vitruv.framework.remote.client.impl; -import java.util.Collection; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; +import java.util.Collection; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; - import tools.vitruv.change.composite.description.TransactionalChange; import tools.vitruv.change.composite.description.VitruviusChangeResolverFactory; import tools.vitruv.change.composite.recording.ChangeRecorder; @@ -15,127 +16,134 @@ import tools.vitruv.framework.views.ViewType; import tools.vitruv.framework.views.changederivation.StateBasedChangeResolutionStrategy; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; - /** - * A {@link RemoteView} that records changes to its resources and allows to propagate them - * back to the Vitruvius server using the {@link #commitChanges} method. + * A {@link RemoteView} that records changes to its resources and allows to propagate them back to + * the Vitruvius server using the {@link #commitChanges} method. */ public class ChangeRecordingRemoteView implements CommittableView { - private final RemoteView base; - private ChangeRecorder changeRecorder; - - public ChangeRecordingRemoteView(RemoteView base) { - checkArgument(base != null, "base must not be null"); - checkState(!base.isModified(), "view must not be modified"); - this.base = base; - setupChangeRecorder(); - } - - private void setupChangeRecorder() { - changeRecorder = new ChangeRecorder(base.viewSource); - changeRecorder.addToRecording(base.viewSource); - changeRecorder.beginRecording(); - } - - @Override - public void close() { - if (!isClosed()) { - changeRecorder.close(); - } - base.close(); - } - - @Override - public Collection getRootObjects() { - return base.getRootObjects(); - } - - @Override - public boolean isModified() { - return base.isModified(); - } - - @Override - public boolean isOutdated() { - return base.isOutdated(); - } - - @Override - public void update() { - if (changeRecorder.isRecording()) { - changeRecorder.endRecording(); - } - changeRecorder.close(); - base.update(); - setupChangeRecorder(); - } - - @Override - public boolean isClosed() { - return base.isClosed(); - } - - @Override - public void registerRoot(EObject object, URI persistAt) { - base.registerRoot(object, persistAt); - } - - @Override - public void moveRoot(EObject object, URI newLocation) { - base.moveRoot(object, newLocation); - } - - @Override - public ViewSelection getSelection() { - return base.getSelection(); - } - - @Override - public ViewType getViewType() { - return base.getViewType(); - } - - - @Override - public CommittableView withChangeRecordingTrait() { - changeRecorder.close(); - return base.withChangeDerivingTrait(); - } - - @Override - public CommittableView withChangeDerivingTrait(StateBasedChangeResolutionStrategy changeResolutionStrategy) { - changeRecorder.close(); - return base.withChangeDerivingTrait(changeResolutionStrategy); - } - - /** - * Commits the changes made to the view and its containing elements. - * - * @throws IllegalStateException if called on a closed view - * @see #isClosed() - * @see #commitChangesAndUpdate() - */ - @Override - public void commitChanges() { - base.checkNotClosed(); - var recordedChange = changeRecorder.endRecording(); - var changeResolver = VitruviusChangeResolverFactory.forHierarchicalIds(base.viewSource); - var unresolvedChanges = changeResolver.assignIds(recordedChange); - base.remoteConnection.propagateChanges(base.uuid, unresolvedChanges); - base.modified = false; - changeRecorder.beginRecording(); - } - - public void commitChanges(Iterable userInputs) { - base.checkNotClosed(); - var recordedChange = changeRecorder.endRecording(); - var changeResolver = VitruviusChangeResolverFactory.forHierarchicalIds(base.viewSource); - var unresolvedChanges = changeResolver.assignIds(recordedChange); - ((TransactionalChange) unresolvedChanges).setUserInteractions(userInputs); - base.remoteConnection.propagateChanges(base.uuid, unresolvedChanges); - base.modified = false; - changeRecorder.beginRecording(); - } + private final RemoteView base; + private ChangeRecorder changeRecorder; + + /** + * Creates a new {@link ChangeRecordingRemoteView} based on the given {@link RemoteView}. + * + * @param base the base remote view + */ + public ChangeRecordingRemoteView(RemoteView base) { + checkArgument(base != null, "base must not be null"); + checkState(!base.isModified(), "view must not be modified"); + this.base = base; + setupChangeRecorder(); + } + + private void setupChangeRecorder() { + changeRecorder = new ChangeRecorder(base.viewSource); + changeRecorder.addToRecording(base.viewSource); + changeRecorder.beginRecording(); + } + + @Override + public void close() { + if (!isClosed()) { + changeRecorder.close(); + } + base.close(); + } + + @Override + public Collection getRootObjects() { + return base.getRootObjects(); + } + + @Override + public boolean isModified() { + return base.isModified(); + } + + @Override + public boolean isOutdated() { + return base.isOutdated(); + } + + @Override + public void update() { + if (changeRecorder.isRecording()) { + changeRecorder.endRecording(); + } + changeRecorder.close(); + base.update(); + setupChangeRecorder(); + } + + @Override + public boolean isClosed() { + return base.isClosed(); + } + + @Override + public void registerRoot(EObject object, URI persistAt) { + base.registerRoot(object, persistAt); + } + + @Override + public void moveRoot(EObject object, URI newLocation) { + base.moveRoot(object, newLocation); + } + + @Override + public ViewSelection getSelection() { + return base.getSelection(); + } + + @Override + public ViewType getViewType() { + return base.getViewType(); + } + + @Override + public CommittableView withChangeRecordingTrait() { + changeRecorder.close(); + return base.withChangeDerivingTrait(); + } + + @Override + public CommittableView withChangeDerivingTrait( + StateBasedChangeResolutionStrategy changeResolutionStrategy) { + changeRecorder.close(); + return base.withChangeDerivingTrait(changeResolutionStrategy); + } + + /** + * Commits the changes made to the view and its containing elements. + * + * @throws IllegalStateException if called on a closed view + * @see #isClosed() + * @see #commitChangesAndUpdate() + */ + @Override + public void commitChanges() { + base.checkNotClosed(); + var recordedChange = changeRecorder.endRecording(); + var changeResolver = VitruviusChangeResolverFactory.forHierarchicalIds(base.viewSource); + var unresolvedChanges = changeResolver.assignIds(recordedChange); + base.remoteConnection.propagateChanges(base.uuid, unresolvedChanges); + base.modified = false; + changeRecorder.beginRecording(); + } + + /** + * Commits the changes made to the view and its containing elements, along with the provided user. + * + * @param userInputs the user interactions associated with the changes + */ + public void commitChanges(Iterable userInputs) { + base.checkNotClosed(); + var recordedChange = changeRecorder.endRecording(); + var changeResolver = VitruviusChangeResolverFactory.forHierarchicalIds(base.viewSource); + var unresolvedChanges = changeResolver.assignIds(recordedChange); + ((TransactionalChange) unresolvedChanges).setUserInteractions(userInputs); + base.remoteConnection.propagateChanges(base.uuid, unresolvedChanges); + base.modified = false; + changeRecorder.beginRecording(); + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/client/impl/RemoteView.java b/remote/src/main/java/tools/vitruv/framework/remote/client/impl/RemoteView.java index 88a4a91..b476328 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/client/impl/RemoteView.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/client/impl/RemoteView.java @@ -1,7 +1,9 @@ package tools.vitruv.framework.remote.client.impl; -import java.util.Collection; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; +import java.util.Collection; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.notify.Notifier; import org.eclipse.emf.common.notify.impl.AdapterImpl; @@ -9,7 +11,6 @@ import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; - import tools.vitruv.framework.views.CommittableView; import tools.vitruv.framework.views.View; import tools.vitruv.framework.views.ViewSelection; @@ -17,210 +18,212 @@ import tools.vitruv.framework.views.ViewType; import tools.vitruv.framework.views.changederivation.StateBasedChangeResolutionStrategy; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; - /** - * A {@link View} which is a copy of a {@link View} from the VSUM of a Vitruvius - * server. - *

- * Actions performed on this remote view or to the original view can be - * synchronized via the network. This view uses - * a {@link VitruvRemoteConnection} to do so. + * A {@link View} which is a copy of a {@link View} from the VSUM of a Vitruvius server. + * + *

Actions performed on this remote view or to the original view can be synchronized via the + * network. This view uses a {@link VitruvRemoteConnection} to do so. */ public class RemoteView implements View { - private final ViewSelector selector; - - protected final String uuid; - protected final VitruvRemoteConnection remoteConnection; - - protected ResourceSet viewSource; - protected boolean modified = false; - - RemoteView(String uuid, ResourceSet viewSource, ViewSelector selector, VitruvRemoteConnection remoteConnection) { - checkArgument(uuid != null, "uuid must not be null"); - checkArgument(viewSource != null, "view source must not be null"); - checkArgument(remoteConnection != null, "remote connection must not be null"); - checkArgument(selector != null, "selector must not be null"); - this.uuid = uuid; - this.remoteConnection = remoteConnection; - this.viewSource = viewSource; - this.selector = selector; - - addChangeListeners(viewSource); - } - - /** - * Provides the root model elements of this view. - * - * @throws IllegalStateException If called on a closed view. - * @see View#isClosed() - */ - @Override - public Collection getRootObjects() { - checkNotClosed(); - return viewSource.getResources().stream().map(Resource::getContents).flatMap(Collection::stream).toList(); - } - - /** - * Checks whether the view was closed. Closed views cannot be used further. All - * methods may throw an {@link IllegalStateException}. - */ - @Override - public boolean isClosed() { - return remoteConnection.isViewClosed(uuid); - } - - /** - * Returns whether the view was modified. - */ - @Override - public boolean isModified() { - return modified; - } - - /** - * Returns whether the view is outdated, i.e., whether the underlying view - * sources have changed. - */ - @Override - public boolean isOutdated() { - return remoteConnection.isViewOutdated(uuid); - } - - /** - * Updates the view via the {@link VitruvRemoteConnection}, thus invalidating - * its previous state and now providing - * an updated view. This can only be done for an unmodified view. - * - * @throws UnsupportedOperationException If called on a modified view. - * @throws IllegalStateException If called on a closed view. - * @see #isClosed() - * @see #isModified() - */ - @Override - public void update() { - checkNotClosed(); - checkState(!isModified(), "cannot update from model when view is modified"); - removeChangeListeners(viewSource); - viewSource = remoteConnection.updateView(uuid); - modified = false; - addChangeListeners(viewSource); - } - - @Override - public void close() { - if (!isClosed()) { - remoteConnection.closeView(uuid); - viewSource.getResources().forEach(Resource::unload); - viewSource.getResources().clear(); - removeChangeListeners(viewSource); - } - } - - /** - * Persists the given object at the given {@link URI} and adds it as view root. - */ - @Override - public void registerRoot(EObject object, URI persistAt) { - checkNotClosed(); - checkArgument(object != null, "object must not be null"); - checkArgument(persistAt != null, "URI for root must not be null"); - viewSource.createResource(persistAt).getContents().add(object); - } - - /** - * Moves the given object to the given {@link URI}. The given {@link EObject} - * must already be a root object of the view, otherwise an - * {@link IllegalStateException} is thrown. - */ - @Override - public void moveRoot(EObject object, URI newLocation) { - checkNotClosed(); - checkArgument(object != null, "object to move must not be null"); - checkState(getRootObjects().contains(object), "view must contain element %s to move", object); - checkArgument(newLocation != null, "URI for new location of root must not be null"); - viewSource.getResources().stream() - .filter(it -> it.getContents().contains(object)) - .findFirst() - .ifPresent(resource -> resource.setURI(newLocation)); - } - - /** - * Returns the {@link ViewSelection} with which this view has been created. - */ - @Override - public ViewSelection getSelection() { - return selector; - } - - /** - * UNSUPPORTED AT THE MOMENT!! - */ - @Override - public ViewType getViewType() { - // The client has no knowledge which view type was used to create the remote - // view. - // Additionally, the client is not able to create views. - throw new UnsupportedOperationException(); - } - - /** - * Returns a {@link CommittableView} based on the view's configuration. - * Changes to commit are identified by recording any changes made to the view. - * - * @throws UnsupportedOperationException If called on a modified view. - * @throws IllegalStateException If called on a closed view. - * @see #isClosed() - * @see #isModified() - */ - @Override - public CommittableView withChangeRecordingTrait() { - checkNotClosed(); - return new ChangeRecordingRemoteView(this); - } - - /** - * Returns a {@link CommittableView} based on the view's configuration. - * Changes to commit are identified by comparing the current view state with its - * state from the last update. - * - * @param changeResolutionStrategy The change resolution strategy to use for - * view state comparison. Must not be - * null. - * @throws UnsupportedOperationException If called on a modified view. - * @throws IllegalStateException If called on a closed view. - * @see #isClosed() - * @see #isModified() - */ - @Override - public CommittableView withChangeDerivingTrait(StateBasedChangeResolutionStrategy changeResolutionStrategy) { - checkNotClosed(); - return new ChangeDerivingRemoteView(this, changeResolutionStrategy); - } - - void checkNotClosed() { - checkState(!isClosed(), "view is already closed"); - } - - private void addChangeListeners(Notifier notifier) { - notifier.eAdapters().add(new AdapterImpl() { - @Override - public void notifyChanged(Notification message) { + private final ViewSelector selector; + + protected final String uuid; + protected final VitruvRemoteConnection remoteConnection; + + protected ResourceSet viewSource; + protected boolean modified = false; + + /** + * Creates a new {@link RemoteView}. + * + * @param uuid the unique identifier of the view + * @param viewSource the resource set representing the view's content + * @param selector the view selector used to create this view + * @param remoteConnection the remote connection to the Vitruvius server + */ + RemoteView( + String uuid, + ResourceSet viewSource, + ViewSelector selector, + VitruvRemoteConnection remoteConnection) { + checkArgument(uuid != null, "uuid must not be null"); + checkArgument(viewSource != null, "view source must not be null"); + checkArgument(remoteConnection != null, "remote connection must not be null"); + checkArgument(selector != null, "selector must not be null"); + this.uuid = uuid; + this.remoteConnection = remoteConnection; + this.viewSource = viewSource; + this.selector = selector; + + addChangeListeners(viewSource); + } + + /** + * Provides the root model elements of this view. + * + * @throws IllegalStateException If called on a closed view. + * @see View#isClosed() + */ + @Override + public Collection getRootObjects() { + checkNotClosed(); + return viewSource.getResources().stream() + .map(Resource::getContents) + .flatMap(Collection::stream) + .toList(); + } + + /** + * Checks whether the view was closed. Closed views cannot be used further. All methods may throw + * an {@link IllegalStateException}. + */ + @Override + public boolean isClosed() { + return remoteConnection.isViewClosed(uuid); + } + + /** Returns whether the view was modified. */ + @Override + public boolean isModified() { + return modified; + } + + /** + * Returns whether the view is outdated, i.e., whether the underlying view sources have changed. + */ + @Override + public boolean isOutdated() { + return remoteConnection.isViewOutdated(uuid); + } + + /** + * Updates the view via the {@link VitruvRemoteConnection}, thus invalidating its previous state + * and now providing an updated view. This can only be done for an unmodified view. + * + * @throws UnsupportedOperationException If called on a modified view. + * @throws IllegalStateException If called on a closed view. + * @see #isClosed() + * @see #isModified() + */ + @Override + public void update() { + checkNotClosed(); + checkState(!isModified(), "cannot update from model when view is modified"); + removeChangeListeners(viewSource); + viewSource = remoteConnection.updateView(uuid); + modified = false; + addChangeListeners(viewSource); + } + + @Override + public void close() { + if (!isClosed()) { + remoteConnection.closeView(uuid); + viewSource.getResources().forEach(Resource::unload); + viewSource.getResources().clear(); + removeChangeListeners(viewSource); + } + } + + /** Persists the given object at the given {@link URI} and adds it as view root. */ + @Override + public void registerRoot(EObject object, URI persistAt) { + checkNotClosed(); + checkArgument(object != null, "object must not be null"); + checkArgument(persistAt != null, "URI for root must not be null"); + viewSource.createResource(persistAt).getContents().add(object); + } + + /** + * Moves the given object to the given {@link URI}. The given {@link EObject} must already be a + * root object of the view, otherwise an {@link IllegalStateException} is thrown. + */ + @Override + public void moveRoot(EObject object, URI newLocation) { + checkNotClosed(); + checkArgument(object != null, "object to move must not be null"); + checkState(getRootObjects().contains(object), "view must contain element %s to move", object); + checkArgument(newLocation != null, "URI for new location of root must not be null"); + viewSource.getResources().stream() + .filter(it -> it.getContents().contains(object)) + .findFirst() + .ifPresent(resource -> resource.setURI(newLocation)); + } + + /** Returns the {@link ViewSelection} with which this view has been created. */ + @Override + public ViewSelection getSelection() { + return selector; + } + + /** UNSUPPORTED AT THE MOMENT!!. */ + @Override + public ViewType getViewType() { + // The client has no knowledge which view type was used to create the remote + // view. + // Additionally, the client is not able to create views. + throw new UnsupportedOperationException(); + } + + /** + * Returns a {@link CommittableView} based on the view's configuration. Changes to commit are + * identified by recording any changes made to the view. + * + * @throws UnsupportedOperationException If called on a modified view. + * @throws IllegalStateException If called on a closed view. + * @see #isClosed() + * @see #isModified() + */ + @Override + public CommittableView withChangeRecordingTrait() { + checkNotClosed(); + return new ChangeRecordingRemoteView(this); + } + + /** + * Returns a {@link CommittableView} based on the view's configuration. Changes to commit are + * identified by comparing the current view state with its state from the last update. + * + * @param changeResolutionStrategy The change resolution strategy to use for view state + * comparison. Must not be null. + * @throws UnsupportedOperationException If called on a modified view. + * @throws IllegalStateException If called on a closed view. + * @see #isClosed() + * @see #isModified() + */ + @Override + public CommittableView withChangeDerivingTrait( + StateBasedChangeResolutionStrategy changeResolutionStrategy) { + checkNotClosed(); + return new ChangeDerivingRemoteView(this, changeResolutionStrategy); + } + + /** Checks that the view is not closed. */ + void checkNotClosed() { + checkState(!isClosed(), "view is already closed"); + } + + private void addChangeListeners(Notifier notifier) { + notifier + .eAdapters() + .add( + new AdapterImpl() { + @Override + public void notifyChanged(Notification message) { modified = true; - } - }); - - if (notifier instanceof ResourceSet resourceSet) { - resourceSet.getResources().forEach(this::addChangeListeners); - } else if (notifier instanceof Resource resource) { - resource.getContents().forEach(this::addChangeListeners); - } else if (notifier instanceof EObject eObject) { - eObject.eContents().forEach(this::addChangeListeners); - } - } + } + }); - private void removeChangeListeners(ResourceSet resourceSet) { - resourceSet.getAllContents().forEachRemaining(it -> it.eAdapters().clear()); + if (notifier instanceof ResourceSet resourceSet) { + resourceSet.getResources().forEach(this::addChangeListeners); + } else if (notifier instanceof Resource resource) { + resource.getContents().forEach(this::addChangeListeners); + } else if (notifier instanceof EObject eObject) { + eObject.eContents().forEach(this::addChangeListeners); } + } + + private void removeChangeListeners(ResourceSet resourceSet) { + resourceSet.getAllContents().forEachRemaining(it -> it.eAdapters().clear()); + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/client/impl/RemoteViewSelector.java b/remote/src/main/java/tools/vitruv/framework/remote/client/impl/RemoteViewSelector.java index 502f433..500fef0 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/client/impl/RemoteViewSelector.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/client/impl/RemoteViewSelector.java @@ -1,93 +1,114 @@ package tools.vitruv.framework.remote.client.impl; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emfcloud.jackson.resource.JsonResource; - import tools.vitruv.framework.views.ModifiableViewSelection; import tools.vitruv.framework.views.View; import tools.vitruv.framework.views.ViewSelection; import tools.vitruv.framework.views.ViewSelector; import tools.vitruv.framework.views.selection.ElementViewSelection; -import java.util.Collection; -import java.util.LinkedList; -import java.util.List; - /** - * A selector for selecting the elements to be represented in a view, but on the Vitruvius client itself. - * It is capable of acting as a builder for a view by providing an appropriate creation method, handling the remote connection. + * A selector for selecting the elements to be represented in a view, but on the Vitruvius client + * itself. It is capable of acting as a builder for a view by providing an appropriate creation + * method, handling the remote connection. */ public class RemoteViewSelector implements ViewSelector { - private final String uuid; - private final VitruvRemoteConnection remoteConnection; - private final ModifiableViewSelection viewSelection; + private final String uuid; + private final VitruvRemoteConnection remoteConnection; + private final ModifiableViewSelection viewSelection; - public RemoteViewSelector(String uuid, Resource selection, VitruvRemoteConnection remoteConnection) { - this.uuid = uuid; - this.remoteConnection = remoteConnection; - this.viewSelection = new ElementViewSelection(selection.getContents()); - } + /** + * Creates a new {@link RemoteViewSelector}. + * + * @param uuid the unique identifier of the view + * @param selection the selection defining the elements to be included in the view + * @param remoteConnection the remote connection to the Vitruvius server + */ + public RemoteViewSelector( + String uuid, Resource selection, VitruvRemoteConnection remoteConnection) { + this.uuid = uuid; + this.remoteConnection = remoteConnection; + this.viewSelection = new ElementViewSelection(selection.getContents()); + } - /** - * Creates a view by delegating the request to the Vitruvius server, performing the selection done by this selector. - * - * @return The created view. - */ - @Override - public View createView() { - return remoteConnection.getView(this); - } + /** + * Creates a view by delegating the request to the Vitruvius server, performing the selection done + * by this selector. + * + * @return The created view. + */ + @Override + public View createView() { + return remoteConnection.getView(this); + } - @Override - public boolean isValid() { - return true; - } + @Override + public boolean isValid() { + return true; + } - @Override - public ViewSelection getSelection() { - return viewSelection; - } + @Override + public ViewSelection getSelection() { + return viewSelection; + } - @Override - public Collection getSelectableElements() { - return viewSelection.getSelectableElements(); - } + @Override + public Collection getSelectableElements() { + return viewSelection.getSelectableElements(); + } - @Override - public boolean isSelected(EObject eObject) { - return viewSelection.isSelected(eObject); - } + @Override + public boolean isSelected(EObject eObject) { + return viewSelection.isSelected(eObject); + } - @Override - public boolean isSelectable(EObject eObject) { - return viewSelection.isSelectable(eObject); - } + @Override + public boolean isSelectable(EObject eObject) { + return viewSelection.isSelectable(eObject); + } - @Override - public void setSelected(EObject eObject, boolean selected) { - viewSelection.setSelected(eObject, selected); - } + @Override + public void setSelected(EObject eObject, boolean selected) { + viewSelection.setSelected(eObject, selected); + } - @Override - public boolean isViewObjectSelected(EObject eObject) { - return viewSelection.getSelectableElements().stream().anyMatch(it -> - EcoreUtil.equals(eObject, it) && viewSelection.isViewObjectSelected(it)); - } + @Override + public boolean isViewObjectSelected(EObject eObject) { + return viewSelection.getSelectableElements().stream() + .anyMatch(it -> EcoreUtil.equals(eObject, it) && viewSelection.isViewObjectSelected(it)); + } - String getUUID() { - return this.uuid; - } + /** + * Gets the UUID of the view to be created. + * + * @return The UUID. + */ + String getUUID() { + return this.uuid; + } - List getSelectionIds() { - var ids = new LinkedList(); - viewSelection.getSelectableElements().forEach(it -> { - if (viewSelection.isSelected(it)) { - var resource = (JsonResource) it.eResource(); + /** + * Gets the IDs of the selected elements in the selection. + * + * @return The list of IDs. + */ + List getSelectionIds() { + var ids = new LinkedList(); + viewSelection + .getSelectableElements() + .forEach( + it -> { + if (viewSelection.isSelected(it)) { + var resource = (JsonResource) it.eResource(); ids.add(resource.getID(it)); - } - }); - return ids; - } + } + }); + return ids; + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/client/impl/RemoteViewType.java b/remote/src/main/java/tools/vitruv/framework/remote/client/impl/RemoteViewType.java index 3e9e597..fd49ac5 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/client/impl/RemoteViewType.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/client/impl/RemoteViewType.java @@ -1,53 +1,63 @@ package tools.vitruv.framework.remote.client.impl; import org.eclipse.emf.ecore.EPackage; - import tools.vitruv.framework.views.ChangeableViewSource; import tools.vitruv.framework.views.ViewSelector; import tools.vitruv.framework.views.ViewType; /** - * A Vitruvius view type representing actual types on the virtual model, but is - * still capable of providing a view selector and allows creating - * views by querying the Vitruvius server. + * A Vitruvius view type representing actual types on the virtual model, but is still capable of + * providing a view selector and allows creating views by querying the Vitruvius server. */ public class RemoteViewType implements ViewType { - private final String name; - private final VitruvRemoteConnection remoteConnection; - private final EPackage metamodel; - - RemoteViewType(String name, VitruvRemoteConnection remoteConnection, EPackage metamodel) { - this.name = name; - this.remoteConnection = remoteConnection; - this.metamodel = metamodel; - } - - RemoteViewType(String name, VitruvRemoteConnection remoteConnection) { - this.name = name; - this.remoteConnection = remoteConnection; - this.metamodel = null; - } - - @Override - public String getName() { - return name; - } - - /** - * Returns the {@link ViewSelector} of the {@link ViewType}, which allows - * configuring views by delegating the request to the Vitruvius server. - * - * @param viewSource Ignored, can be null. - * @return A view selector for the view type represented by this remote view - * type. - */ - @Override - public ViewSelector createSelector(ChangeableViewSource viewSource) { - return remoteConnection.getSelector(name); - } - - @Override - public EPackage getMetamodel() { - return this.metamodel; - } + private final String name; + private final VitruvRemoteConnection remoteConnection; + private final EPackage metamodel; + + /** + * Creates a new RemoteViewType. + * + * @param name the name of the view type + * @param remoteConnection the remote connection to the Vitruvius server + * @param metamodel the metamodel of the view type + */ + RemoteViewType(String name, VitruvRemoteConnection remoteConnection, EPackage metamodel) { + this.name = name; + this.remoteConnection = remoteConnection; + this.metamodel = metamodel; + } + + /** + * Creates a new RemoteViewType without metamodel. + * + * @param name the name of the view type + * @param remoteConnection the remote connection to the Vitruvius server + */ + RemoteViewType(String name, VitruvRemoteConnection remoteConnection) { + this.name = name; + this.remoteConnection = remoteConnection; + this.metamodel = null; + } + + @Override + public String getName() { + return name; + } + + /** + * Returns the {@link ViewSelector} of the {@link ViewType}, which allows configuring views by + * delegating the request to the Vitruvius server. + * + * @param viewSource Ignored, can be null. + * @return A view selector for the view type represented by this remote view type. + */ + @Override + public ViewSelector createSelector(ChangeableViewSource viewSource) { + return remoteConnection.getSelector(name); + } + + @Override + public EPackage getMetamodel() { + return this.metamodel; + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/client/impl/VitruvRemoteConnection.java b/remote/src/main/java/tools/vitruv/framework/remote/client/impl/VitruvRemoteConnection.java index 831de65..463eb2b 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/client/impl/VitruvRemoteConnection.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/client/impl/VitruvRemoteConnection.java @@ -1,5 +1,7 @@ package tools.vitruv.framework.remote.client.impl; +import io.micrometer.core.instrument.Metrics; +import io.micrometer.core.instrument.Timer; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URI; @@ -17,8 +19,6 @@ import java.util.Optional; import java.util.stream.Stream; import org.eclipse.emf.ecore.resource.ResourceSet; -import io.micrometer.core.instrument.Metrics; -import io.micrometer.core.instrument.Timer; import tools.vitruv.change.atomic.root.InsertRootEObject; import tools.vitruv.change.composite.description.VitruviusChange; import tools.vitruv.change.utils.ProjectMarker; @@ -39,268 +39,333 @@ * server. This enables the ability to perform actions on this remote Vitruvius instance. */ public class VitruvRemoteConnection implements VitruvClient { - private static final String SUCCESS = "success"; - private static final String EXCEPTION = "exception"; - private static final String RESULT = "result"; - private static final String METHOD = "method"; - private static final String ENDPOINT = "endpoint"; - private static final String METRIC_CLIENT_NAME = "vitruv.client.rest.client"; - private final int port; - private final String hostOrIp; - private final String protocol; - private final HttpClient client; - private final JsonMapper mapper; + /** Constant for successful request result. */ + private static final String SUCCESS = "success"; + + /** Constant for exception during request processing. */ + private static final String EXCEPTION = "exception"; + + /** The constant result. */ + private static final String RESULT = "result"; + + /** The method constant. */ + private static final String METHOD = "method"; - /** - * Creates a new {@link VitruvRemoteConnection} using the given URL and port to connect to the - * Vitruvius server. - * - * @param protocol The protocol of the Vitruvius server. - * @param hostOrIp The host name of IP address of the Vitruvius server. - * @param port of the Vitruvius server. - */ - public VitruvRemoteConnection(String protocol, String hostOrIp, int port, Path temp) { - this.client = HttpClient.newHttpClient(); - this.protocol = protocol; - this.hostOrIp = hostOrIp; - this.port = port; + /** The endpoint constant. */ + private static final String ENDPOINT = "endpoint"; - try { - if (Files.notExists(temp) || (Files.isDirectory(temp) && isDirectoryEmpty(temp))) { - Files.createDirectories(temp); - ProjectMarker.markAsProjectRootFolder(temp); - } - } catch (IOException e) { - throw new IllegalArgumentException( - "Given temporary directory for models could not be created!", e); - } + /** The metric client name. */ + private static final String METRIC_CLIENT_NAME = "vitruv.client.rest.client"; - this.mapper = new JsonMapper(temp); + private final int port; + private final String hostOrIp; + private final String protocol; + private final HttpClient client; + private final JsonMapper mapper; + + /** + * Creates a new {@link VitruvRemoteConnection} using the given URL and port to connect to the + * Vitruvius server. + * + * @param protocol The protocol of the Vitruvius server. + * @param hostOrIp The host name of IP address of the Vitruvius server. + * @param port of the Vitruvius server. + */ + public VitruvRemoteConnection(String protocol, String hostOrIp, int port, Path temp) { + this.client = HttpClient.newHttpClient(); + this.protocol = protocol; + this.hostOrIp = hostOrIp; + this.port = port; + + try { + if (Files.notExists(temp) || (Files.isDirectory(temp) && isDirectoryEmpty(temp))) { + Files.createDirectories(temp); + ProjectMarker.markAsProjectRootFolder(temp); + } + } catch (IOException e) { + throw new IllegalArgumentException( + "Given temporary directory for models could not be created!", e); } - private boolean isDirectoryEmpty(Path directory) throws IOException { - try (Stream entries = Files.list(directory)) { - return entries.findAny().isEmpty(); - } + this.mapper = new JsonMapper(temp); + } + + private boolean isDirectoryEmpty(Path directory) throws IOException { + try (Stream entries = Files.list(directory)) { + return entries.findAny().isEmpty(); } + } - /** - * Returns a list of remote representations of {@link ViewType}s available at the Vitruvius - * server. - */ - @Override - public Collection> getViewTypes() { - var request = - HttpRequest.newBuilder().uri(createURIFrom(EndpointPath.VIEW_TYPES)).GET().build(); - try { - var response = sendRequest(request); - var typeNames = mapper.deserializeArrayOf(response.body(), String.class); - var list = new LinkedList>(); - typeNames.forEach(it -> list.add(new RemoteViewType(it, this))); - return list; - } catch (IOException e) { - throw new BadClientResponseException(e); - } + /** + * Returns a list of remote representations of {@link ViewType}s available at the Vitruvius + * server. + */ + @Override + public Collection> getViewTypes() { + var request = + HttpRequest.newBuilder().uri(createURIFrom(EndpointPath.VIEW_TYPES)).GET().build(); + try { + var response = sendRequest(request); + var typeNames = mapper.deserializeArrayOf(response.body(), String.class); + var list = new LinkedList>(); + typeNames.forEach(it -> list.add(new RemoteViewType(it, this))); + return list; + } catch (IOException e) { + throw new BadClientResponseException(e); } + } - /** - * Returns a view selector for the given {@link ViewType} by querying the selector from the - * Vitruvius server. The view type must be of type {@link RemoteViewType} as these represent the - * actual view types available at the server side. - * - * @param viewType The {@link ViewType} to create a selector for. - * @return A {@link ViewSelector} for the given view type. - * @throws IllegalArgumentException If view type is no {@link RemoteViewType}. - */ - @Override - public S createSelector(ViewType viewType) { - if (!(viewType instanceof RemoteViewType)) { - throw new IllegalArgumentException( - "This vitruv client can only process RemoteViewType!"); - } - return viewType.createSelector(null); + /** + * Returns a view selector for the given {@link ViewType} by querying the selector from the + * Vitruvius server. The view type must be of type {@link RemoteViewType} as these represent the + * actual view types available at the server side. + * + * @param viewType The {@link ViewType} to create a selector for. + * @return A {@link ViewSelector} for the given view type. + * @throws IllegalArgumentException If view type is no {@link RemoteViewType}. + */ + @Override + public S createSelector(ViewType viewType) { + if (!(viewType instanceof RemoteViewType)) { + throw new IllegalArgumentException("This vitruv client can only process RemoteViewType!"); } + return viewType.createSelector(null); + } - /** - * Queries the Vitruvius server to obtain a view selector from the view type with the given - * name. - * - * @param typeName The name of the view type. - * @return The selector generated with the view type of the given name. - * @throws BadServerResponseException If the server answered with a bad response or a connection - * error occurred. - * @throws NoSuchElementException If the response headers do not contain the expected selector - * UUID. - */ - RemoteViewSelector getSelector(String typeName) throws BadServerResponseException { - var request = HttpRequest.newBuilder().uri(createURIFrom(EndpointPath.VIEW_SELECTOR)) - .header(Header.VIEW_TYPE, typeName).GET().build(); - try { - var response = sendRequest(request); - var resource = mapper.deserializeResource(response.body(), JsonFieldName.TEMP_VALUE, - ResourceUtil.createJsonResourceSet()); - Optional selectorUuid = response.headers().firstValue(Header.SELECTOR_UUID); - if (selectorUuid.isPresent()) { - return new RemoteViewSelector(selectorUuid.get(), resource, this); - } else { - // Handle the case where the value is not present - throw new NoSuchElementException( - "Header.SELECTOR_UUID not found in response headers"); - } - } catch (IOException e) { - throw new BadClientResponseException(e); - } + /** + * Queries the Vitruvius server to obtain a view selector from the view type with the given name. + * + * @param typeName The name of the view type. + * @return The selector generated with the view type of the given name. + * @throws BadServerResponseException If the server answered with a bad response or a connection + * error occurred. + * @throws NoSuchElementException If the response headers do not contain the expected selector + * UUID. + */ + RemoteViewSelector getSelector(String typeName) throws BadServerResponseException { + var request = + HttpRequest.newBuilder() + .uri(createURIFrom(EndpointPath.VIEW_SELECTOR)) + .header(Header.VIEW_TYPE, typeName) + .GET() + .build(); + try { + var response = sendRequest(request); + var resource = + mapper.deserializeResource( + response.body(), JsonFieldName.TEMP_VALUE, ResourceUtil.createJsonResourceSet()); + Optional selectorUuid = response.headers().firstValue(Header.SELECTOR_UUID); + if (selectorUuid.isPresent()) { + return new RemoteViewSelector(selectorUuid.get(), resource, this); + } else { + // Handle the case where the value is not present + throw new NoSuchElementException("Header.SELECTOR_UUID not found in response headers"); + } + } catch (IOException e) { + throw new BadClientResponseException(e); } + } - /** - * Queries the Vitruvius server to obtain the view using the given view selector. - * - * @param selector The {@link tools.vitruv.framework.views.ViewSelector} which should be used to - * create the view. - * @return The view generated with the given view selector. - * @throws BadServerResponseException If the server answered with a bad response or a connection - * error occurred. - */ - RemoteView getView(RemoteViewSelector selector) throws BadServerResponseException { - try { - var request = HttpRequest.newBuilder().uri(createURIFrom(EndpointPath.VIEW)) - .header(Header.SELECTOR_UUID, selector.getUUID()) - .POST(BodyPublishers.ofString(mapper.serialize(selector.getSelectionIds()))) - .build(); - var response = sendRequest(request); - var rSet = mapper.deserialize(response.body(), ResourceSet.class); - Optional viewUuid = response.headers().firstValue(Header.VIEW_UUID); - if (viewUuid.isPresent()) { - return new RemoteView(viewUuid.get(), rSet, selector, this); - } else { - // Handle the case where the value is not present - throw new NoSuchElementException("Header.VIEW_UUID not found in response headers"); - } - } catch (IOException e) { - throw new BadClientResponseException(e); - } + /** + * Queries the Vitruvius server to obtain the view using the given view selector. + * + * @param selector The {@link tools.vitruv.framework.views.ViewSelector} which should be used to + * create the view. + * @return The view generated with the given view selector. + * @throws BadServerResponseException If the server answered with a bad response or a connection + * error occurred. + */ + RemoteView getView(RemoteViewSelector selector) throws BadServerResponseException { + try { + var request = + HttpRequest.newBuilder() + .uri(createURIFrom(EndpointPath.VIEW)) + .header(Header.SELECTOR_UUID, selector.getUUID()) + .POST(BodyPublishers.ofString(mapper.serialize(selector.getSelectionIds()))) + .build(); + var response = sendRequest(request); + var rSet = mapper.deserialize(response.body(), ResourceSet.class); + Optional viewUuid = response.headers().firstValue(Header.VIEW_UUID); + if (viewUuid.isPresent()) { + return new RemoteView(viewUuid.get(), rSet, selector, this); + } else { + // Handle the case where the value is not present + throw new NoSuchElementException("Header.VIEW_UUID not found in response headers"); + } + } catch (IOException e) { + throw new BadClientResponseException(e); } + } - /** - * Queries the Vitruvius server to propagate the given changes for the view with the given UUID. - * - * @param uuid UUID of the changed view. - * @param change The changes performed on the affected view. - * @throws BadServerResponseException If the server answered with a bad response or a connection - * error occurred. - */ - void propagateChanges(String uuid, VitruviusChange change) - throws BadServerResponseException { - try { - change.getEChanges().forEach(it -> { + /** + * Queries the Vitruvius server to propagate the given changes for the view with the given UUID. + * + * @param uuid UUID of the changed view. + * @param change The changes performed on the affected view. + * @throws BadServerResponseException If the server answered with a bad response or a connection + * error occurred. + */ + void propagateChanges(String uuid, VitruviusChange change) throws BadServerResponseException { + try { + change + .getEChanges() + .forEach( + it -> { if (it instanceof InsertRootEObject) { - ((InsertRootEObject) it).setResource(null); + ((InsertRootEObject) it).setResource(null); } - }); - var jsonBody = mapper.serialize(change); - var request = HttpRequest.newBuilder().uri(createURIFrom(EndpointPath.VIEW)) - .header(Header.CONTENT_TYPE, ContentType.APPLICATION_JSON) - .header(Header.VIEW_UUID, uuid) - .method("PATCH", BodyPublishers.ofString(jsonBody)).build(); - sendRequest(request); - } catch (IOException e) { - throw new BadClientResponseException(e); - } + }); + var jsonBody = mapper.serialize(change); + var request = + HttpRequest.newBuilder() + .uri(createURIFrom(EndpointPath.VIEW)) + .header(Header.CONTENT_TYPE, ContentType.APPLICATION_JSON) + .header(Header.VIEW_UUID, uuid) + .method("PATCH", BodyPublishers.ofString(jsonBody)) + .build(); + sendRequest(request); + } catch (IOException e) { + throw new BadClientResponseException(e); } + } - /** - * Queries the Vitruvius server to close the view with the given. - * - * @param uuid UUID of the view. - * @throws BadServerResponseException If the server answered with a bad response or a connection - * error occurred. - */ - void closeView(String uuid) throws BadServerResponseException { - var request = HttpRequest.newBuilder().uri(createURIFrom(EndpointPath.VIEW)) - .header(Header.VIEW_UUID, uuid).DELETE().build(); - sendRequest(request); - } + /** + * Queries the Vitruvius server to close the view with the given. + * + * @param uuid UUID of the view. + * @throws BadServerResponseException If the server answered with a bad response or a connection + * error occurred. + */ + void closeView(String uuid) throws BadServerResponseException { + var request = + HttpRequest.newBuilder() + .uri(createURIFrom(EndpointPath.VIEW)) + .header(Header.VIEW_UUID, uuid) + .DELETE() + .build(); + sendRequest(request); + } - /** - * Queries the Vitruvius serve to check if the view with the given ID is closed. - * - * @param uuid UUID of the view. - * @return {@code true} if the view is closed, {@code false} otherwise. - * @throws BadServerResponseException If the server answered with a bad response or a connection - * error occurred. - */ - boolean isViewClosed(String uuid) throws BadServerResponseException { - var request = HttpRequest.newBuilder().uri(createURIFrom(EndpointPath.IS_VIEW_CLOSED)) - .header(Header.VIEW_UUID, uuid).GET().build(); - return sendRequestAndCheckBooleanResult(request); - } + /** + * Queries the Vitruvius serve to check if the view with the given ID is closed. + * + * @param uuid UUID of the view. + * @return {@code true} if the view is closed, {@code false} otherwise. + * @throws BadServerResponseException If the server answered with a bad response or a connection + * error occurred. + */ + boolean isViewClosed(String uuid) throws BadServerResponseException { + var request = + HttpRequest.newBuilder() + .uri(createURIFrom(EndpointPath.IS_VIEW_CLOSED)) + .header(Header.VIEW_UUID, uuid) + .GET() + .build(); + return sendRequestAndCheckBooleanResult(request); + } - /** - * Queries the Vitruvius server to check if the view with the given ID is outdated. - * - * @param uuid UUID of the view. - * @return {@code true} if the view is outdated, {@code false} otherwise. - */ - boolean isViewOutdated(String uuid) { - var request = HttpRequest.newBuilder().uri(createURIFrom(EndpointPath.IS_VIEW_OUTDATED)) - .header(Header.VIEW_UUID, uuid).GET().build(); - return sendRequestAndCheckBooleanResult(request); - } + /** + * Queries the Vitruvius server to check if the view with the given ID is outdated. + * + * @param uuid UUID of the view. + * @return {@code true} if the view is outdated, {@code false} otherwise. + */ + boolean isViewOutdated(String uuid) { + var request = + HttpRequest.newBuilder() + .uri(createURIFrom(EndpointPath.IS_VIEW_OUTDATED)) + .header(Header.VIEW_UUID, uuid) + .GET() + .build(); + return sendRequestAndCheckBooleanResult(request); + } - /** - * Queries the Vitruvius server to update the view with the given ID. - * - * @param uuid UUID of the view. - * @return The updated {@link ResourceSet} of the view. - * @throws BadServerResponseException If the server answered with a bad response or a connection - * error occurred. - */ - ResourceSet updateView(String uuid) throws BadServerResponseException { - var request = HttpRequest.newBuilder().uri(createURIFrom(EndpointPath.VIEW)) - .header(Header.VIEW_UUID, uuid).GET().build(); - try { - var response = sendRequest(request); - return mapper.deserialize(response.body(), ResourceSet.class); - } catch (IOException e) { - throw new BadClientResponseException(e); - } + /** + * Queries the Vitruvius server to update the view with the given ID. + * + * @param uuid UUID of the view. + * @return The updated {@link ResourceSet} of the view. + * @throws BadServerResponseException If the server answered with a bad response or a connection + * error occurred. + */ + ResourceSet updateView(String uuid) throws BadServerResponseException { + var request = + HttpRequest.newBuilder() + .uri(createURIFrom(EndpointPath.VIEW)) + .header(Header.VIEW_UUID, uuid) + .GET() + .build(); + try { + var response = sendRequest(request); + return mapper.deserialize(response.body(), ResourceSet.class); + } catch (IOException e) { + throw new BadClientResponseException(e); } + } - private boolean sendRequestAndCheckBooleanResult(HttpRequest request) { - var response = sendRequest(request); - if (!Objects.equals(response.body(), Boolean.TRUE.toString()) - && !Objects.equals(response.body(), Boolean.FALSE.toString())) { - throw new BadServerResponseException( - "Expected response to be true or false! Actual: " + response); - } - return response.body().equals(Boolean.TRUE.toString()); + private boolean sendRequestAndCheckBooleanResult(HttpRequest request) { + var response = sendRequest(request); + if (!Objects.equals(response.body(), Boolean.TRUE.toString()) + && !Objects.equals(response.body(), Boolean.FALSE.toString())) { + throw new BadServerResponseException( + "Expected response to be true or false! Actual: " + response); } + return response.body().equals(Boolean.TRUE.toString()); + } - private HttpResponse sendRequest(HttpRequest request) { - var timer = Timer.start(Metrics.globalRegistry); - try { - var response = client.send(request, BodyHandlers.ofString()); - if (response.statusCode() != HttpURLConnection.HTTP_OK) { - timer.stop(Metrics.timer(METRIC_CLIENT_NAME, ENDPOINT, request.uri().getPath(), - METHOD, request.method(), RESULT, "" + response.statusCode())); - throw new BadServerResponseException(response.body(), response.statusCode()); - } - timer.stop(Metrics.timer(METRIC_CLIENT_NAME, ENDPOINT, request.uri().getPath(), - METHOD, request.method(), RESULT, SUCCESS)); - return response; - } catch (IOException e) { - timer.stop(Metrics.timer(METRIC_CLIENT_NAME, ENDPOINT, request.uri().getPath(), - METHOD, request.method(), RESULT, EXCEPTION)); - throw new BadServerResponseException(e); - } catch (InterruptedException e) { - timer.stop(Metrics.timer(METRIC_CLIENT_NAME, ENDPOINT, request.uri().getPath(), - METHOD, request.method(), RESULT, EXCEPTION)); - Thread.currentThread().interrupt(); // Re-interrupt the current thread - throw new BadServerResponseException(e); - } + private HttpResponse sendRequest(HttpRequest request) { + var timer = Timer.start(Metrics.globalRegistry); + try { + var response = client.send(request, BodyHandlers.ofString()); + if (response.statusCode() != HttpURLConnection.HTTP_OK) { + timer.stop( + Metrics.timer( + METRIC_CLIENT_NAME, + ENDPOINT, + request.uri().getPath(), + METHOD, + request.method(), + RESULT, + "" + response.statusCode())); + throw new BadServerResponseException(response.body(), response.statusCode()); + } + timer.stop( + Metrics.timer( + METRIC_CLIENT_NAME, + ENDPOINT, + request.uri().getPath(), + METHOD, + request.method(), + RESULT, + SUCCESS)); + return response; + } catch (IOException e) { + timer.stop( + Metrics.timer( + METRIC_CLIENT_NAME, + ENDPOINT, + request.uri().getPath(), + METHOD, + request.method(), + RESULT, + EXCEPTION)); + throw new BadServerResponseException(e); + } catch (InterruptedException e) { + timer.stop( + Metrics.timer( + METRIC_CLIENT_NAME, + ENDPOINT, + request.uri().getPath(), + METHOD, + request.method(), + RESULT, + EXCEPTION)); + Thread.currentThread().interrupt(); // Re-interrupt the current thread + throw new BadServerResponseException(e); } + } - private URI createURIFrom(String path) { - return URI.create(String.format("%s://%s:%d%s", protocol, hostOrIp, port, path)); - } + private URI createURIFrom(String path) { + return URI.create(String.format("%s://%s:%d%s", protocol, hostOrIp, port, path)); + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/client/impl/package-info.java b/remote/src/main/java/tools/vitruv/framework/remote/client/impl/package-info.java index 9d21012..07e7347 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/client/impl/package-info.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/client/impl/package-info.java @@ -1,5 +1,5 @@ /** - * In this package, the Vitruvius View API is re-implemented for the Vitruvius client. - * Additionally, the package contains the remote connection handling. + * In this package, the Vitruvius View API is re-implemented for the Vitruvius client. Additionally, + * the package contains the remote connection handling. */ package tools.vitruv.framework.remote.client.impl; diff --git a/remote/src/main/java/tools/vitruv/framework/remote/client/package-info.java b/remote/src/main/java/tools/vitruv/framework/remote/client/package-info.java index 7dba503..67e2e76 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/client/package-info.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/client/package-info.java @@ -1,4 +1,2 @@ -/** - * This package provides the Vitruvius client. - */ +/** This package provides the Vitruvius client. */ package tools.vitruv.framework.remote.client; diff --git a/remote/src/main/java/tools/vitruv/framework/remote/common/DefaultConnectionSettings.java b/remote/src/main/java/tools/vitruv/framework/remote/common/DefaultConnectionSettings.java index 4ca62b0..b504ce5 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/common/DefaultConnectionSettings.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/common/DefaultConnectionSettings.java @@ -1,13 +1,18 @@ package tools.vitruv.framework.remote.common; /** - * Defines default settings for the connection between a Vitruvius server and client. - * They are only used if no other settings are provided. + * Defines default settings for the connection between a Vitruvius server and client. They are only + * used if no other settings are provided. */ public class DefaultConnectionSettings { - public static final String STD_PROTOCOL = "http"; - public static final String STD_HOST = "localhost"; - public static final int STD_PORT = 8080; - - private DefaultConnectionSettings() {} + /** The standard protocol used for the connection. */ + public static final String STD_PROTOCOL = "http"; + + /** The standard host used for the connection. */ + public static final String STD_HOST = "localhost"; + + /** The standard port used for the connection. */ + public static final int STD_PORT = 8080; + + private DefaultConnectionSettings() {} } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/common/apm/SingleMeasureRecordingTimer.java b/remote/src/main/java/tools/vitruv/framework/remote/common/apm/SingleMeasureRecordingTimer.java index 33b803f..39931d9 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/common/apm/SingleMeasureRecordingTimer.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/common/apm/SingleMeasureRecordingTimer.java @@ -1,56 +1,81 @@ package tools.vitruv.framework.remote.common.apm; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.distribution.DistributionStatisticConfig; import io.micrometer.core.instrument.distribution.pause.PauseDetector; import io.micrometer.core.instrument.step.StepTimer; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; -/** - * Provides a specialized {@link StepTimer}, which records every single - * measurement. - */ +/** Provides a specialized {@link StepTimer}, which records every single measurement. */ public class SingleMeasureRecordingTimer extends StepTimer { - public static record SingleRecordedMeasure(long amount, TimeUnit unit) { - } - - private List recordings = new ArrayList<>(); - - public SingleMeasureRecordingTimer(Id id, Clock clock, DistributionStatisticConfig distributionStatisticConfig, - PauseDetector pauseDetector, TimeUnit baseTimeUnit, long stepDurationMillis, - boolean supportsAggregablePercentiles) { - super(id, clock, distributionStatisticConfig, pauseDetector, baseTimeUnit, stepDurationMillis, - supportsAggregablePercentiles); - } - - @Override - protected void recordNonNegative(long amount, TimeUnit unit) { - super.recordNonNegative(amount, unit); - recordings.add(new SingleRecordedMeasure(amount, unit)); - } - - public List getRecordings() { - return List.copyOf(recordings); - } - - public void clear() { - recordings.clear(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (!(obj instanceof SingleMeasureRecordingTimer)) - return false; - return super.equals(obj); - } - - @Override - public int hashCode() { - return super.hashCode(); - } + public static record SingleRecordedMeasure(long amount, TimeUnit unit) {} + + private final List recordings = new ArrayList<>(); + + /** + * Creates a new SingleMeasureRecordingTimer. + * + * @param id the id of the timer + * @param clock the clock to use + * @param distributionStatisticConfig the distribution statistic configuration + * @param pauseDetector the pause detector + * @param baseTimeUnit the base time unit + * @param stepDurationMillis the step duration in milliseconds + * @param supportsAggregablePercentiles whether the timer supports aggregable percentiles + */ + public SingleMeasureRecordingTimer( + Id id, + Clock clock, + DistributionStatisticConfig distributionStatisticConfig, + PauseDetector pauseDetector, + TimeUnit baseTimeUnit, + long stepDurationMillis, + boolean supportsAggregablePercentiles) { + super( + id, + clock, + distributionStatisticConfig, + pauseDetector, + baseTimeUnit, + stepDurationMillis, + supportsAggregablePercentiles); + } + + @Override + protected void recordNonNegative(long amount, TimeUnit unit) { + super.recordNonNegative(amount, unit); + recordings.add(new SingleRecordedMeasure(amount, unit)); + } + + /** + * Gets the list of single recorded measurements. + * + * @return the list of single recorded measurements + */ + public List getRecordings() { + return List.copyOf(recordings); + } + + /** Clears the recorded measurements. */ + public void clear() { + recordings.clear(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof SingleMeasureRecordingTimer)) { + return false; + } + return super.equals(obj); + } + + @Override + public int hashCode() { + return super.hashCode(); + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/common/apm/VitruvApmController.java b/remote/src/main/java/tools/vitruv/framework/remote/common/apm/VitruvApmController.java index 9e53e21..4407775 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/common/apm/VitruvApmController.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/common/apm/VitruvApmController.java @@ -1,18 +1,16 @@ package tools.vitruv.framework.remote.common.apm; -import java.nio.file.Path; - import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.Metrics; +import java.nio.file.Path; -/** - * This class allows controlling the Vitruvius monitoring. - */ +/** This class allows controlling the Vitruvius monitoring. */ public final class VitruvApmController { private static VitruvStepMeterRegistry activeRegistry; // Use the same variable name consistently /** - * Constructor to initialize the monitoring (No initialization needed here, since we're using static methods). + * Constructor to initialize the monitoring (No initialization needed here, since we're using + * static methods). */ private VitruvApmController() { // Private constructor to prevent instantiation @@ -20,19 +18,18 @@ private VitruvApmController() { /** * Enables the monitoring for Vitruvius. - * + * * @param output Path to a file in which all measurements are stored. */ public static void enable(Path output) { if (activeRegistry == null) { // Use the correct variable name here - activeRegistry = new VitruvStepMeterRegistry(new VitruvStepRegistryConfig(), Clock.SYSTEM, output); + activeRegistry = + new VitruvStepMeterRegistry(new VitruvStepRegistryConfig(), Clock.SYSTEM, output); Metrics.globalRegistry.add(activeRegistry); // Register the registry globally } } - /** - * Disables the monitoring for Vitruvius. - */ + /** Disables the monitoring for Vitruvius. */ public static void disable() { if (activeRegistry != null) { activeRegistry.stop(); // Stop the active registry diff --git a/remote/src/main/java/tools/vitruv/framework/remote/common/apm/VitruvStepMeterRegistry.java b/remote/src/main/java/tools/vitruv/framework/remote/common/apm/VitruvStepMeterRegistry.java index c5cf42d..9db4f5d 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/common/apm/VitruvStepMeterRegistry.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/common/apm/VitruvStepMeterRegistry.java @@ -1,12 +1,5 @@ package tools.vitruv.framework.remote.common.apm; -import java.io.BufferedWriter; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardOpenOption; -import java.util.concurrent.TimeUnit; - import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.Meter.Id; import io.micrometer.core.instrument.Timer; @@ -15,53 +8,71 @@ import io.micrometer.core.instrument.step.StepMeterRegistry; import io.micrometer.core.instrument.step.StepRegistryConfig; import io.micrometer.core.instrument.util.NamedThreadFactory; - +import java.io.BufferedWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.concurrent.TimeUnit; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; /** - * Provides a specialized {@link StepMeterRegistry} for Vitruvius, which stores measurements in a file. + * Provides a specialized {@link StepMeterRegistry} for Vitruvius, which stores measurements in a + * file. */ class VitruvStepMeterRegistry extends StepMeterRegistry { - private Path output; - private StepRegistryConfig config; - private static final Logger LOGGER = LogManager.getLogger(VitruvStepMeterRegistry.class); + private Path output; + private StepRegistryConfig config; + private static final Logger LOGGER = LogManager.getLogger(VitruvStepMeterRegistry.class); - VitruvStepMeterRegistry(StepRegistryConfig config, Clock clock, Path output) { - super(config, clock); - this.output = output.toAbsolutePath(); - this.config = config; - this.start(new NamedThreadFactory("vitruv-tf")); - } + /** + * Creates a new {@link VitruvStepMeterRegistry}. + * + * @param config The step registry configuration. + * @param clock the clock to use. + * @param output Path to a file in which all measurements are stored. + */ + VitruvStepMeterRegistry(StepRegistryConfig config, Clock clock, Path output) { + super(config, clock); + this.output = output.toAbsolutePath(); + this.config = config; + this.start(new NamedThreadFactory("vitruv-tf")); + } - @Override - protected void publish() { - try (BufferedWriter writer = Files.newBufferedWriter(output, StandardOpenOption.CREATE, StandardOpenOption.APPEND)) { - for (var meter : getMeters()) { - if (meter instanceof SingleMeasureRecordingTimer timer) { - for (var recording : timer.getRecordings()) { - writer.append(meter.getId().toString() + "," + recording.unit().toMillis(recording.amount()) + "\n"); - } - timer.clear(); - } else { - for (var measurement : meter.measure()) { - writer.append(meter.getId().toString() + "," + measurement.getValue() + "\n"); - } - } - } - } catch (IOException e) { - LOGGER.error("Could not write metrics because: {}", e.getMessage()); - } - } + @Override + protected void publish() { + try (BufferedWriter writer = + Files.newBufferedWriter(output, StandardOpenOption.CREATE, StandardOpenOption.APPEND)) { + for (var meter : getMeters()) { + if (meter instanceof SingleMeasureRecordingTimer timer) { + for (var recording : timer.getRecordings()) { + writer.append( + meter.getId().toString() + + "," + + recording.unit().toMillis(recording.amount()) + + "\n"); + } + timer.clear(); + } else { + for (var measurement : meter.measure()) { + writer.append(meter.getId().toString() + "," + measurement.getValue() + "\n"); + } + } + } + } catch (IOException e) { + LOGGER.error("Could not write metrics because: {}", e.getMessage()); + } + } - @Override - protected TimeUnit getBaseTimeUnit() { - return TimeUnit.MILLISECONDS; - } + @Override + protected TimeUnit getBaseTimeUnit() { + return TimeUnit.MILLISECONDS; + } - @Override - protected Timer newTimer(Id id, DistributionStatisticConfig config, PauseDetector detector) { - return new SingleMeasureRecordingTimer(id, this.clock, config, detector, - getBaseTimeUnit(), this.config.step().toMillis(), false); - } + @Override + protected Timer newTimer(Id id, DistributionStatisticConfig config, PauseDetector detector) { + return new SingleMeasureRecordingTimer( + id, this.clock, config, detector, getBaseTimeUnit(), this.config.step().toMillis(), false); + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/common/apm/VitruvStepRegistryConfig.java b/remote/src/main/java/tools/vitruv/framework/remote/common/apm/VitruvStepRegistryConfig.java index 6293daa..33d8214 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/common/apm/VitruvStepRegistryConfig.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/common/apm/VitruvStepRegistryConfig.java @@ -2,14 +2,15 @@ import io.micrometer.core.instrument.step.StepRegistryConfig; +/** Configuration for the Vitruvius step meter registry. */ class VitruvStepRegistryConfig implements StepRegistryConfig { - @Override - public String get(String arg0) { - return null; - } + @Override + public String get(String arg0) { + return null; + } - @Override - public String prefix() { - return "vitruv-step-config"; - } + @Override + public String prefix() { + return "vitruv-step-config"; + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/common/json/ChangeType.java b/remote/src/main/java/tools/vitruv/framework/remote/common/json/ChangeType.java index 1dfaae6..6766bb4 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/common/json/ChangeType.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/common/json/ChangeType.java @@ -4,28 +4,28 @@ import tools.vitruv.change.composite.description.TransactionalChange; import tools.vitruv.change.composite.description.VitruviusChange; -/** - * Represents the type of the {@link VitruviusChange}. - * - * @see CompositeChange - * @see TransactionalChange - */ +/** An enumeration representing different types of Vitruvius changes. */ public enum ChangeType { - TRANSACTIONAL, COMPOSITE, UNKNOWN; + /** Transaltional change type. */ + TRANSACTIONAL, + /** Composite change type. */ + COMPOSITE, + /** Unknown change type. */ + UNKNOWN; - /** - * Returns the type of the given {@link VitruviusChange}. - * - * @param change The change to obtain the type from. - * @return The type of the change. - */ - public static ChangeType getChangeTypeOf(VitruviusChange change) { - if (change instanceof TransactionalChange) { - return TRANSACTIONAL; - } - if (change instanceof CompositeChange) { - return COMPOSITE; - } - return UNKNOWN; + /** + * Returns the type of the given {@link VitruviusChange}. + * + * @param change The change to obtain the type from. + * @return The type of the change. + */ + public static ChangeType getChangeTypeOf(VitruviusChange change) { + if (change instanceof TransactionalChange) { + return TRANSACTIONAL; } + if (change instanceof CompositeChange) { + return COMPOSITE; + } + return UNKNOWN; + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/common/json/IdTransformation.java b/remote/src/main/java/tools/vitruv/framework/remote/common/json/IdTransformation.java index c663881..5f53498 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/common/json/IdTransformation.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/common/json/IdTransformation.java @@ -2,81 +2,97 @@ import java.nio.file.Path; import java.util.List; - import org.eclipse.emf.common.util.URI; - import tools.vitruv.change.atomic.EChange; import tools.vitruv.change.atomic.hid.HierarchicalId; import tools.vitruv.change.atomic.root.RootEChange; import tools.vitruv.change.utils.ProjectMarker; /** - * Contains functions to transform IDs used by the Vitruvius framework to identify - * {@link org.eclipse.emf.ecore.EObject EObjects}. + * Contains functions to transform IDs used by the Vitruvius framework to identify {@link + * org.eclipse.emf.ecore.EObject EObjects}. */ public class IdTransformation { - private URI root; - - IdTransformation(Path vsumPath) { - root = URI.createFileURI(ProjectMarker.getProjectRootFolder(vsumPath).toString()); - - var nextToCheck = vsumPath; - while ((nextToCheck = nextToCheck.getParent()) != null) { - try { - root = URI.createFileURI(ProjectMarker.getProjectRootFolder(nextToCheck).toString()); - } catch (IllegalStateException e) { - break; - } - } + private URI root; + + /** + * Creates a new IdTransformation. + * + * @param vsumPath the path to the .vsum file of the project + */ + public IdTransformation(Path vsumPath) { + root = URI.createFileURI(ProjectMarker.getProjectRootFolder(vsumPath).toString()); + + var nextToCheck = vsumPath; + while ((nextToCheck = nextToCheck.getParent()) != null) { + try { + root = URI.createFileURI(ProjectMarker.getProjectRootFolder(nextToCheck).toString()); + } catch (IllegalStateException e) { + break; + } } + } - /** - * Transforms the given global (absolute path) ID to a local ID (relative path). - * - * @param global The ID to transform. - * @return The local ID. - */ - public URI toLocal(URI global) { - if (global == null || global.toString().contains("cache") || - global.toString().equals(JsonFieldName.TEMP_VALUE) || !global.isFile()) { - return global; - } - - return URI.createURI(global.toString().replace(root.toString(), "")); + /** + * Transforms the given global (absolute path) ID to a local ID (relative path). + * + * @param global The ID to transform. + * @return The local ID. + */ + public URI toLocal(URI global) { + if (global == null + || global.toString().contains("cache") + || global.toString().equals(JsonFieldName.TEMP_VALUE) + || !global.isFile()) { + return global; } - /** - * Transforms the given local ID (relative path) to a global ID (absolute path). - * - * @param local The ID to transform. - * @return The global ID. - */ - public URI toGlobal(URI local) { - if (local == null || local.toString().contains("cache") || - local.toString().equals(JsonFieldName.TEMP_VALUE)) { - return local; - } - - if (!local.isRelative()) { - return local; - } - - return URI.createURI(root.toString() + local.toString()); + return URI.createURI(global.toString().replace(root.toString(), "")); + } + + /** + * Transforms the given local ID (relative path) to a global ID (absolute path). + * + * @param local The ID to transform. + * @return The global ID. + */ + public URI toGlobal(URI local) { + if (local == null + || local.toString().contains("cache") + || local.toString().equals(JsonFieldName.TEMP_VALUE)) { + return local; } - public void allToGlobal(List> eChanges) { - for (var eChange : eChanges) { - if (eChange instanceof RootEChange change) { - change.setUri(toGlobal(URI.createURI(change.getUri())).toString()); - } - } + if (!local.isRelative()) { + return local; } - - public void allToLocal(List> eChanges) { - for (var eChange : eChanges) { - if (eChange instanceof RootEChange change) { - change.setUri(toLocal(URI.createURI(change.getUri())).toString()); - } - } + + return URI.createURI(root.toString() + local.toString()); + } + + /** + * Transforms all root change URIs in the given list of changes to global IDs. + * + * @param eChanges the list of changes + */ + public void allToGlobal(List> eChanges) { + for (var eChange : eChanges) { + if (eChange instanceof RootEChange change) { + change.setUri(toGlobal(URI.createURI(change.getUri())).toString()); + } + } + } + + /** + * Transforms all root change URIs in the given list of changes to local IDs. + * + * @param eChanges the list of changes + */ + public void allToLocal(List> eChanges) { + for (var eChange : eChanges) { + if (eChange instanceof RootEChange change) { + change.setUri(toLocal(URI.createURI(change.getUri())).toString()); + } } + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/common/json/JsonFieldName.java b/remote/src/main/java/tools/vitruv/framework/remote/common/json/JsonFieldName.java index 1b5e4ae..32782c9 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/common/json/JsonFieldName.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/common/json/JsonFieldName.java @@ -1,15 +1,29 @@ package tools.vitruv.framework.remote.common.json; +/** Field names used in JSON serialization and deserialization. */ public final class JsonFieldName { - public static final String CHANGE_TYPE = "changeType"; - public static final String E_CHANGES = "eChanges"; - public static final String V_CHANGES = "vChanges"; - public static final String U_INTERACTIONS = "uInteractions"; - public static final String TEMP_VALUE = "temp"; - public static final String CONTENT = "content"; - public static final String URI = "uri"; - - private JsonFieldName() throws InstantiationException { - throw new InstantiationException("Cannot be instantiated"); - } + /** The field name for the change type. */ + public static final String CHANGE_TYPE = "changeType"; + + /** The field name for eChanges. */ + public static final String E_CHANGES = "eChanges"; + + /** The field name for vChanges. */ + public static final String V_CHANGES = "vChanges"; + + /** The field name for user interactions. */ + public static final String U_INTERACTIONS = "uInteractions"; + + /** The field name for temporary values. */ + public static final String TEMP_VALUE = "temp"; + + /** The field name for content. */ + public static final String CONTENT = "content"; + + /** The field name for URI. */ + public static final String URI = "uri"; + + private JsonFieldName() throws InstantiationException { + throw new InstantiationException("Cannot be instantiated"); + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/common/json/JsonMapper.java b/remote/src/main/java/tools/vitruv/framework/remote/common/json/JsonMapper.java index a7c7833..ff7b4bb 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/common/json/JsonMapper.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/common/json/JsonMapper.java @@ -1,9 +1,12 @@ package tools.vitruv.framework.remote.common.json; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; import java.io.IOException; import java.nio.file.Path; import java.util.List; - import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; @@ -11,12 +14,6 @@ import org.eclipse.emfcloud.jackson.databind.EMFContext; import org.eclipse.emfcloud.jackson.module.EMFModule; import org.eclipse.emfcloud.jackson.module.EMFModule.Feature; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; - import tools.vitruv.change.composite.description.VitruviusChange; import tools.vitruv.framework.remote.common.json.deserializer.ReferenceDeserializerModifier; import tools.vitruv.framework.remote.common.json.deserializer.ResourceSetDeserializer; @@ -26,90 +23,110 @@ import tools.vitruv.framework.remote.common.json.serializer.VitruviusChangeSerializer; /** - * This mapper can be used to serialize objects and deserialize JSON in the context of Vitruvius. - * It has custom De-/Serializers for {@link ResourceSet}s, {@link Resource}s and {@link VitruviusChange}s. + * This mapper can be used to serialize objects and deserialize JSON in the context of Vitruvius. It + * has custom De-/Serializers for {@link ResourceSet}s, {@link Resource}s and {@link + * VitruviusChange}s. */ public class JsonMapper { - private final ObjectMapper mapper = new ObjectMapper(); + private final ObjectMapper mapper = new ObjectMapper(); + + /** + * Creates a new JsonMapper. + * + * @param vsumPath the path to the .vsum file of the project + */ + public JsonMapper(Path vsumPath) { + final var transformation = new IdTransformation(vsumPath); + + mapper.configure(SerializationFeature.INDENT_OUTPUT, true); + var module = new EMFModule(); + + // Register serializer + module.addSerializer(ResourceSet.class, new ResourceSetSerializer(transformation)); + module.addSerializer(VitruviusChange.class, new VitruviusChangeSerializer()); + + // Register deserializer + module.addDeserializer(ResourceSet.class, new ResourceSetDeserializer(this, transformation)); + module.addDeserializer( + VitruviusChange.class, new VitruviusChangeDeserializer(this, transformation)); + + // Register modifiers for references to handle HierarichalId + module.setSerializerModifier(new ReferenceSerializerModifier(transformation)); + module.setDeserializerModifier(new ReferenceDeserializerModifier(transformation)); + + // Use IDs to identify eObjects on client and server + module.configure(Feature.OPTION_USE_ID, true); + module.setIdentityInfo(new EcoreIdentityInfo("_id")); - public JsonMapper(Path vsumPath) { - final var transformation = new IdTransformation(vsumPath); - - mapper.configure(SerializationFeature.INDENT_OUTPUT, true); - var module = new EMFModule(); - - //Register serializer - module.addSerializer(ResourceSet.class, new ResourceSetSerializer(transformation)); - module.addSerializer(VitruviusChange.class, new VitruviusChangeSerializer()); + mapper.registerModule(module); + } - //Register deserializer - module.addDeserializer(ResourceSet.class, new ResourceSetDeserializer(this, transformation)); - module.addDeserializer(VitruviusChange.class, new VitruviusChangeDeserializer(this, transformation)); - - //Register modifiers for references to handle HierarichalId - module.setSerializerModifier(new ReferenceSerializerModifier(transformation)); - module.setDeserializerModifier(new ReferenceDeserializerModifier(transformation)); - - //Use IDs to identify eObjects on client and server - module.configure(Feature.OPTION_USE_ID, true); - module.setIdentityInfo(new EcoreIdentityInfo("_id")); + /** + * Serializes the given object. + * + * @param obj The object to serialize. + * @return The JSON or {@code null} if an {@link JsonProcessingException} occurred. + */ + public String serialize(Object obj) throws JsonProcessingException { + return mapper.writeValueAsString(obj); + } - mapper.registerModule(module); - } + /** + * Deserializes the given JSON string. + * + * @param The type of the returned object. + * @param json The JSON to deserialize. + * @param clazz The class of the JSON type. + * @return The object or {@code null} if an {@link JsonProcessingException} occurred. + */ + public T deserialize(String json, Class clazz) throws JsonProcessingException { + return mapper.reader().forType(clazz).readValue(json); + } - /** - * Serializes the given object. - * - * @param obj The object to serialize. - * @return The JSON or {@code null} if an {@link JsonProcessingException} occurred. - */ - public String serialize(Object obj) throws JsonProcessingException { - return mapper.writeValueAsString(obj); - } + /** + * Deserializes the given JSON node. + * + * @param The type of the returned object. + * @param json The JSON node to deserialize. + * @param clazz The class of the JSON type. + * @return The object. + * @throws JsonProcessingException If the JSON node cannot be processed. + * @throws IOException If there is an IO exception during deserialization. + */ + public T deserialize(JsonNode json, Class clazz) + throws JsonProcessingException, IOException { + return mapper.reader().forType(clazz).readValue(json); + } - /** - * Deserializes the given JSON string. - * - * @param The type of the returned object. - * @param json The JSON to deserialize. - * @param clazz The class of the JSON type. - * @return The object or {@code null} if an {@link JsonProcessingException} occurred. - */ - public T deserialize(String json, Class clazz) throws JsonProcessingException { - return mapper.reader().forType(clazz).readValue(json); - } - - /** - * Deserializes the given JSON node. - * - * @param The type of the returned object. - * @param json The JSON node to deserialize. - * @param clazz The class of the JSON type. - * @return The object. - * @throws JsonProcessingException If the JSON node cannot be processed. - * @throws IOException If there is an IO exception during deserialization. - */ - public T deserialize(JsonNode json, Class clazz) throws JsonProcessingException, IOException { - return mapper.reader().forType(clazz).readValue(json); - } - - public Resource deserializeResource(String json, String uri, ResourceSet parentSet) throws JsonProcessingException { - return mapper.reader() - .withAttribute(EMFContext.Attributes.RESOURCE_SET, parentSet) - .withAttribute(EMFContext.Attributes.RESOURCE_URI, URI.createURI(uri)) - .forType(Resource.class) - .readValue(json); - } + /** + * Deserializes the given JSON string to a Resource. + * + * @param json The JSON string representing the Resource. + * @param uri The URI of the Resource. + * @param parentSet The parent ResourceSet of the Resource. + * @return The deserialized Resource. + * @throws JsonProcessingException If an error occurs during deserialization. + */ + public Resource deserializeResource(String json, String uri, ResourceSet parentSet) + throws JsonProcessingException { + return mapper + .reader() + .withAttribute(EMFContext.Attributes.RESOURCE_SET, parentSet) + .withAttribute(EMFContext.Attributes.RESOURCE_URI, URI.createURI(uri)) + .forType(Resource.class) + .readValue(json); + } - /** - * Deserializes the given JSON array to a list. - * - * @param json The JSON array to deserialize. - * @param clazz The class representing the JSON type of the objects in the JSON array. - * @return The list of objects or {@code null} if an {@link JsonProcessingException} occurred. - */ - public List deserializeArrayOf(String json, Class clazz) throws JsonProcessingException { - var javaType = mapper.getTypeFactory().constructCollectionType(List.class, clazz); - return mapper.readValue(json, javaType); - } + /** + * Deserializes the given JSON array to a list. + * + * @param json The JSON array to deserialize. + * @param clazz The class representing the JSON type of the objects in the JSON array. + * @return The list of objects or {@code null} if an {@link JsonProcessingException} occurred. + */ + public List deserializeArrayOf(String json, Class clazz) + throws JsonProcessingException { + var javaType = mapper.getTypeFactory().constructCollectionType(List.class, clazz); + return mapper.readValue(json, javaType); + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/common/json/deserializer/HidReferenceEntry.java b/remote/src/main/java/tools/vitruv/framework/remote/common/json/deserializer/HidReferenceEntry.java index 4c6f014..3739b01 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/common/json/deserializer/HidReferenceEntry.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/common/json/deserializer/HidReferenceEntry.java @@ -1,28 +1,34 @@ package tools.vitruv.framework.remote.common.json.deserializer; +import com.fasterxml.jackson.databind.DatabindContext; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emfcloud.jackson.databind.deser.ReferenceEntry; import org.eclipse.emfcloud.jackson.handlers.URIHandler; import org.eclipse.emfcloud.jackson.utils.EObjects; - -import com.fasterxml.jackson.databind.DatabindContext; - import tools.vitruv.change.atomic.hid.HierarchicalId; +/** A {@link ReferenceEntry} for hierarchical IDs (HIDs). */ public class HidReferenceEntry implements ReferenceEntry { - private final EObject owner; - private final EReference reference; - private final String hid; - - public HidReferenceEntry(EObject owner, EReference reference, String hid) { - this.owner = owner; - this.reference = reference; - this.hid = hid; - } + private final EObject owner; + private final EReference reference; + private final String hid; + + /** + * Creates a new {@link HidReferenceEntry}. + * + * @param owner the owner EObject + * @param reference the EReference + * @param hid the hierarchical ID as a string + */ + public HidReferenceEntry(EObject owner, EReference reference, String hid) { + this.owner = owner; + this.reference = reference; + this.hid = hid; + } - @Override - public void resolve(DatabindContext context, URIHandler handler) { - EObjects.setOrAdd(owner, reference, new HierarchicalId(hid)); - } + @Override + public void resolve(DatabindContext context, URIHandler handler) { + EObjects.setOrAdd(owner, reference, new HierarchicalId(hid)); + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/common/json/deserializer/ReferenceDeserializerModifier.java b/remote/src/main/java/tools/vitruv/framework/remote/common/json/deserializer/ReferenceDeserializerModifier.java index a6e83a7..3279a29 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/common/json/deserializer/ReferenceDeserializerModifier.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/common/json/deserializer/ReferenceDeserializerModifier.java @@ -1,28 +1,35 @@ package tools.vitruv.framework.remote.common.json.deserializer; -import org.eclipse.emfcloud.jackson.databind.deser.EcoreReferenceDeserializer; - import com.fasterxml.jackson.databind.BeanDescription; import com.fasterxml.jackson.databind.DeserializationConfig; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier; import com.fasterxml.jackson.databind.type.ReferenceType; - +import org.eclipse.emfcloud.jackson.databind.deser.EcoreReferenceDeserializer; import tools.vitruv.framework.remote.common.json.IdTransformation; +/** A {@link BeanDeserializerModifier} that modifies reference deserializers to use. */ public class ReferenceDeserializerModifier extends BeanDeserializerModifier { - private final transient IdTransformation transformation; - - public ReferenceDeserializerModifier(IdTransformation transformation) { - this.transformation = transformation; - } + private final transient IdTransformation transformation; + + /** + * Creates a new ReferenceDeserializerModifier. + * + * @param transformation the id transformation to be used + */ + public ReferenceDeserializerModifier(IdTransformation transformation) { + this.transformation = transformation; + } - @Override - public JsonDeserializer modifyReferenceDeserializer(DeserializationConfig config, ReferenceType type, - BeanDescription beanDesc, JsonDeserializer deserializer) { - if (deserializer instanceof EcoreReferenceDeserializer referenceDeserializer) { - return new HierarichalIdDeserializer(referenceDeserializer, transformation); - } - return deserializer; - } + @Override + public JsonDeserializer modifyReferenceDeserializer( + DeserializationConfig config, + ReferenceType type, + BeanDescription beanDesc, + JsonDeserializer deserializer) { + if (deserializer instanceof EcoreReferenceDeserializer referenceDeserializer) { + return new HierarichalIdDeserializer(referenceDeserializer, transformation); + } + return deserializer; + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/common/json/deserializer/ResourceSetDeserializer.java b/remote/src/main/java/tools/vitruv/framework/remote/common/json/deserializer/ResourceSetDeserializer.java index 4b095e1..3b0cb37 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/common/json/deserializer/ResourceSetDeserializer.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/common/json/deserializer/ResourceSetDeserializer.java @@ -1,43 +1,50 @@ package tools.vitruv.framework.remote.common.json.deserializer; -import java.io.IOException; -import java.util.Map; - -import org.eclipse.emf.common.util.URI; -import org.eclipse.emf.ecore.resource.ResourceSet; - import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.node.ArrayNode; - +import java.io.IOException; +import java.util.Map; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.resource.ResourceSet; import tools.vitruv.framework.remote.common.json.IdTransformation; import tools.vitruv.framework.remote.common.json.JsonFieldName; import tools.vitruv.framework.remote.common.json.JsonMapper; import tools.vitruv.framework.remote.common.util.ResourceUtil; +/** A deserializer for {@link ResourceSet}. */ public class ResourceSetDeserializer extends JsonDeserializer { - private final IdTransformation transformation; - private final JsonMapper mapper; - - public ResourceSetDeserializer(JsonMapper mapper, IdTransformation transformation) { - this.transformation = transformation; - this.mapper = mapper; - } + private final IdTransformation transformation; + private final JsonMapper mapper; + + /** + * Creates a new ResourceSetDeserializer. + * + * @param mapper The json mapper to be used. + * @param transformation the id transformation to be used + */ + public ResourceSetDeserializer(JsonMapper mapper, IdTransformation transformation) { + this.transformation = transformation; + this.mapper = mapper; + } + + @Override + public ResourceSet deserialize(JsonParser parser, DeserializationContext context) + throws IOException { + var rootNode = (ArrayNode) parser.getCodec().readTree(parser); - @Override - public ResourceSet deserialize(JsonParser parser, DeserializationContext context) throws IOException { - var rootNode = (ArrayNode) parser.getCodec().readTree(parser); - - var resourceSet = ResourceUtil.createJsonResourceSet(); - for (var e : rootNode) { - var resource = mapper.deserializeResource(e.get(JsonFieldName.CONTENT).toString(), - transformation.toGlobal(URI.createURI(e.get(JsonFieldName.URI).asText())).toString(), resourceSet); - if (!resource.getURI().toString().equals(JsonFieldName.TEMP_VALUE)) { - resource.save(Map.of()); - } - } - - return resourceSet; - } + var resourceSet = ResourceUtil.createJsonResourceSet(); + for (var e : rootNode) { + var resource = + mapper.deserializeResource( + e.get(JsonFieldName.CONTENT).toString(), + transformation.toGlobal(URI.createURI(e.get(JsonFieldName.URI).asText())).toString(), + resourceSet); + if (!resource.getURI().toString().equals(JsonFieldName.TEMP_VALUE)) { + resource.save(Map.of()); + } + } + return resourceSet; + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/common/json/deserializer/VitruviusChangeDeserializer.java b/remote/src/main/java/tools/vitruv/framework/remote/common/json/deserializer/VitruviusChangeDeserializer.java index 299f7d1..641c51a 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/common/json/deserializer/VitruviusChangeDeserializer.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/common/json/deserializer/VitruviusChangeDeserializer.java @@ -1,15 +1,13 @@ package tools.vitruv.framework.remote.common.json.deserializer; -import java.io.IOException; -import java.util.LinkedList; -import java.util.List; - import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.TextNode; - +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; import tools.vitruv.change.atomic.EChange; import tools.vitruv.change.atomic.hid.HierarchicalId; import tools.vitruv.change.composite.description.TransactionalChange; @@ -22,40 +20,56 @@ import tools.vitruv.framework.remote.common.json.JsonMapper; import tools.vitruv.framework.remote.common.util.ResourceUtil; +/** A deserializer for {@link VitruviusChange}. */ public class VitruviusChangeDeserializer extends JsonDeserializer> { - private final IdTransformation transformation; - private final JsonMapper mapper; - - public VitruviusChangeDeserializer(JsonMapper mapper, IdTransformation transformation) { - this.mapper = mapper; - this.transformation = transformation; - } + private final IdTransformation transformation; + private final JsonMapper mapper; + + /** + * Creates a new VitruviusChangeDeserializer. + * + * @param mapper the json mapper to be used + * @param transformation the id transformation to be used + */ + public VitruviusChangeDeserializer(JsonMapper mapper, IdTransformation transformation) { + this.mapper = mapper; + this.transformation = transformation; + } - @Override - public VitruviusChange deserialize(JsonParser parser, DeserializationContext context) throws IOException { - var rootNode = parser.getCodec().readTree(parser); - var type = ChangeType.valueOf(((TextNode)rootNode.get(JsonFieldName.CHANGE_TYPE)).asText()); + @Override + public VitruviusChange deserialize(JsonParser parser, DeserializationContext context) + throws IOException { + var rootNode = parser.getCodec().readTree(parser); + var type = ChangeType.valueOf(((TextNode) rootNode.get(JsonFieldName.CHANGE_TYPE)).asText()); - VitruviusChange change; - if (type == ChangeType.TRANSACTIONAL) { - var resourceNode = rootNode.get(JsonFieldName.E_CHANGES); - var changesResource = mapper.deserializeResource(resourceNode.toString(), JsonFieldName.TEMP_VALUE, ResourceUtil.createJsonResourceSet()); - @SuppressWarnings("unchecked") - List> changes = changesResource.getContents().stream().map(e -> (EChange) e).toList(); - transformation.allToGlobal(changes); - change = VitruviusChangeFactory.getInstance().createTransactionalChange(changes); - var interactions = mapper.deserializeArrayOf(rootNode.get(JsonFieldName.U_INTERACTIONS).toString(), UserInteractionBase.class); - ((TransactionalChange) change).setUserInteractions(interactions); - } else if (type == ChangeType.COMPOSITE) { - var changesNode = (ArrayNode) rootNode.get(JsonFieldName.V_CHANGES); - var changes = new LinkedList>(); - for (var e : changesNode) { - changes.add(mapper.deserialize(e, VitruviusChange.class)); - } - change = VitruviusChangeFactory.getInstance().createCompositeChange(changes); - } else { - throw new UnsupportedOperationException("Change deserialization for type" + type + " not implemented!"); - } - return change; + VitruviusChange change; + if (type == ChangeType.TRANSACTIONAL) { + var resourceNode = rootNode.get(JsonFieldName.E_CHANGES); + var changesResource = + mapper.deserializeResource( + resourceNode.toString(), + JsonFieldName.TEMP_VALUE, + ResourceUtil.createJsonResourceSet()); + @SuppressWarnings("unchecked") + List> changes = + changesResource.getContents().stream().map(e -> (EChange) e).toList(); + transformation.allToGlobal(changes); + change = VitruviusChangeFactory.getInstance().createTransactionalChange(changes); + var interactions = + mapper.deserializeArrayOf( + rootNode.get(JsonFieldName.U_INTERACTIONS).toString(), UserInteractionBase.class); + ((TransactionalChange) change).setUserInteractions(interactions); + } else if (type == ChangeType.COMPOSITE) { + var changesNode = (ArrayNode) rootNode.get(JsonFieldName.V_CHANGES); + var changes = new LinkedList>(); + for (var e : changesNode) { + changes.add(mapper.deserialize(e, VitruviusChange.class)); + } + change = VitruviusChangeFactory.getInstance().createCompositeChange(changes); + } else { + throw new UnsupportedOperationException( + "Change deserialization for type" + type + " not implemented!"); } + return change; + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/common/json/serializer/HierarichalIdSerializer.java b/remote/src/main/java/tools/vitruv/framework/remote/common/json/serializer/HierarichalIdSerializer.java index 4af5b67..0f60b82 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/common/json/serializer/HierarichalIdSerializer.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/common/json/serializer/HierarichalIdSerializer.java @@ -1,33 +1,39 @@ package tools.vitruv.framework.remote.common.json.serializer; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; import java.io.IOException; - import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emfcloud.jackson.databind.ser.EcoreReferenceSerializer; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; - import tools.vitruv.change.atomic.hid.HierarchicalId; import tools.vitruv.framework.remote.common.json.IdTransformation; -public class HierarichalIdSerializer extends JsonSerializer{ - private final EcoreReferenceSerializer standardSerializer; - private final IdTransformation transformation; - - public HierarichalIdSerializer(EcoreReferenceSerializer standardDeserializer, IdTransformation transformation) { - this.standardSerializer = standardDeserializer; - this.transformation = transformation; - } +/** A serializer for {@link HierarchicalId}. */ +public class HierarichalIdSerializer extends JsonSerializer { + private final EcoreReferenceSerializer standardSerializer; + private final IdTransformation transformation; + + /** + * Creates a new HierarichalIdSerializer. + * + * @param standardDeserializer the standard deserializer + * @param transformation the id transformation to be used + */ + public HierarichalIdSerializer( + EcoreReferenceSerializer standardDeserializer, IdTransformation transformation) { + this.standardSerializer = standardDeserializer; + this.transformation = transformation; + } - @Override - public void serialize(EObject value, JsonGenerator gen, SerializerProvider serializers) throws IOException { - if (value instanceof HierarchicalId hid) { - gen.writeString(transformation.toLocal(URI.createURI(hid.getId())).toString()); - } else { - standardSerializer.serialize(value, gen, serializers); - } - } + @Override + public void serialize(EObject value, JsonGenerator gen, SerializerProvider serializers) + throws IOException { + if (value instanceof HierarchicalId hid) { + gen.writeString(transformation.toLocal(URI.createURI(hid.getId())).toString()); + } else { + standardSerializer.serialize(value, gen, serializers); + } + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/common/json/serializer/ReferenceSerializerModifier.java b/remote/src/main/java/tools/vitruv/framework/remote/common/json/serializer/ReferenceSerializerModifier.java index ec49a60..c2f005c 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/common/json/serializer/ReferenceSerializerModifier.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/common/json/serializer/ReferenceSerializerModifier.java @@ -1,26 +1,31 @@ package tools.vitruv.framework.remote.common.json.serializer; -import org.eclipse.emfcloud.jackson.databind.ser.EcoreReferenceSerializer; - import com.fasterxml.jackson.databind.BeanDescription; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializationConfig; import com.fasterxml.jackson.databind.ser.BeanSerializerModifier; - +import org.eclipse.emfcloud.jackson.databind.ser.EcoreReferenceSerializer; import tools.vitruv.framework.remote.common.json.IdTransformation; +/** A {@link BeanSerializerModifier} that modifies reference serializers to use. */ public class ReferenceSerializerModifier extends BeanSerializerModifier { - private final transient IdTransformation transformation; - - public ReferenceSerializerModifier(IdTransformation transformation) { - this.transformation = transformation; - } + private final transient IdTransformation transformation; + + /** + * Creates a new ReferenceSerializerModifier. + * + * @param transformation the id transformation to be used + */ + public ReferenceSerializerModifier(IdTransformation transformation) { + this.transformation = transformation; + } - @Override - public JsonSerializer modifySerializer(SerializationConfig config, BeanDescription desc, JsonSerializer serializer) { - if (serializer instanceof EcoreReferenceSerializer referenceSerializer) { - return new HierarichalIdSerializer(referenceSerializer, transformation); - } - return serializer; - } + @Override + public JsonSerializer modifySerializer( + SerializationConfig config, BeanDescription desc, JsonSerializer serializer) { + if (serializer instanceof EcoreReferenceSerializer referenceSerializer) { + return new HierarichalIdSerializer(referenceSerializer, transformation); + } + return serializer; + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/common/json/serializer/ResourceSetSerializer.java b/remote/src/main/java/tools/vitruv/framework/remote/common/json/serializer/ResourceSetSerializer.java index ddec8a8..a1547f9 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/common/json/serializer/ResourceSetSerializer.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/common/json/serializer/ResourceSetSerializer.java @@ -1,33 +1,38 @@ package tools.vitruv.framework.remote.common.json.serializer; -import java.io.IOException; - -import org.eclipse.emf.ecore.resource.ResourceSet; - import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; - +import java.io.IOException; +import org.eclipse.emf.ecore.resource.ResourceSet; import tools.vitruv.framework.remote.common.json.IdTransformation; import tools.vitruv.framework.remote.common.json.JsonFieldName; +/** A serializer for {@link ResourceSet}. */ public class ResourceSetSerializer extends JsonSerializer { - private final IdTransformation transformation; - - public ResourceSetSerializer(IdTransformation transformation) { - this.transformation = transformation; - } + private final IdTransformation transformation; + + /** + * Creates a new ResourceSetSerializer. + * + * @param transformation the id transformation to be used + */ + public ResourceSetSerializer(IdTransformation transformation) { + this.transformation = transformation; + } - @Override - public void serialize(ResourceSet resourceSet, JsonGenerator generator, SerializerProvider provider) throws IOException { - generator.writeStartArray(); - var resources = resourceSet.getResources(); - for (var r : resources) { - generator.writeStartObject(); - generator.writeObjectField(JsonFieldName.URI, transformation.toLocal(r.getURI()).toString()); - generator.writeObjectField(JsonFieldName.CONTENT, r); - generator.writeEndObject(); - } - generator.writeEndArray(); + @Override + public void serialize( + ResourceSet resourceSet, JsonGenerator generator, SerializerProvider provider) + throws IOException { + generator.writeStartArray(); + var resources = resourceSet.getResources(); + for (var r : resources) { + generator.writeStartObject(); + generator.writeObjectField(JsonFieldName.URI, transformation.toLocal(r.getURI()).toString()); + generator.writeObjectField(JsonFieldName.CONTENT, r); + generator.writeEndObject(); } + generator.writeEndArray(); + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/common/json/serializer/VitruviusChangeSerializer.java b/remote/src/main/java/tools/vitruv/framework/remote/common/json/serializer/VitruviusChangeSerializer.java index 1b1a25a..0ad4d96 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/common/json/serializer/VitruviusChangeSerializer.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/common/json/serializer/VitruviusChangeSerializer.java @@ -1,13 +1,10 @@ package tools.vitruv.framework.remote.common.json.serializer; -import java.io.IOException; - -import org.eclipse.emf.common.util.URI; - import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; - +import java.io.IOException; +import org.eclipse.emf.common.util.URI; import tools.vitruv.change.composite.description.CompositeChange; import tools.vitruv.change.composite.description.TransactionalChange; import tools.vitruv.change.composite.description.VitruviusChange; @@ -15,27 +12,36 @@ import tools.vitruv.framework.remote.common.json.JsonFieldName; import tools.vitruv.framework.remote.common.util.ResourceUtil; +/** A serializer for {@link VitruviusChange}. */ @SuppressWarnings("rawtypes") public class VitruviusChangeSerializer extends JsonSerializer { - @Override - public void serialize(VitruviusChange vitruviusChange, JsonGenerator generator, SerializerProvider provider) throws IOException { - generator.writeStartObject(); - generator.writeStringField(JsonFieldName.CHANGE_TYPE, ChangeType.getChangeTypeOf(vitruviusChange).toString()); - if (vitruviusChange instanceof TransactionalChange tc) { - var changesResource = ResourceUtil.createResourceWith(URI.createURI(JsonFieldName.TEMP_VALUE), tc.getEChanges()); - generator.writeFieldName(JsonFieldName.E_CHANGES); - generator.writeObject(changesResource); - generator.writeObjectField(JsonFieldName.U_INTERACTIONS,tc.getUserInteractions()); - } else if (vitruviusChange instanceof CompositeChange cc) { - var changes = cc.getChanges(); - generator.writeArrayFieldStart(JsonFieldName.V_CHANGES); - for (var change : changes) { - generator.writeObject(change); - } - generator.writeEndArray(); - } else { - throw new UnsupportedOperationException("Change serialization of type " + vitruviusChange.getClass().getName() + " not implemented!"); - } - generator.writeEndObject(); + @Override + public void serialize( + VitruviusChange vitruviusChange, JsonGenerator generator, SerializerProvider provider) + throws IOException { + generator.writeStartObject(); + generator.writeStringField( + JsonFieldName.CHANGE_TYPE, ChangeType.getChangeTypeOf(vitruviusChange).toString()); + if (vitruviusChange instanceof TransactionalChange tc) { + var changesResource = + ResourceUtil.createResourceWith( + URI.createURI(JsonFieldName.TEMP_VALUE), tc.getEChanges()); + generator.writeFieldName(JsonFieldName.E_CHANGES); + generator.writeObject(changesResource); + generator.writeObjectField(JsonFieldName.U_INTERACTIONS, tc.getUserInteractions()); + } else if (vitruviusChange instanceof CompositeChange cc) { + var changes = cc.getChanges(); + generator.writeArrayFieldStart(JsonFieldName.V_CHANGES); + for (var change : changes) { + generator.writeObject(change); + } + generator.writeEndArray(); + } else { + throw new UnsupportedOperationException( + "Change serialization of type " + + vitruviusChange.getClass().getName() + + " not implemented!"); } + generator.writeEndObject(); + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/common/rest/constants/ContentType.java b/remote/src/main/java/tools/vitruv/framework/remote/common/rest/constants/ContentType.java index df580b4..415b342 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/common/rest/constants/ContentType.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/common/rest/constants/ContentType.java @@ -1,10 +1,14 @@ package tools.vitruv.framework.remote.common.rest.constants; +/** A class holding constants for content types used in REST communication. */ public final class ContentType { - public static final String APPLICATION_JSON = "application/json"; - public static final String TEXT_PLAIN = "text/plain"; - - private ContentType() throws InstantiationException { - throw new InstantiationException("Cannot be instantiated"); - } + /** The content type for JSON data. */ + public static final String APPLICATION_JSON = "application/json"; + + /** The content type for plain text data. */ + public static final String TEXT_PLAIN = "text/plain"; + + private ContentType() throws InstantiationException { + throw new InstantiationException("Cannot be instantiated"); + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/common/rest/constants/EndpointPath.java b/remote/src/main/java/tools/vitruv/framework/remote/common/rest/constants/EndpointPath.java index 08518ca..1137e4c 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/common/rest/constants/EndpointPath.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/common/rest/constants/EndpointPath.java @@ -1,14 +1,29 @@ package tools.vitruv.framework.remote.common.rest.constants; +/** Constants for endpoint paths used in the Vitruvius remote framework. */ public final class EndpointPath { - public static final String HEALTH = "/health"; - public static final String VIEW_TYPES = "/vsum/view/types"; - public static final String VIEW_SELECTOR = "/vsum/view/selector"; - public static final String VIEW = "/vsum/view"; - public static final String IS_VIEW_CLOSED = "/vsum/view/closed"; - public static final String IS_VIEW_OUTDATED = "/vsum/view/outdated"; - - private EndpointPath() throws InstantiationException { - throw new InstantiationException("Cannot be instantiated"); - } + /** The health check endpoint path. */ + public static final String HEALTH = "/health"; + + /** The endpoint path for view types. */ + public static final String VIEW_TYPES = "/vsum/view/types"; + + /** The endpoint path for view selectors. */ + public static final String VIEW_SELECTOR = "/vsum/view/selector"; + + /** The endpoint path for views. */ + public static final String VIEW = "/vsum/view"; + + /** The endpoint path to check if a view is closed. */ + public static final String IS_VIEW_CLOSED = "/vsum/view/closed"; + + /** The endpoint path to check if a view is outdated. */ + public static final String IS_VIEW_OUTDATED = "/vsum/view/outdated"; + + /** The endpoint path for deriving changes. */ + public static final String CHANGE_DERIVING = "/vsum/view/derive-changes"; + + private EndpointPath() throws InstantiationException { + throw new InstantiationException("Cannot be instantiated"); + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/common/rest/constants/Header.java b/remote/src/main/java/tools/vitruv/framework/remote/common/rest/constants/Header.java index f05faea..758e6a0 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/common/rest/constants/Header.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/common/rest/constants/Header.java @@ -1,12 +1,20 @@ package tools.vitruv.framework.remote.common.rest.constants; +/** Constants for HTTP headers used in the Vitruv Remote Framework. */ public final class Header { - public static final String CONTENT_TYPE = "Content-Type"; - public static final String VIEW_UUID = "View-UUID"; - public static final String SELECTOR_UUID = "Selector-UUID"; - public static final String VIEW_TYPE = "View-Type"; - - private Header() throws InstantiationException { - throw new InstantiationException("Cannot be instantiated"); - } + /** The Content-Type header key. */ + public static final String CONTENT_TYPE = "Content-Type"; + + /** The View-UUID header key. */ + public static final String VIEW_UUID = "View-UUID"; + + /** The Selector-UUID header key. */ + public static final String SELECTOR_UUID = "Selector-UUID"; + + /** The View-Type header key. */ + public static final String VIEW_TYPE = "View-Type"; + + private Header() throws InstantiationException { + throw new InstantiationException("Cannot be instantiated"); + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/server/VirtualModelInitializer.java b/remote/src/main/java/tools/vitruv/framework/remote/server/VirtualModelInitializer.java index a0bb6bf..53cb4ec 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/server/VirtualModelInitializer.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/server/VirtualModelInitializer.java @@ -2,16 +2,14 @@ import tools.vitruv.framework.vsum.VirtualModel; -/** - * Interface for virtual model initialization. - */ +/** Interface for virtual model initialization. */ @FunctionalInterface -public interface VirtualModelInitializer { - - /** - * Initializes the virtual model and returns it. - * - * @return the initialized model. - */ - VirtualModel init(); +public interface VirtualModelInitializer { + + /** + * Initializes the virtual model and returns it. + * + * @return the initialized model. + */ + VirtualModel init(); } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/server/VitruvServer.java b/remote/src/main/java/tools/vitruv/framework/remote/server/VitruvServer.java index f9526b1..ca530b4 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/server/VitruvServer.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/server/VitruvServer.java @@ -1,70 +1,69 @@ package tools.vitruv.framework.remote.server; import java.io.IOException; - +import java.util.List; import tools.vitruv.framework.remote.common.DefaultConnectionSettings; import tools.vitruv.framework.remote.common.json.JsonMapper; import tools.vitruv.framework.remote.server.http.java.VitruvJavaHttpServer; +import tools.vitruv.framework.remote.server.rest.PathEndointCollector; import tools.vitruv.framework.remote.server.rest.endpoints.EndpointsProvider; import tools.vitruv.framework.vsum.VirtualModel; /** - * A Vitruvius server wraps a REST-based API around a {@link VirtualModel VSUM}. Therefore, - * it takes a {@link VirtualModelInitializer} which is responsible to create an instance - * of a {@link VirtualModel virtual model}. Once the server is started, the API can be used by the - * Vitruvius client to perform remote actions on the VSUM. + * A Vitruvius server wraps a REST-based API around a {@link VirtualModel VSUM}. Therefore, it takes + * a {@link VirtualModelInitializer} which is responsible to create an instance of a {@link + * VirtualModel virtual model}. Once the server is started, the API can be used by the Vitruvius + * client to perform remote actions on the VSUM. */ public class VitruvServer { - private final VitruvJavaHttpServer server; + private final VitruvJavaHttpServer server; + + /** + * Creates a new {@link VitruvServer} using the given {@link VirtualModelInitializer}. Sets host + * name or IP address and port which are used to open the server. + * + * @param modelInitializer The initializer which creates an {@link VirtualModel}. + * @param port The port to open to server on. + * @param hostOrIp The host name or IP address to which the server is bound. + */ + public VitruvServer(VirtualModelInitializer modelInitializer, int port, String hostOrIp) + throws IOException { + VirtualModel model = modelInitializer.init(); + JsonMapper mapper = new JsonMapper(model.getFolder()); + List endpoints = EndpointsProvider.getAllEndpoints(model, mapper); - /** - * Creates a new {@link VitruvServer} using the given {@link VirtualModelInitializer}. - * Sets host name or IP address and port which are used to open the server. - * - * @param modelInitializer The initializer which creates an {@link VirtualModel}. - * @param port The port to open to server on. - * @param hostOrIp The host name or IP address to which the server is bound. - */ - public VitruvServer(VirtualModelInitializer modelInitializer, int port, String hostOrIp) throws IOException { - var model = modelInitializer.init(); - var mapper = new JsonMapper(model.getFolder()); - var endpoints = EndpointsProvider.getAllEndpoints(model, mapper); + this.server = new VitruvJavaHttpServer(hostOrIp, port, endpoints); + } - this.server = new VitruvJavaHttpServer(hostOrIp, port, endpoints); - } - - /** - * Creates a new {@link VitruvServer} using the given {@link VirtualModelInitializer}. - * Sets the port which is used to open the server on to the given one. - * - * @param modelInitializer The initializer which creates an {@link VirtualModel}. - * @param port The port to open to server on. - */ - public VitruvServer(VirtualModelInitializer modelInitializer, int port) throws IOException { - this(modelInitializer, port, DefaultConnectionSettings.STD_HOST); - } + /** + * Creates a new {@link VitruvServer} using the given {@link VirtualModelInitializer}. Sets the + * port which is used to open the server on to the given one. + * + * @param modelInitializer The initializer which creates an {@link VirtualModel}. + * @param port The port to open to server on. + */ + public VitruvServer(VirtualModelInitializer modelInitializer, int port) throws IOException { + this(modelInitializer, port, DefaultConnectionSettings.STD_HOST); + } - /** - * Creates a new {@link VitruvServer} using the given {@link VirtualModelInitializer}. - * Sets the port which is used to open the server on to 8080. - * - * @param modelInitializer The initializer which creates an {@link tools.vitruv.framework.vsum.internal.InternalVirtualModel}. - */ - public VitruvServer(VirtualModelInitializer modelInitializer) throws IOException { - this(modelInitializer, DefaultConnectionSettings.STD_PORT); - } + /** + * Creates a new {@link VitruvServer} using the given {@link VirtualModelInitializer}. Sets the + * port which is used to open the server on to 8080. + * + * @param modelInitializer The initializer which creates an {@link + * tools.vitruv.framework.vsum.internal.InternalVirtualModel}. + */ + public VitruvServer(VirtualModelInitializer modelInitializer) throws IOException { + this(modelInitializer, DefaultConnectionSettings.STD_PORT); + } - /** - * Starts the Vitruvius server. - */ - public void start() { - server.start(); - } + /** Starts the Vitruvius server. */ + public void start() { + server.start(); + } - /** - * Stops the Vitruvius server. - */ - public void stop() { - server.stop(); - } + /** Stops the Vitruvius server. */ + public void stop() { + server.stop(); + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/server/exception/ServerHaltingException.java b/remote/src/main/java/tools/vitruv/framework/remote/server/exception/ServerHaltingException.java index c874352..b605f0b 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/server/exception/ServerHaltingException.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/server/exception/ServerHaltingException.java @@ -1,20 +1,32 @@ package tools.vitruv.framework.remote.server.exception; /** - * Represents an exception which should be thrown when the processing of a REST-request is halted due to an error. - * An HTTP-status code must be provided, since this exception is caught by the - * {@link tools.vitruv.framework.remote.server.VitruvServer Server} in order to create error response messages. + * Represents an exception which should be thrown when the processing of a REST-request is halted + * due to an error. An HTTP-status code must be provided, since this exception is caught by the + * {@link tools.vitruv.framework.remote.server.VitruvServer Server} in order to create error + * response messages. */ public class ServerHaltingException extends RuntimeException { - private final int statusCode; + private final int statusCode; - public ServerHaltingException(int statusCode, String msg) { - super(msg); - this.statusCode = statusCode; - } + /** + * Creates a new {@link ServerHaltingException}. + * + * @param statusCode the HTTP status code representing the error + * @param msg the error message + */ + public ServerHaltingException(int statusCode, String msg) { + super(msg); + this.statusCode = statusCode; + } - public int getStatusCode() { - return statusCode; - } + /** + * Returns the HTTP status code representing the error. + * + * @return the HTTP status code + */ + public int getStatusCode() { + return statusCode; + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/server/exception/package-info.java b/remote/src/main/java/tools/vitruv/framework/remote/server/exception/package-info.java index 75a10c7..af3f6cd 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/server/exception/package-info.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/server/exception/package-info.java @@ -1,4 +1,2 @@ -/** - * Defines exceptions for the Vitruvius server. - */ +/** Defines exceptions for the Vitruvius server. */ package tools.vitruv.framework.remote.server.exception; diff --git a/remote/src/main/java/tools/vitruv/framework/remote/server/http/HttpWrapper.java b/remote/src/main/java/tools/vitruv/framework/remote/server/http/HttpWrapper.java index 3f5d425..1bb3dc1 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/server/http/HttpWrapper.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/server/http/HttpWrapper.java @@ -2,51 +2,53 @@ import java.io.IOException; -/** - * This interface wraps an HTTP request/response from the underlying HTTP server implementation. - */ +/** This interface wraps an HTTP request/response from the underlying HTTP server implementation. */ public interface HttpWrapper { - /** - * Returns a response header. - * - * @param header Name of the header. - * @return The value of the header. - */ - String getRequestHeader(String header); - /** - * Returns the request body converted to a String. - * - * @return The request body as String. - * @throws IOException If the body cannot be read or if the conversion fails. - */ - String getRequestBodyAsString() throws IOException; - - /** - * Adds a value to the response header. - * - * @param header Name of the header. - * @param value The value of the header to add. - */ - void addResponseHeader(String header, String value); - /** - * Sets the content type of the response. - * - * @param type The content type. - */ - void setContentType(String type); - /** - * Sends an HTTP response without a body. - * - * @param responseCode The status code of the response. - * @throws IOException If the response cannot be sent. - */ - void sendResponse(int responseCode) throws IOException; - /** - * Sends an HTTP response with a body. - * - * @param responseCode The status code of the response. - * @param body The body of the response. - * @throws IOException If the response cannot be sent. - */ - void sendResponse(int responseCode, byte[] body) throws IOException; + /** + * Returns a response header. + * + * @param header Name of the header. + * @return The value of the header. + */ + String getRequestHeader(String header); + + /** + * Returns the request body converted to a String. + * + * @return The request body as String. + * @throws IOException If the body cannot be read or if the conversion fails. + */ + String getRequestBodyAsString() throws IOException; + + /** + * Adds a value to the response header. + * + * @param header Name of the header. + * @param value The value of the header to add. + */ + void addResponseHeader(String header, String value); + + /** + * Sets the content type of the response. + * + * @param type The content type. + */ + void setContentType(String type); + + /** + * Sends an HTTP response without a body. + * + * @param responseCode The status code of the response. + * @throws IOException If the response cannot be sent. + */ + void sendResponse(int responseCode) throws IOException; + + /** + * Sends an HTTP response with a body. + * + * @param responseCode The status code of the response. + * @param body The body of the response. + * @throws IOException If the response cannot be sent. + */ + void sendResponse(int responseCode, byte[] body) throws IOException; } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/server/http/java/HttpExchangeWrapper.java b/remote/src/main/java/tools/vitruv/framework/remote/server/http/java/HttpExchangeWrapper.java index 0bc9745..ad63796 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/server/http/java/HttpExchangeWrapper.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/server/http/java/HttpExchangeWrapper.java @@ -1,55 +1,56 @@ package tools.vitruv.framework.remote.server.http.java; import com.sun.net.httpserver.HttpExchange; - -import tools.vitruv.framework.remote.common.rest.constants.Header; -import tools.vitruv.framework.remote.server.http.HttpWrapper; - import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.List; +import tools.vitruv.framework.remote.common.rest.constants.Header; +import tools.vitruv.framework.remote.server.http.HttpWrapper; -/** - * This is an implementation of the {@link HttpWrapper} for the Java built-in HTTP server. - */ +/** This is an implementation of the {@link HttpWrapper} for the Java built-in HTTP server. */ class HttpExchangeWrapper implements HttpWrapper { - private final HttpExchange exchange; - - HttpExchangeWrapper(HttpExchange exchange) { - this.exchange = exchange; - } - - @Override - public void addResponseHeader(String header, String value) { - exchange.getResponseHeaders().add(header, value); - } - - @Override - public void setContentType(String type) { - exchange.getResponseHeaders().replace(Header.CONTENT_TYPE, List.of(type)); - } - - @Override - public String getRequestHeader(String header) { - return exchange.getRequestHeaders().getFirst(header); - } - - @Override - public String getRequestBodyAsString() throws IOException { - return new String(exchange.getRequestBody().readAllBytes(), StandardCharsets.UTF_8); - } - - @Override - public void sendResponse(int responseCode) throws IOException { - exchange.sendResponseHeaders(responseCode, -1); - } - - @Override - public void sendResponse(int responseCode, byte[] body) throws IOException { - exchange.sendResponseHeaders(responseCode, body.length); - var outputStream = exchange.getResponseBody(); - outputStream.write(body); - outputStream.flush(); - outputStream.close(); - } + private final HttpExchange exchange; + + /** + * Creates a new {@link HttpExchangeWrapper}. + * + * @param exchange The {@link HttpExchange} to wrap. + */ + public HttpExchangeWrapper(HttpExchange exchange) { + this.exchange = exchange; + } + + @Override + public void addResponseHeader(String header, String value) { + exchange.getResponseHeaders().add(header, value); + } + + @Override + public void setContentType(String type) { + exchange.getResponseHeaders().replace(Header.CONTENT_TYPE, List.of(type)); + } + + @Override + public String getRequestHeader(String header) { + return exchange.getRequestHeaders().getFirst(header); + } + + @Override + public String getRequestBodyAsString() throws IOException { + return new String(exchange.getRequestBody().readAllBytes(), StandardCharsets.UTF_8); + } + + @Override + public void sendResponse(int responseCode) throws IOException { + exchange.sendResponseHeaders(responseCode, -1); + } + + @Override + public void sendResponse(int responseCode, byte[] body) throws IOException { + exchange.sendResponseHeaders(responseCode, body.length); + var outputStream = exchange.getResponseBody(); + outputStream.write(body); + outputStream.flush(); + outputStream.close(); + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/server/http/java/RequestHandler.java b/remote/src/main/java/tools/vitruv/framework/remote/server/http/java/RequestHandler.java index 263f624..5672199 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/server/http/java/RequestHandler.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/server/http/java/RequestHandler.java @@ -1,62 +1,67 @@ package tools.vitruv.framework.remote.server.http.java; +import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR; +import static java.net.HttpURLConnection.HTTP_NOT_FOUND; +import static java.net.HttpURLConnection.HTTP_OK; + import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; - +import java.io.IOException; +import java.nio.charset.StandardCharsets; import tools.vitruv.framework.remote.common.rest.constants.ContentType; import tools.vitruv.framework.remote.server.exception.ServerHaltingException; import tools.vitruv.framework.remote.server.rest.PathEndointCollector; -import java.nio.charset.StandardCharsets; - -import static java.net.HttpURLConnection.*; - -import java.io.IOException; - -/** - * Represents an {@link HttpHandler}. - */ +/** Represents an {@link HttpHandler}. */ class RequestHandler implements HttpHandler { - private PathEndointCollector endpoints; + private PathEndointCollector endpoints; - RequestHandler(PathEndointCollector endpoints) { - this.endpoints = endpoints; - } + /** + * Creates a new {@link RequestHandler}. + * + * @param endpoints The endpoint collector providing the endpoints to handle requests. + */ + RequestHandler(PathEndointCollector endpoints) { + this.endpoints = endpoints; + } - /** - * Handles the request when this end point is called. - * - * @param exchange An object encapsulating the HTTP request and response. - */ - @Override - public void handle(HttpExchange exchange) { - var method = exchange.getRequestMethod(); - var wrapper = new HttpExchangeWrapper(exchange); - try { - var response = switch (method) { - case "GET" -> endpoints.getEndpoint().process(wrapper); - case "PUT" -> endpoints.putEndpoint().process(wrapper); - case "POST" -> endpoints.postEndpoint().process(wrapper); - case "PATCH" -> endpoints.patchEndpoint().process(wrapper); - case "DELETE" -> endpoints.deleteEndpoint().process(wrapper); - default -> throw new ServerHaltingException(HTTP_NOT_FOUND, "Request method not supported!"); - }; - if (response != null) { - wrapper.sendResponse(HTTP_OK, response.getBytes(StandardCharsets.UTF_8)); - } else { - wrapper.sendResponse(HTTP_OK); - } - } catch (Exception exception) { - var statusCode = HTTP_INTERNAL_ERROR; - if (exception instanceof ServerHaltingException haltingException) { - statusCode = haltingException.getStatusCode(); - } - wrapper.setContentType(ContentType.TEXT_PLAIN); - try { - wrapper.sendResponse(statusCode, exception.getMessage().getBytes(StandardCharsets.UTF_8)); - } catch (IOException e) { - throw new IllegalStateException("Sending a response (" + statusCode + " " + exception.getMessage() + ") failed.", e); - } - } + /** + * Handles the request when this end point is called. + * + * @param exchange An object encapsulating the HTTP request and response. + */ + @Override + public void handle(HttpExchange exchange) { + var method = exchange.getRequestMethod(); + var wrapper = new HttpExchangeWrapper(exchange); + try { + var response = + switch (method) { + case "GET" -> endpoints.getEndpoint().process(wrapper); + case "PUT" -> endpoints.putEndpoint().process(wrapper); + case "POST" -> endpoints.postEndpoint().process(wrapper); + case "PATCH" -> endpoints.patchEndpoint().process(wrapper); + case "DELETE" -> endpoints.deleteEndpoint().process(wrapper); + default -> + throw new ServerHaltingException(HTTP_NOT_FOUND, "Request method not supported!"); + }; + if (response != null) { + wrapper.sendResponse(HTTP_OK, response.getBytes(StandardCharsets.UTF_8)); + } else { + wrapper.sendResponse(HTTP_OK); + } + } catch (IOException | ServerHaltingException exception) { + var statusCode = HTTP_INTERNAL_ERROR; + if (exception instanceof ServerHaltingException haltingException) { + statusCode = haltingException.getStatusCode(); + } + wrapper.setContentType(ContentType.TEXT_PLAIN); + try { + wrapper.sendResponse(statusCode, exception.getMessage().getBytes(StandardCharsets.UTF_8)); + } catch (IOException e) { + throw new IllegalStateException( + "Sending a response (" + statusCode + " " + exception.getMessage() + ") failed.", e); + } } + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/server/http/java/VitruvJavaHttpServer.java b/remote/src/main/java/tools/vitruv/framework/remote/server/http/java/VitruvJavaHttpServer.java index 0b067ac..0d8f47e 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/server/http/java/VitruvJavaHttpServer.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/server/http/java/VitruvJavaHttpServer.java @@ -7,18 +7,24 @@ import tools.vitruv.framework.remote.server.rest.PathEndointCollector; public class VitruvJavaHttpServer { - private final HttpServer server; + private final HttpServer server; - public VitruvJavaHttpServer(String host, int port, List endpoints) throws IOException { - this.server = HttpServer.create(new InetSocketAddress(host, port), 0); - endpoints.forEach(endp -> server.createContext(endp.path(), new RequestHandler(endp))); - } + /** + * Creates a Vitruvius HTTP server on the given host and port, registering the given endpoints. + */ + public VitruvJavaHttpServer(String host, int port, List endpoints) + throws IOException { + this.server = HttpServer.create(new InetSocketAddress(host, port), 0); + endpoints.forEach(endp -> server.createContext(endp.path(), new RequestHandler(endp))); + } - public void start() { - server.start(); - } + /** Starts the Vitruvius server. */ + public void start() { + server.start(); + } - public void stop() { - server.stop(0); - } + /** Stops the Vitruvius server. */ + public void stop() { + server.stop(0); + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/server/http/java/package-info.java b/remote/src/main/java/tools/vitruv/framework/remote/server/http/java/package-info.java index 99a54fb..1c07497 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/server/http/java/package-info.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/server/http/java/package-info.java @@ -1,4 +1,2 @@ -/** - * This package provides the built-in Java HTTP server for Vitruvius. - */ +/** This package provides the built-in Java HTTP server for Vitruvius. */ package tools.vitruv.framework.remote.server.http.java; diff --git a/remote/src/main/java/tools/vitruv/framework/remote/server/http/package-info.java b/remote/src/main/java/tools/vitruv/framework/remote/server/http/package-info.java index 64df053..8faf377 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/server/http/package-info.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/server/http/package-info.java @@ -1,4 +1,2 @@ -/** - * This package serves interfaces to wrap HTTP servers from the actual REST end points. - */ +/** This package serves interfaces to wrap HTTP servers from the actual REST end points. */ package tools.vitruv.framework.remote.server.http; diff --git a/remote/src/main/java/tools/vitruv/framework/remote/server/package-info.java b/remote/src/main/java/tools/vitruv/framework/remote/server/package-info.java index 0fd8244..f451f88 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/server/package-info.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/server/package-info.java @@ -1,4 +1,2 @@ -/** - * This package contains the Vitruvius server implementation. - */ +/** This package contains the Vitruvius server implementation. */ package tools.vitruv.framework.remote.server; diff --git a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/DeleteEndpoint.java b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/DeleteEndpoint.java index c8e30e6..eced41c 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/DeleteEndpoint.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/DeleteEndpoint.java @@ -1,4 +1,4 @@ package tools.vitruv.framework.remote.server.rest; -public interface DeleteEndpoint extends RestEndpoint { -} +/** Marker interface for REST endpoints that handle HTTP DELETE requests. */ +public interface DeleteEndpoint extends RestEndpoint {} diff --git a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/GetEndpoint.java b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/GetEndpoint.java index b3619b1..dcc48f2 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/GetEndpoint.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/GetEndpoint.java @@ -1,4 +1,4 @@ package tools.vitruv.framework.remote.server.rest; -public interface GetEndpoint extends RestEndpoint { -} +/** Marker interface for REST endpoints that handle HTTP GET requests. */ +public interface GetEndpoint extends RestEndpoint {} diff --git a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/PatchEndpoint.java b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/PatchEndpoint.java index d4f45e8..f649d9d 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/PatchEndpoint.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/PatchEndpoint.java @@ -1,4 +1,4 @@ package tools.vitruv.framework.remote.server.rest; -public interface PatchEndpoint extends RestEndpoint { -} +/** Marker interface for REST endpoints that handle HTTP PATCH requests. */ +public interface PatchEndpoint extends RestEndpoint {} diff --git a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/PathEndointCollector.java b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/PathEndointCollector.java index b480e40..c1405ec 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/PathEndointCollector.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/PathEndointCollector.java @@ -1,9 +1,10 @@ package tools.vitruv.framework.remote.server.rest; +/** Collects all REST endpoints for a specific path. */ public record PathEndointCollector( - String path, - GetEndpoint getEndpoint, - PostEndpoint postEndpoint, - PutEndpoint putEndpoint, - PatchEndpoint patchEndpoint, - DeleteEndpoint deleteEndpoint) {} + String path, + GetEndpoint getEndpoint, + PostEndpoint postEndpoint, + PutEndpoint putEndpoint, + PatchEndpoint patchEndpoint, + DeleteEndpoint deleteEndpoint) {} diff --git a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/PostEndpoint.java b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/PostEndpoint.java index 58c32eb..813514d 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/PostEndpoint.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/PostEndpoint.java @@ -1,4 +1,4 @@ package tools.vitruv.framework.remote.server.rest; -public interface PostEndpoint extends RestEndpoint { -} +/** Marker interface for REST endpoints that handle HTTP POST requests. */ +public interface PostEndpoint extends RestEndpoint {} diff --git a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/PutEndpoint.java b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/PutEndpoint.java index a79c984..f2e96fe 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/PutEndpoint.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/PutEndpoint.java @@ -1,4 +1,4 @@ package tools.vitruv.framework.remote.server.rest; -public interface PutEndpoint extends RestEndpoint { -} +/** Marker interface for REST endpoints that handle HTTP PUT requests. */ +public interface PutEndpoint extends RestEndpoint {} diff --git a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/RestEndpoint.java b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/RestEndpoint.java index d1c3e10..90cefbd 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/RestEndpoint.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/RestEndpoint.java @@ -1,37 +1,40 @@ package tools.vitruv.framework.remote.server.rest; +import static java.net.HttpURLConnection.HTTP_BAD_METHOD; +import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR; + import tools.vitruv.framework.remote.server.exception.ServerHaltingException; import tools.vitruv.framework.remote.server.http.HttpWrapper; -import static java.net.HttpURLConnection.*; - -/** - * Represents an REST endpoint. - */ +/** Represents an REST endpoint. */ public interface RestEndpoint { - /** - * Processes a given HTTP request. - * - * @param wrapper An object wrapping an HTTP request/response. - * @throws ServerHaltingException If an internal error occurred. - */ - String process(HttpWrapper wrapper) throws ServerHaltingException; + /** + * Processes a given HTTP request. + * + * @param wrapper An object wrapping an HTTP request/response. + * @throws ServerHaltingException If an internal error occurred. + */ + String process(HttpWrapper wrapper) throws ServerHaltingException; - /** - * Halts the execution of the requested endpoint and returns the status code NOT FOUND with the given message. - * - * @param msg A message containing the reason of halting the execution. - */ - default ServerHaltingException notFound(String msg) { - return new ServerHaltingException(HTTP_BAD_METHOD, msg); - } + /** + * Halts the execution of the requested endpoint and returns the status code NOT FOUND with the + * given message. + * + * @param msg A message containing the reason of halting the execution. + * @return A ServerHaltingException representing the NOT FOUND error. + */ + default ServerHaltingException notFound(String msg) { + return new ServerHaltingException(HTTP_BAD_METHOD, msg); + } - /** - * Halts the execution of the requested endpoint and returns the status code INTERNAL SERVER ERROR with the given message. - * - * @param msg A message containing the reason of halting the execution. - */ - default ServerHaltingException internalServerError(String msg) { - return new ServerHaltingException(HTTP_INTERNAL_ERROR, msg); - } + /** + * Halts the execution of the requested endpoint and returns the status code INTERNAL SERVER ERROR + * with the given message. + * + * @param msg A message containing the reason of halting the execution. + * @return A ServerHaltingException representing the INTERNAL SERVER ERROR. + */ + default ServerHaltingException internalServerError(String msg) { + return new ServerHaltingException(HTTP_INTERNAL_ERROR, msg); + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/Cache.java b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/Cache.java index 040dee8..1fedf6d 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/Cache.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/Cache.java @@ -1,56 +1,108 @@ package tools.vitruv.framework.remote.server.rest.endpoints; +import com.google.common.collect.BiMap; import java.util.HashMap; import java.util.Map; - -import com.google.common.collect.BiMap; import org.eclipse.emf.ecore.EObject; import tools.vitruv.framework.views.View; import tools.vitruv.framework.views.ViewSelector; /** - * A global cache holding {@link View}s, {@link ViewSelector}s and mappings of the form UUID {@literal <->} {@link EObject}. + * A global cache holding {@link View}s, {@link ViewSelector}s and mappings of the form UUID and the + * {@link EObject}. */ public class Cache { - private Cache() throws InstantiationException { - throw new InstantiationException("Cannot be instantiated"); - } - - private static final Map viewCache = new HashMap<>(); - private static final Map selectorCache = new HashMap<>(); - private static final Map> perSelectorUuidToEObjectMapping = new HashMap<>(); - - public static void addView(String uuid, View view) { - viewCache.put(uuid, view); - } - - public static View getView(String uuid) { - return viewCache.get(uuid); - } - - public static View removeView(String uuid) { - return viewCache.remove(uuid); - } - - public static void addSelectorWithMapping(String selectorUuid, ViewSelector selector, BiMap mapping) { - selectorCache.put(selectorUuid, selector); - perSelectorUuidToEObjectMapping.put(selectorUuid, mapping); - } - - public static ViewSelector getSelector(String selectorUuid) { - return selectorCache.get(selectorUuid); - } - - public static EObject getEObjectFromMapping(String selectorUuid, String objectUuid) { - return perSelectorUuidToEObjectMapping.get(selectorUuid).get(objectUuid); - } - - public static String getUuidFromMapping(String selectorUuid, EObject eObject) { - return perSelectorUuidToEObjectMapping.get(selectorUuid).inverse().get(eObject); - } - - public static void removeSelectorAndMapping(String selectorUuid) { - perSelectorUuidToEObjectMapping.remove(selectorUuid); - selectorCache.remove(selectorUuid); - } + private Cache() throws InstantiationException { + throw new InstantiationException("Cannot be instantiated"); + } + + private static final Map viewCache = new HashMap<>(); + private static final Map selectorCache = new HashMap<>(); + private static final Map> perSelectorUuidToEObjectMapping = + new HashMap<>(); + + /** + * Adds a view to the cache with the given uuid. + * + * @param uuid The uuid of the view. + * @param view The view to add. + */ + public static void addView(String uuid, View view) { + viewCache.put(uuid, view); + } + + /** + * Retrieves a view from the cache with the given uuid. + * + * @param uuid The uuid of the view. + * @return The view with the given uuid. + */ + public static View getView(String uuid) { + return viewCache.get(uuid); + } + + /** + * Removes a view from the cache with the given uuid. + * + * @param uuid The uuid of the view. + * @return The removed view. + */ + public static View removeView(String uuid) { + return viewCache.remove(uuid); + } + + /** + * Adds a selector and its corresponding EObject mapping to the cache. + * + * @param selectorUuid The uuid of the selector. + * @param selector The selector to add. + * @param mapping The mapping of the form String and the EObject. + */ + public static void addSelectorWithMapping( + String selectorUuid, ViewSelector selector, BiMap mapping) { + selectorCache.put(selectorUuid, selector); + perSelectorUuidToEObjectMapping.put(selectorUuid, mapping); + } + + /** + * Retrieves a selector from the cache with the given uuid. + * + * @param selectorUuid The uuid of the selector. + * @return The selector with the given uuid. + */ + public static ViewSelector getSelector(String selectorUuid) { + return selectorCache.get(selectorUuid); + } + + /** + * Retrieves an EObject from the mapping for the given selector uuid and object uuid. + * + * @param selectorUuid The uuid of the selector. + * @param objectUuid The uuid of the object. + * @return The EObject with the given uuid. + */ + public static EObject getEObjectFromMapping(String selectorUuid, String objectUuid) { + return perSelectorUuidToEObjectMapping.get(selectorUuid).get(objectUuid); + } + + /** + * Retrieves an object uuid from the mapping for the given selector uuid and EObject. + * + * @param selectorUuid The uuid of the selector. + * @param eObject The EObject to get the uuid for. + * @return The uuid of the given EObject. + */ + public static String getUuidFromMapping(String selectorUuid, EObject eObject) { + return perSelectorUuidToEObjectMapping.get(selectorUuid).inverse().get(eObject); + } + + /** + * Removes a selector and its corresponding EObject mapping from the cache. + * + * @param selectorUuid The uuid of the selector. + */ + public static void removeSelectorAndMapping(String selectorUuid) { + perSelectorUuidToEObjectMapping.remove(selectorUuid); + selectorCache.remove(selectorUuid); + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/ChangeDerivingEndpoint.java b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/ChangeDerivingEndpoint.java new file mode 100644 index 0000000..4656759 --- /dev/null +++ b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/ChangeDerivingEndpoint.java @@ -0,0 +1,140 @@ +package tools.vitruv.framework.remote.server.rest.endpoints; + +import static java.net.HttpURLConnection.HTTP_BAD_REQUEST; +import static java.net.HttpURLConnection.HTTP_CONFLICT; + +import com.fasterxml.jackson.core.JsonProcessingException; +import io.micrometer.core.instrument.Metrics; +import io.micrometer.core.instrument.Timer; +import java.io.IOException; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.ecore.resource.impl.ResourceImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import tools.vitruv.change.atomic.hid.HierarchicalId; +import tools.vitruv.change.atomic.root.InsertRootEObject; +import tools.vitruv.change.composite.description.VitruviusChange; +import tools.vitruv.change.composite.description.VitruviusChangeFactory; +import tools.vitruv.framework.remote.common.json.JsonMapper; +import tools.vitruv.framework.remote.common.rest.constants.Header; +import tools.vitruv.framework.remote.server.exception.ServerHaltingException; +import tools.vitruv.framework.remote.server.http.HttpWrapper; +import tools.vitruv.framework.remote.server.rest.PatchEndpoint; +import tools.vitruv.framework.views.changederivation.DefaultStateBasedChangeResolutionStrategy; +import tools.vitruv.framework.views.changederivation.StateBasedChangeResolutionStrategy; +import tools.vitruv.framework.views.impl.ModifiableView; +import tools.vitruv.framework.views.impl.ViewCreatingViewType; + +/** + * This endpoint applies {@link VitruviusChange}s to the VSUM that are derived from the new state + * given by the client. + */ +public class ChangeDerivingEndpoint implements PatchEndpoint { + private static final String ENDPOINT_METRIC_NAME = "vitruv.server.rest.deriving"; + private final JsonMapper mapper; + private final StateBasedChangeResolutionStrategy resolutionStrategy = + new DefaultStateBasedChangeResolutionStrategy(); + private final Logger logger = LoggerFactory.getLogger(ChangeDerivingEndpoint.class); + + /** + * Creates a new ChangeDerivingEndpoint. + * + * @param mapper The JSON mapper to use. + */ + public ChangeDerivingEndpoint(JsonMapper mapper) { + this.mapper = mapper; + } + + @SuppressWarnings("unchecked") + @Override + public String process(HttpWrapper wrapper) { + var view = Cache.getView(wrapper.getRequestHeader(Header.VIEW_UUID)); + if (view == null) { + throw notFound("View with given id not found!"); + } + + String body; + try { + body = wrapper.getRequestBodyAsString(); + } catch (IOException e) { + throw internalServerError(e.getMessage()); + } + + ResourceSet resourceSet; + var desTimer = Timer.start(Metrics.globalRegistry); + try { + resourceSet = mapper.deserialize(body, ResourceSet.class); + desTimer.stop(Metrics.timer(ENDPOINT_METRIC_NAME, "deserialization", "success")); + } catch (JsonProcessingException e) { + logger.warn("Failed to deserialize request body: {}", e.getMessage()); + desTimer.stop(Metrics.timer(ENDPOINT_METRIC_NAME, "deserialization", "failure")); + throw new ServerHaltingException(HTTP_BAD_REQUEST, e.getMessage()); + } + + var currentRessources = + view.getRootObjects().stream().map(EObject::eResource).distinct().toList(); + Map resourceMap = new HashMap<>(); + for (Resource r : currentRessources) { + resourceMap.put(r.getURI(), r); + } + + var allChanges = new LinkedList>(); + resourceSet + .getResources() + .forEach( + it -> { + var u = resourceMap.get(it.getURI()); + var changes = findChanges(u, it); + if (!changes.getEChanges().isEmpty()) { + allChanges.add(changes); + } + }); + + if (allChanges.isEmpty()) { + logger.info("No changes detected."); + return "[]"; + } + + @SuppressWarnings("rawtypes") + VitruviusChange change; + change = VitruviusChangeFactory.getInstance().createCompositeChange(allChanges); + + change + .getEChanges() + .forEach( + it -> { + if (it instanceof InsertRootEObject echange) { + echange.setResource(new ResourceImpl(URI.createURI(echange.getUri()))); + } + }); + + var type = (ViewCreatingViewType) view.getViewType(); + var propTimer = Timer.start(Metrics.globalRegistry); + try { + type.commitViewChanges((ModifiableView) view, change); + propTimer.stop(Metrics.timer(ENDPOINT_METRIC_NAME, "propagation", "success")); + return mapper.serialize(change); + } catch (RuntimeException e) { + propTimer.stop(Metrics.timer(ENDPOINT_METRIC_NAME, "propagation", "failure")); + throw new ServerHaltingException(HTTP_CONFLICT, "Changes rejected: " + e.getMessage()); + } catch (JsonProcessingException e) { + throw internalServerError(e.getMessage()); + } + } + + private VitruviusChange findChanges(Resource oldState, Resource newState) { + if (oldState == null) { + return resolutionStrategy.getChangeSequenceForCreated(newState); + } else if (newState == null) { + return resolutionStrategy.getChangeSequenceForDeleted(oldState); + } else { + return resolutionStrategy.getChangeSequenceBetween(newState, oldState); + } + } +} diff --git a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/ChangePropagationEndpoint.java b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/ChangePropagationEndpoint.java index b952c32..c8b8fc5 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/ChangePropagationEndpoint.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/ChangePropagationEndpoint.java @@ -1,78 +1,81 @@ package tools.vitruv.framework.remote.server.rest.endpoints; +import static java.net.HttpURLConnection.HTTP_BAD_REQUEST; +import static java.net.HttpURLConnection.HTTP_CONFLICT; + +import com.fasterxml.jackson.core.JsonProcessingException; +import io.micrometer.core.instrument.Metrics; +import io.micrometer.core.instrument.Timer; +import java.io.IOException; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.resource.impl.ResourceImpl; import tools.vitruv.change.atomic.root.InsertRootEObject; import tools.vitruv.change.composite.description.VitruviusChange; +import tools.vitruv.framework.remote.common.json.JsonMapper; +import tools.vitruv.framework.remote.common.rest.constants.Header; import tools.vitruv.framework.remote.server.exception.ServerHaltingException; import tools.vitruv.framework.remote.server.http.HttpWrapper; import tools.vitruv.framework.remote.server.rest.PatchEndpoint; -import tools.vitruv.framework.remote.common.json.JsonMapper; -import tools.vitruv.framework.remote.common.rest.constants.Header; import tools.vitruv.framework.views.impl.ModifiableView; import tools.vitruv.framework.views.impl.ViewCreatingViewType; -import java.io.IOException; - -import org.eclipse.emf.common.util.URI; -import org.eclipse.emf.ecore.resource.impl.ResourceImpl; +/** This endpoint applies given {@link VitruviusChange}s to the VSUM. */ +public class ChangePropagationEndpoint implements PatchEndpoint { + private static final String ENDPOINT_METRIC_NAME = "vitruv.server.rest.propagation"; + private final JsonMapper mapper; -import com.fasterxml.jackson.core.JsonProcessingException; + /** + * Creates a new ChangePropagationEndpoint. + * + * @param mapper The JSON mapper to use. + */ + public ChangePropagationEndpoint(JsonMapper mapper) { + this.mapper = mapper; + } -import io.micrometer.core.instrument.Metrics; -import io.micrometer.core.instrument.Timer; + @SuppressWarnings("unchecked") + @Override + public String process(HttpWrapper wrapper) { + var view = Cache.getView(wrapper.getRequestHeader(Header.VIEW_UUID)); + if (view == null) { + throw notFound("View with given id not found!"); + } -import static java.net.HttpURLConnection.*; + String body; + try { + body = wrapper.getRequestBodyAsString(); + } catch (IOException e) { + throw internalServerError(e.getMessage()); + } -/** - * This endpoint applies given {@link VitruviusChange}s to the VSUM. - */ -public class ChangePropagationEndpoint implements PatchEndpoint { - private static final String ENDPOINT_METRIC_NAME = "vitruv.server.rest.propagation"; - private final JsonMapper mapper; - - public ChangePropagationEndpoint(JsonMapper mapper) { - this.mapper = mapper; - } + @SuppressWarnings("rawtypes") + VitruviusChange change; + var desTimer = Timer.start(Metrics.globalRegistry); + try { + change = mapper.deserialize(body, VitruviusChange.class); + desTimer.stop(Metrics.timer(ENDPOINT_METRIC_NAME, "deserialization", "success")); + } catch (JsonProcessingException e) { + desTimer.stop(Metrics.timer(ENDPOINT_METRIC_NAME, "deserialization", "failure")); + throw new ServerHaltingException(HTTP_BAD_REQUEST, e.getMessage()); + } + change + .getEChanges() + .forEach( + it -> { + if (it instanceof InsertRootEObject echange) { + echange.setResource(new ResourceImpl(URI.createURI(echange.getUri()))); + } + }); - @SuppressWarnings("unchecked") - @Override - public String process(HttpWrapper wrapper) { - var view = Cache.getView(wrapper.getRequestHeader(Header.VIEW_UUID)); - if (view == null) { - throw notFound("View with given id not found!"); - } - - String body; - try { - body = wrapper.getRequestBodyAsString(); - } catch (IOException e) { - throw internalServerError(e.getMessage()); - } - - @SuppressWarnings("rawtypes") - VitruviusChange change; - var desTimer = Timer.start(Metrics.globalRegistry); - try { - change = mapper.deserialize(body, VitruviusChange.class); - desTimer.stop(Metrics.timer(ENDPOINT_METRIC_NAME, "deserialization", "success")); - } catch (JsonProcessingException e) { - desTimer.stop(Metrics.timer(ENDPOINT_METRIC_NAME, "deserialization", "failure")); - throw new ServerHaltingException(HTTP_BAD_REQUEST, e.getMessage()); - } - change.getEChanges().forEach(it -> { - if (it instanceof InsertRootEObject echange) { - echange.setResource(new ResourceImpl(URI.createURI(echange.getUri()))); - } - }); - - var type = (ViewCreatingViewType) view.getViewType(); - var propTimer = Timer.start(Metrics.globalRegistry); - try { - type.commitViewChanges((ModifiableView) view, change); - propTimer.stop(Metrics.timer(ENDPOINT_METRIC_NAME, "propagation", "success")); - } catch (RuntimeException e) { - propTimer.stop(Metrics.timer(ENDPOINT_METRIC_NAME, "propagation", "failure")); - throw new ServerHaltingException(HTTP_CONFLICT, "Changes rejected: " + e.getMessage()); - } - return null; + var type = (ViewCreatingViewType) view.getViewType(); + var propTimer = Timer.start(Metrics.globalRegistry); + try { + type.commitViewChanges((ModifiableView) view, change); + propTimer.stop(Metrics.timer(ENDPOINT_METRIC_NAME, "propagation", "success")); + } catch (RuntimeException e) { + propTimer.stop(Metrics.timer(ENDPOINT_METRIC_NAME, "propagation", "failure")); + throw new ServerHaltingException(HTTP_CONFLICT, "Changes rejected: " + e.getMessage()); } + return null; + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/CloseViewEndpoint.java b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/CloseViewEndpoint.java index 6700035..00173a1 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/CloseViewEndpoint.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/CloseViewEndpoint.java @@ -1,24 +1,23 @@ package tools.vitruv.framework.remote.server.rest.endpoints; +import tools.vitruv.framework.remote.common.rest.constants.Header; import tools.vitruv.framework.remote.server.http.HttpWrapper; import tools.vitruv.framework.remote.server.rest.DeleteEndpoint; -import tools.vitruv.framework.remote.common.rest.constants.Header; +import tools.vitruv.framework.views.View; -/** - * This endpoint closes a {@link tools.vitruv.framework.views.View View}. - */ +/** This endpoint closes a {@link tools.vitruv.framework.views.View View}. */ public class CloseViewEndpoint implements DeleteEndpoint { - @Override - public String process(HttpWrapper wrapper) { - var view = Cache.removeView(wrapper.getRequestHeader(Header.VIEW_UUID)); - if (view == null) { - throw notFound("View with given id not found!"); - } - try { - view.close(); - return null; - } catch (Exception e) { - throw internalServerError(e.getMessage()); - } + @Override + public String process(HttpWrapper wrapper) { + View view = Cache.removeView(wrapper.getRequestHeader(Header.VIEW_UUID)); + if (view == null) { + throw notFound("View with given id not found!"); + } + try { + view.close(); + return null; + } catch (Exception e) { + throw internalServerError(e.getMessage()); } + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/EndpointsProvider.java b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/EndpointsProvider.java index aa998f4..2ad3a16 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/EndpointsProvider.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/EndpointsProvider.java @@ -2,7 +2,6 @@ import java.util.ArrayList; import java.util.List; - import tools.vitruv.framework.remote.common.json.JsonMapper; import tools.vitruv.framework.remote.common.rest.constants.EndpointPath; import tools.vitruv.framework.remote.server.exception.ServerHaltingException; @@ -15,96 +14,120 @@ import tools.vitruv.framework.remote.server.rest.PutEndpoint; import tools.vitruv.framework.vsum.VirtualModel; +/** Provides all REST endpoints for the Vitruv server. */ public class EndpointsProvider { - public static List getAllEndpoints(VirtualModel virtualModel, JsonMapper mapper) { - var defaultEndpoints = getDefaultEndpoints(); - - List result = new ArrayList<>(); - result.add(new PathEndointCollector( - EndpointPath.HEALTH, - new HealthEndpoint(), - defaultEndpoints.postEndpoint(), - defaultEndpoints.putEndpoint(), - defaultEndpoints.patchEndpoint(), - defaultEndpoints.deleteEndpoint() - )); - result.add(new PathEndointCollector( - EndpointPath.IS_VIEW_CLOSED, - new IsViewClosedEndpoint(), - defaultEndpoints.postEndpoint(), - defaultEndpoints.putEndpoint(), - defaultEndpoints.patchEndpoint(), - defaultEndpoints.deleteEndpoint() - )); - result.add(new PathEndointCollector( - EndpointPath.IS_VIEW_OUTDATED, - new IsViewOutdatedEndpoint(), - defaultEndpoints.postEndpoint(), - defaultEndpoints.putEndpoint(), - defaultEndpoints.patchEndpoint(), - defaultEndpoints.deleteEndpoint() - )); - result.add(new PathEndointCollector( - EndpointPath.VIEW, - new UpdateViewEndpoint(mapper), - new ViewEndpoint(mapper), - defaultEndpoints.putEndpoint(), - new ChangePropagationEndpoint(mapper), - new CloseViewEndpoint() - )); - result.add(new PathEndointCollector( - EndpointPath.VIEW_SELECTOR, - new ViewSelectorEndpoint(virtualModel, mapper), - defaultEndpoints.postEndpoint(), - defaultEndpoints.putEndpoint(), - defaultEndpoints.patchEndpoint(), - defaultEndpoints.deleteEndpoint() - )); - result.add(new PathEndointCollector( - EndpointPath.VIEW_TYPES, - new ViewTypesEndpoint(virtualModel, mapper), - defaultEndpoints.postEndpoint(), - defaultEndpoints.putEndpoint(), - defaultEndpoints.patchEndpoint(), - defaultEndpoints.deleteEndpoint() - )); - - return result; - } - - private static PathEndointCollector getDefaultEndpoints() { - var getEndpoint = new GetEndpoint() { - @Override - public String process(HttpWrapper wrapper) throws ServerHaltingException { - throw notFound("Get mapping for this request path not found!"); - } + + /** + * Creates and returns all REST endpoints for the Vitruv server. + * + * @param virtualModel The virtual model to use. + * @param mapper The JSON mapper to use. + * @return A list of all REST endpoints. + */ + public static List getAllEndpoints( + VirtualModel virtualModel, JsonMapper mapper) { + var defaultEndpoints = getDefaultEndpoints(); + + List result = new ArrayList<>(); + result.add( + new PathEndointCollector( + EndpointPath.HEALTH, + new HealthEndpoint(), + defaultEndpoints.postEndpoint(), + defaultEndpoints.putEndpoint(), + defaultEndpoints.patchEndpoint(), + defaultEndpoints.deleteEndpoint())); + result.add( + new PathEndointCollector( + EndpointPath.IS_VIEW_CLOSED, + new IsViewClosedEndpoint(), + defaultEndpoints.postEndpoint(), + defaultEndpoints.putEndpoint(), + defaultEndpoints.patchEndpoint(), + defaultEndpoints.deleteEndpoint())); + result.add( + new PathEndointCollector( + EndpointPath.IS_VIEW_OUTDATED, + new IsViewOutdatedEndpoint(), + defaultEndpoints.postEndpoint(), + defaultEndpoints.putEndpoint(), + defaultEndpoints.patchEndpoint(), + defaultEndpoints.deleteEndpoint())); + result.add( + new PathEndointCollector( + EndpointPath.VIEW, + new UpdateViewEndpoint(mapper), + new ViewEndpoint(mapper), + defaultEndpoints.putEndpoint(), + new ChangePropagationEndpoint(mapper), + new CloseViewEndpoint())); + result.add( + new PathEndointCollector( + EndpointPath.VIEW_SELECTOR, + new ViewSelectorEndpoint(virtualModel, mapper), + defaultEndpoints.postEndpoint(), + defaultEndpoints.putEndpoint(), + defaultEndpoints.patchEndpoint(), + defaultEndpoints.deleteEndpoint())); + result.add( + new PathEndointCollector( + EndpointPath.VIEW_TYPES, + new ViewTypesEndpoint(virtualModel, mapper), + defaultEndpoints.postEndpoint(), + defaultEndpoints.putEndpoint(), + defaultEndpoints.patchEndpoint(), + defaultEndpoints.deleteEndpoint())); + result.add( + new PathEndointCollector( + EndpointPath.CHANGE_DERIVING, + defaultEndpoints.getEndpoint(), + defaultEndpoints.postEndpoint(), + defaultEndpoints.putEndpoint(), + new ChangeDerivingEndpoint(mapper), + defaultEndpoints.deleteEndpoint())); + + return result; + } + + private static PathEndointCollector getDefaultEndpoints() { + var getEndpoint = + new GetEndpoint() { + @Override + public String process(HttpWrapper wrapper) throws ServerHaltingException { + throw notFound("Get mapping for this request path not found!"); + } }; - var postEndpoint = new PostEndpoint() { - @Override - public String process(HttpWrapper wrapper) throws ServerHaltingException { - throw notFound("Post mapping for this request path not found!"); - } + var postEndpoint = + new PostEndpoint() { + @Override + public String process(HttpWrapper wrapper) throws ServerHaltingException { + throw notFound("Post mapping for this request path not found!"); + } }; - var patchEndpoint = new PatchEndpoint() { - @Override - public String process(HttpWrapper wrapper) throws ServerHaltingException { - throw notFound("Patch mapping for this request path not found!"); - } + var patchEndpoint = + new PatchEndpoint() { + @Override + public String process(HttpWrapper wrapper) throws ServerHaltingException { + throw notFound("Patch mapping for this request path not found!"); + } }; - var deleteEndpoint = new DeleteEndpoint() { - @Override - public String process(HttpWrapper wrapper) throws ServerHaltingException { - throw notFound("Delete mapping for this request path not found!"); - } + var deleteEndpoint = + new DeleteEndpoint() { + @Override + public String process(HttpWrapper wrapper) throws ServerHaltingException { + throw notFound("Delete mapping for this request path not found!"); + } }; - var putEndpoint = new PutEndpoint() { - @Override - public String process(HttpWrapper wrapper) throws ServerHaltingException { - throw notFound("Put mapping for this request path not found!"); - } + var putEndpoint = + new PutEndpoint() { + @Override + public String process(HttpWrapper wrapper) throws ServerHaltingException { + throw notFound("Put mapping for this request path not found!"); + } }; - return new PathEndointCollector("", getEndpoint, postEndpoint, putEndpoint, patchEndpoint, deleteEndpoint); - } - - private EndpointsProvider() {} + return new PathEndointCollector( + "", getEndpoint, postEndpoint, putEndpoint, patchEndpoint, deleteEndpoint); + } + + private EndpointsProvider() {} } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/HealthEndpoint.java b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/HealthEndpoint.java index f55868c..1d759c6 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/HealthEndpoint.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/HealthEndpoint.java @@ -1,16 +1,14 @@ package tools.vitruv.framework.remote.server.rest.endpoints; +import tools.vitruv.framework.remote.common.rest.constants.ContentType; import tools.vitruv.framework.remote.server.http.HttpWrapper; import tools.vitruv.framework.remote.server.rest.GetEndpoint; -import tools.vitruv.framework.remote.common.rest.constants.ContentType; -/** - * This endpoint can be used to check, if the server is running. - */ +/** This endpoint can be used to check, if the server is running. */ public class HealthEndpoint implements GetEndpoint { - @Override - public String process(HttpWrapper wrapper) { - wrapper.setContentType(ContentType.TEXT_PLAIN); - return "Vitruv server up and running!"; - } + @Override + public String process(HttpWrapper wrapper) { + wrapper.setContentType(ContentType.TEXT_PLAIN); + return "Vitruv server up and running!"; + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/IsViewClosedEndpoint.java b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/IsViewClosedEndpoint.java index 01b2ee1..cfa5e3f 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/IsViewClosedEndpoint.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/IsViewClosedEndpoint.java @@ -1,24 +1,23 @@ package tools.vitruv.framework.remote.server.rest.endpoints; -import tools.vitruv.framework.remote.server.http.HttpWrapper; -import tools.vitruv.framework.remote.server.rest.GetEndpoint; import tools.vitruv.framework.remote.common.rest.constants.ContentType; import tools.vitruv.framework.remote.common.rest.constants.Header; +import tools.vitruv.framework.remote.server.http.HttpWrapper; +import tools.vitruv.framework.remote.server.rest.GetEndpoint; +import tools.vitruv.framework.views.View; -/** - * This endpoint returns whether a {@link tools.vitruv.framework.views.View View} is closed. - */ +/** This endpoint returns whether a {@link tools.vitruv.framework.views.View View} is closed. */ public class IsViewClosedEndpoint implements GetEndpoint { - @Override - public String process(HttpWrapper wrapper) { - var view = Cache.getView(wrapper.getRequestHeader(Header.VIEW_UUID)); - if (view == null) { - return Boolean.TRUE.toString(); - } - if (view.isClosed()) { - Cache.removeView(wrapper.getRequestHeader(Header.VIEW_UUID)); - } - wrapper.setContentType(ContentType.TEXT_PLAIN); - return view.isClosed() ? Boolean.TRUE.toString() : Boolean.FALSE.toString(); + @Override + public String process(HttpWrapper wrapper) { + View view = Cache.getView(wrapper.getRequestHeader(Header.VIEW_UUID)); + if (view == null) { + return Boolean.TRUE.toString(); + } + if (view.isClosed()) { + Cache.removeView(wrapper.getRequestHeader(Header.VIEW_UUID)); } + wrapper.setContentType(ContentType.TEXT_PLAIN); + return view.isClosed() ? Boolean.TRUE.toString() : Boolean.FALSE.toString(); + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/IsViewOutdatedEndpoint.java b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/IsViewOutdatedEndpoint.java index 9fbbc7a..224f621 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/IsViewOutdatedEndpoint.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/IsViewOutdatedEndpoint.java @@ -1,21 +1,20 @@ package tools.vitruv.framework.remote.server.rest.endpoints; -import tools.vitruv.framework.remote.server.http.HttpWrapper; -import tools.vitruv.framework.remote.server.rest.GetEndpoint; import tools.vitruv.framework.remote.common.rest.constants.ContentType; import tools.vitruv.framework.remote.common.rest.constants.Header; +import tools.vitruv.framework.remote.server.http.HttpWrapper; +import tools.vitruv.framework.remote.server.rest.GetEndpoint; +import tools.vitruv.framework.views.View; -/** - * This view returns whether a {@link tools.vitruv.framework.views.View View} is outdated. - */ +/** This view returns whether a {@link tools.vitruv.framework.views.View View} is outdated. */ public class IsViewOutdatedEndpoint implements GetEndpoint { - @Override - public String process(HttpWrapper wrapper) { - var view = Cache.getView(wrapper.getRequestHeader(Header.VIEW_UUID)); - if (view == null) { - throw notFound("View with given id not found!"); - } - wrapper.setContentType(ContentType.TEXT_PLAIN); - return view.isOutdated() ? Boolean.TRUE.toString() : Boolean.FALSE.toString(); + @Override + public String process(HttpWrapper wrapper) { + View view = Cache.getView(wrapper.getRequestHeader(Header.VIEW_UUID)); + if (view == null) { + throw notFound("View with given id not found!"); } + wrapper.setContentType(ContentType.TEXT_PLAIN); + return view.isOutdated() ? Boolean.TRUE.toString() : Boolean.FALSE.toString(); + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/UpdateViewEndpoint.java b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/UpdateViewEndpoint.java index d861194..a782429 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/UpdateViewEndpoint.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/UpdateViewEndpoint.java @@ -1,49 +1,56 @@ package tools.vitruv.framework.remote.server.rest.endpoints; import com.fasterxml.jackson.core.JsonProcessingException; - import edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceCopier; - +import java.util.List; import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; - import tools.vitruv.framework.remote.common.json.JsonMapper; import tools.vitruv.framework.remote.common.rest.constants.ContentType; import tools.vitruv.framework.remote.common.rest.constants.Header; import tools.vitruv.framework.remote.server.http.HttpWrapper; import tools.vitruv.framework.remote.server.rest.GetEndpoint; +import tools.vitruv.framework.views.View; /** - * This endpoint updates a {@link tools.vitruv.framework.views.View View} and returns the - * updated {@link org.eclipse.emf.ecore.resource.Resource Resources}. + * This endpoint updates a {@link tools.vitruv.framework.views.View View} and returns the updated + * {@link org.eclipse.emf.ecore.resource.Resource Resources}. */ public class UpdateViewEndpoint implements GetEndpoint { - private final JsonMapper mapper; - - public UpdateViewEndpoint(JsonMapper mapper) { - this.mapper = mapper; - } - - @Override - public String process(HttpWrapper wrapper) { - var view = Cache.getView(wrapper.getRequestHeader(Header.VIEW_UUID)); - if (view == null) { - throw notFound("View with given id not found!"); - } - - view.update(); - - // Get resources. - var resources = view.getRootObjects().stream().map(EObject::eResource).distinct().toList(); - var set = new ResourceSetImpl(); - ResourceCopier.copyViewResources(resources, set); - - wrapper.setContentType(ContentType.APPLICATION_JSON); - - try { - return mapper.serialize(set); - } catch (JsonProcessingException e) { - throw internalServerError(e.getMessage()); - } + private final JsonMapper mapper; + + /** + * Creates a new UpdateViewEndpoint. + * + * @param mapper The JSON mapper to use. + */ + public UpdateViewEndpoint(JsonMapper mapper) { + this.mapper = mapper; + } + + @Override + public String process(HttpWrapper wrapper) { + View view = Cache.getView(wrapper.getRequestHeader(Header.VIEW_UUID)); + if (view == null) { + throw notFound("View with given id not found!"); + } + + view.update(); + + // Get resources. + List resources = + view.getRootObjects().stream().map(EObject::eResource).distinct().toList(); + ResourceSet set = new ResourceSetImpl(); + ResourceCopier.copyViewResources(resources, set); + + wrapper.setContentType(ContentType.APPLICATION_JSON); + + try { + return mapper.serialize(set); + } catch (JsonProcessingException e) { + throw internalServerError(e.getMessage()); } + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/ViewEndpoint.java b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/ViewEndpoint.java index 507ec0b..f80b375 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/ViewEndpoint.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/ViewEndpoint.java @@ -1,68 +1,78 @@ package tools.vitruv.framework.remote.server.rest.endpoints; +import edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceCopier; import java.io.IOException; +import java.util.List; import java.util.UUID; - -import edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceCopier; import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; - import tools.vitruv.framework.remote.common.json.JsonMapper; import tools.vitruv.framework.remote.common.rest.constants.ContentType; import tools.vitruv.framework.remote.common.rest.constants.Header; import tools.vitruv.framework.remote.server.http.HttpWrapper; import tools.vitruv.framework.remote.server.rest.PostEndpoint; +import tools.vitruv.framework.views.View; +import tools.vitruv.framework.views.ViewSelector; /** * This endpoint returns a serialized {@link tools.vitruv.framework.views.View View} for the given * {@link tools.vitruv.framework.views.ViewType ViewType}. */ public class ViewEndpoint implements PostEndpoint { - private final JsonMapper mapper; - - public ViewEndpoint(JsonMapper mapper) { - this.mapper = mapper; - } + private final JsonMapper mapper; - @Override - public String process(HttpWrapper wrapper) { - var selectorUuid = wrapper.getRequestHeader(Header.SELECTOR_UUID); - var selector = Cache.getSelector(selectorUuid); + /** + * Creates a new ViewEndpoint. + * + * @param mapper The JSON mapper to use. + */ + public ViewEndpoint(JsonMapper mapper) { + this.mapper = mapper; + } + + @Override + public String process(HttpWrapper wrapper) { + String selectorUuid = wrapper.getRequestHeader(Header.SELECTOR_UUID); + ViewSelector selector = Cache.getSelector(selectorUuid); + + // Check if view type exists. + if (selector == null) { + throw notFound("Selector with UUID " + selectorUuid + " not found!"); + } - // Check if view type exists. - if (selector == null) { - throw notFound("Selector with UUID " + selectorUuid + " not found!"); - } + try { + String body = wrapper.getRequestBodyAsString(); + List selection = mapper.deserializeArrayOf(body, String.class); - try { - var body = wrapper.getRequestBodyAsString(); - var selection = mapper.deserializeArrayOf(body, String.class); - - // Select elements using IDs sent from client. - selection.forEach(it -> { - var object = Cache.getEObjectFromMapping(selectorUuid, it); - if (object != null) { - selector.setSelected(object, true); - } - }); + // Select elements using IDs sent from client. + selection.forEach( + it -> { + EObject object = Cache.getEObjectFromMapping(selectorUuid, it); + if (object != null) { + selector.setSelected(object, true); + } + }); - // Create and cache view. - var uuid = UUID.randomUUID().toString(); - var view = selector.createView(); - Cache.addView(uuid, view); - Cache.removeSelectorAndMapping(selectorUuid); + // Create and cache view. + String uuid = UUID.randomUUID().toString(); + View view = selector.createView(); + Cache.addView(uuid, view); + Cache.removeSelectorAndMapping(selectorUuid); - // Get resources. - var resources = view.getRootObjects().stream().map(EObject::eResource).distinct().toList(); - var set = new ResourceSetImpl(); - ResourceCopier.copyViewResources(resources, set); + // Get resources. + List resources = + view.getRootObjects().stream().map(EObject::eResource).distinct().toList(); + ResourceSet set = new ResourceSetImpl(); + ResourceCopier.copyViewResources(resources, set); - wrapper.setContentType(ContentType.APPLICATION_JSON); - wrapper.addResponseHeader(Header.VIEW_UUID, uuid); + wrapper.setContentType(ContentType.APPLICATION_JSON); + wrapper.addResponseHeader(Header.VIEW_UUID, uuid); - return mapper.serialize(set); - } catch (IOException e) { - throw internalServerError(e.getMessage()); - } + return mapper.serialize(set); + } catch (IOException e) { + throw internalServerError(e.getMessage()); } + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/ViewSelectorEndpoint.java b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/ViewSelectorEndpoint.java index 90bd4e2..2369eae 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/ViewSelectorEndpoint.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/ViewSelectorEndpoint.java @@ -2,69 +2,82 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.google.common.collect.HashBiMap; +import java.util.Collection; +import java.util.List; +import java.util.UUID; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emfcloud.jackson.resource.JsonResource; - import tools.vitruv.framework.remote.common.json.JsonFieldName; import tools.vitruv.framework.remote.common.json.JsonMapper; import tools.vitruv.framework.remote.common.rest.constants.ContentType; import tools.vitruv.framework.remote.common.rest.constants.Header; -import tools.vitruv.framework.remote.common.util.*; +import tools.vitruv.framework.remote.common.util.ResourceUtil; import tools.vitruv.framework.remote.server.exception.ServerHaltingException; import tools.vitruv.framework.remote.server.http.HttpWrapper; import tools.vitruv.framework.remote.server.rest.GetEndpoint; +import tools.vitruv.framework.views.ViewSelector; +import tools.vitruv.framework.views.ViewType; import tools.vitruv.framework.vsum.VirtualModel; -import java.util.UUID; - +/** This endpoint creates a view selector for a given view type. */ public class ViewSelectorEndpoint implements GetEndpoint { - private final VirtualModel model; - private final JsonMapper mapper; + private final VirtualModel model; + private final JsonMapper mapper; - public ViewSelectorEndpoint(VirtualModel model, JsonMapper mapper) { - this.model = model; - this.mapper = mapper; - } + /** + * Creates a new ViewSelectorEndpoint. + * + * @param model The virtual model to create selectors from. + * @param mapper The JSON mapper to use. + */ + public ViewSelectorEndpoint(VirtualModel model, JsonMapper mapper) { + this.model = model; + this.mapper = mapper; + } - @Override - public String process(HttpWrapper wrapper) throws ServerHaltingException { - var viewTypeName = wrapper.getRequestHeader(Header.VIEW_TYPE); - var types = model.getViewTypes(); - var viewType = types.stream().filter(it -> it.getName().equals(viewTypeName)).findFirst().orElse(null); + @Override + public String process(HttpWrapper wrapper) throws ServerHaltingException { + String viewTypeName = wrapper.getRequestHeader(Header.VIEW_TYPE); + Collection> types = model.getViewTypes(); + ViewType viewType = + types.stream().filter(it -> it.getName().equals(viewTypeName)).findFirst().orElse(null); - // Check if view type exists. - if (viewType == null) { - throw notFound("View Type with name " + viewTypeName + " not found!"); - } + // Check if view type exists. + if (viewType == null) { + throw notFound("View Type with name " + viewTypeName + " not found!"); + } - // Generate selector UUID. - var selectorUuid = UUID.randomUUID().toString(); + // Generate selector UUID. + String selectorUuid = UUID.randomUUID().toString(); - var selector = model.createSelector(viewType); - var originalSelection = selector.getSelectableElements().stream().toList(); - var copiedSelection = EcoreUtil.copyAll(originalSelection).stream().toList(); + ViewSelector selector = model.createSelector(viewType); + List originalSelection = selector.getSelectableElements().stream().toList(); + List copiedSelection = EcoreUtil.copyAll(originalSelection).stream().toList(); - // Wrap selection in resource for serialization. - var resource = (JsonResource) ResourceUtil.createResourceWith(URI.createURI(JsonFieldName.TEMP_VALUE), copiedSelection); + // Wrap selection in resource for serialization. + JsonResource resource = + (JsonResource) + ResourceUtil.createResourceWith( + URI.createURI(JsonFieldName.TEMP_VALUE), copiedSelection); - // Create EObject to UUID mapping. - HashBiMap mapping = HashBiMap.create(); - for (int i = 0; i < originalSelection.size(); i++) { - var objectUuid = UUID.randomUUID().toString(); - mapping.put(objectUuid, originalSelection.get(i)); - resource.setID(copiedSelection.get(i), objectUuid); - } - Cache.addSelectorWithMapping(selectorUuid, selector, mapping); + // Create EObject to UUID mapping. + HashBiMap mapping = HashBiMap.create(); + for (int i = 0; i < originalSelection.size(); i++) { + var objectUuid = UUID.randomUUID().toString(); + mapping.put(objectUuid, originalSelection.get(i)); + resource.setID(copiedSelection.get(i), objectUuid); + } + Cache.addSelectorWithMapping(selectorUuid, selector, mapping); - wrapper.setContentType(ContentType.APPLICATION_JSON); - wrapper.addResponseHeader(Header.SELECTOR_UUID, selectorUuid); + wrapper.setContentType(ContentType.APPLICATION_JSON); + wrapper.addResponseHeader(Header.SELECTOR_UUID, selectorUuid); - try { - return mapper.serialize(resource); - } catch (JsonProcessingException e) { - throw internalServerError(e.getMessage()); - } + try { + return mapper.serialize(resource); + } catch (JsonProcessingException e) { + throw internalServerError(e.getMessage()); } + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/ViewTypesEndpoint.java b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/ViewTypesEndpoint.java index 36671df..4385928 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/ViewTypesEndpoint.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/ViewTypesEndpoint.java @@ -3,35 +3,39 @@ import com.fasterxml.jackson.core.JsonProcessingException; import java.util.Collection; import java.util.List; -import tools.vitruv.framework.remote.server.http.HttpWrapper; -import tools.vitruv.framework.remote.server.rest.GetEndpoint; import tools.vitruv.framework.remote.common.json.JsonMapper; import tools.vitruv.framework.remote.common.rest.constants.ContentType; +import tools.vitruv.framework.remote.server.http.HttpWrapper; +import tools.vitruv.framework.remote.server.rest.GetEndpoint; import tools.vitruv.framework.views.ViewType; import tools.vitruv.framework.vsum.VirtualModel; -/** - * This end point returns a list of names of all registered {@link ViewType}s in the VSUM. - */ +/** This end point returns a list of names of all registered {@link ViewType}s in the VSUM. */ public class ViewTypesEndpoint implements GetEndpoint { - private final VirtualModel model; - private final JsonMapper mapper; + private final VirtualModel model; + private final JsonMapper mapper; - public ViewTypesEndpoint(VirtualModel model, JsonMapper mapper) { - this.model = model; - this.mapper = mapper; - } + /** + * Creates a new ViewTypesEndpoint. + * + * @param model The virtual model containing the view types. + * @param mapper The JSON mapper to serialize the response. + */ + public ViewTypesEndpoint(VirtualModel model, JsonMapper mapper) { + this.model = model; + this.mapper = mapper; + } - @Override - public String process(HttpWrapper wrapper) { - Collection> types = model.getViewTypes(); - List names = types.stream().map(ViewType::getName).toList(); + @Override + public String process(HttpWrapper wrapper) { + Collection> types = model.getViewTypes(); + List names = types.stream().map(ViewType::getName).toList(); - wrapper.setContentType(ContentType.APPLICATION_JSON); - try { - return mapper.serialize(names); - } catch (JsonProcessingException e) { - throw internalServerError(e.getMessage()); - } + wrapper.setContentType(ContentType.APPLICATION_JSON); + try { + return mapper.serialize(names); + } catch (JsonProcessingException e) { + throw internalServerError(e.getMessage()); } + } } diff --git a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/package-info.java b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/package-info.java index 0763334..2ec7933 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/package-info.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/endpoints/package-info.java @@ -1,4 +1,2 @@ -/** - * This package implements the REST API end points for the Vitruvius server. - */ +/** This package implements the REST API end points for the Vitruvius server. */ package tools.vitruv.framework.remote.server.rest.endpoints; diff --git a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/package-info.java b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/package-info.java index 1277b73..adfa4f4 100644 --- a/remote/src/main/java/tools/vitruv/framework/remote/server/rest/package-info.java +++ b/remote/src/main/java/tools/vitruv/framework/remote/server/rest/package-info.java @@ -1,4 +1,5 @@ /** - * This package defines interfaces for REST API end points. They are independent of the underlying HTTP server. + * This package defines interfaces for REST API end points. They are independent of the underlying + * HTTP server. */ package tools.vitruv.framework.remote.server.rest; diff --git a/remote/src/test/java/tools/vitruv/framework/remote/common/apm/VitruvApmControllerTest.java b/remote/src/test/java/tools/vitruv/framework/remote/common/apm/VitruvApmControllerTest.java index e6a650e..cfa62da 100644 --- a/remote/src/test/java/tools/vitruv/framework/remote/common/apm/VitruvApmControllerTest.java +++ b/remote/src/test/java/tools/vitruv/framework/remote/common/apm/VitruvApmControllerTest.java @@ -1,11 +1,12 @@ package tools.vitruv.framework.remote.common.apm; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; import java.lang.reflect.Field; import java.nio.file.Path; import java.nio.file.Paths; - import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; @@ -74,6 +75,9 @@ void testEnableMultipleTimesDoesNotCreateNewInstance() throws Exception { VitruvStepMeterRegistry secondInstance = getActiveRegistry(); // The same instance should be used - assertSame(firstInstance, secondInstance, "Registry instance should be the same when enabling multiple times"); + assertSame( + firstInstance, + secondInstance, + "Registry instance should be the same when enabling multiple times"); } } diff --git a/remote/src/test/java/tools/vitruv/framework/remote/common/apm/VitruvStepMeterRegistryTest.java b/remote/src/test/java/tools/vitruv/framework/remote/common/apm/VitruvStepMeterRegistryTest.java index fd671a4..76f2997 100644 --- a/remote/src/test/java/tools/vitruv/framework/remote/common/apm/VitruvStepMeterRegistryTest.java +++ b/remote/src/test/java/tools/vitruv/framework/remote/common/apm/VitruvStepMeterRegistryTest.java @@ -1,27 +1,30 @@ package tools.vitruv.framework.remote.common.apm; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.Timer; import io.micrometer.core.instrument.step.StepRegistryConfig; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.time.Duration; import java.time.temporal.ChronoUnit; import java.util.List; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; class VitruvStepMeterRegistryTest { private VitruvStepMeterRegistry registry; private Path tempFile; private StepRegistryConfig mockConfig; - + @BeforeEach void setUp() { try { @@ -43,7 +46,7 @@ void tearDown() { fail("Failed to delete temp file: " + e.getMessage()); } } - + @Test void testMetricsAreWrittenToFile() { Timer timer = Timer.builder("test.timer").register(registry); @@ -71,8 +74,10 @@ void testMultipleMetricsAreWrittenToFile() { try { String fileContent = Files.readString(tempFile); - assertTrue(fileContent.contains("test.timer1"), "Metric test.timer1 should be present in the file"); - assertTrue(fileContent.contains("test.timer2"), "Metric test.timer2 should be present in the file"); + assertTrue( + fileContent.contains("test.timer1"), "Metric test.timer1 should be present in the file"); + assertTrue( + fileContent.contains("test.timer2"), "Metric test.timer2 should be present in the file"); } catch (IOException e) { fail("Failed to read temp file: " + e.getMessage()); } @@ -104,7 +109,10 @@ void testFileWritingErrorHandling() { try { String fileContent = Files.readString(tempFile); - assertEquals("Pre-existing content", fileContent, "File content should remain unchanged due to error handling"); + assertEquals( + "Pre-existing content", + fileContent, + "File content should remain unchanged due to error handling"); } catch (IOException e) { fail("Failed to read temp file: " + e.getMessage()); } @@ -115,12 +123,15 @@ void testTimersAreClearedAfterPublishing() { Timer timer = registry.timer("test.timer"); timer.record(Duration.ofMillis(100)); - assertFalse(registry.getMeters().isEmpty(), "There should be at least one recorded metric before publishing."); + assertFalse( + registry.getMeters().isEmpty(), + "There should be at least one recorded metric before publishing."); registry.publish(); - boolean hasRemainingMetrics = registry.getMeters().stream() - .anyMatch(meter -> meter.getId().getName().equals("test.timer")); + boolean hasRemainingMetrics = + registry.getMeters().stream() + .anyMatch(meter -> meter.getId().getName().equals("test.timer")); assertTrue(hasRemainingMetrics, "Timer should still be registered."); }