+ * @EventSubscriber
+ * public class MySubscriber {
+ * @Subscribe
+ * public void listen(MyEvent event) {
+ * System.out.println("event received: " + event);
+ * }
+ * }
+ *
+ *
+ *
+ *
+ *
+ */
+@Target({ TYPE })
+@Retention(RUNTIME)
+@Service
+public @interface EventSubscriber {
+
+}
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/eventbus/event/AbstractDistributedEvent.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/eventbus/event/AbstractDistributedEvent.java
new file mode 100755
index 000000000..c467a746c
--- /dev/null
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/eventbus/event/AbstractDistributedEvent.java
@@ -0,0 +1,105 @@
+/**
+ * Copyright (c) 2015 Bosch Software Innovations GmbH and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.eclipse.hawkbit.eventbus.event;
+
+/**
+ * An abstract class of the {@link DistributedEvent} implementation which holds
+ * all the necessary information of distributing events to other nodes.
+ *
+ *
+ *
+ *
+ */
+public abstract class AbstractDistributedEvent implements DistributedEvent {
+
+ private static final long serialVersionUID = 1L;
+ private final long revision;
+ private String originNodeId;
+ private String nodeId;
+ private final String tenant;
+
+ /**
+ *
+ * @param revision
+ * the revision of this event
+ * @param tenant
+ * the tenant for this event
+ */
+ protected AbstractDistributedEvent(final long revision, final String tenant) {
+ this.revision = revision;
+ this.tenant = tenant;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.hawkbit.server.eventbus.event.NodeAware#setOriginNodeId(java.
+ * lang. String)
+ */
+ @Override
+ public void setOriginNodeId(final String originNodeId) {
+ this.originNodeId = originNodeId;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.hawkbit.server.eventbus.event.NodeAware#setNodeId(java.lang.
+ * String)
+ */
+ @Override
+ public void setNodeId(final String nodeId) {
+ this.nodeId = nodeId;
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.hawkbit.server.eventbus.event.NodeAware#getOriginNodeId()
+ */
+ @Override
+ public String getOriginNodeId() {
+ return this.originNodeId;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.hawkbit.server.eventbus.event.NodeAware#getNodeId()
+ */
+ @Override
+ public String getNodeId() {
+ return this.nodeId;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.hawkbit.server.eventbus.event.Event#getRevision()
+ */
+ @Override
+ public long getRevision() {
+ return revision;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.hawkbit.server.eventbus.event.EntityEvent#getTenant()
+ */
+ @Override
+ public String getTenant() {
+ return tenant;
+ }
+
+}
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/eventbus/event/CancelTargetAssignmentEvent.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/eventbus/event/CancelTargetAssignmentEvent.java
new file mode 100755
index 000000000..ad421b266
--- /dev/null
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/eventbus/event/CancelTargetAssignmentEvent.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2015 Bosch Software Innovations GmbH and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.eclipse.hawkbit.eventbus.event;
+
+import java.net.URI;
+
+/**
+ * Event that gets sent when the assignment of a distribution set to a target
+ * gets canceled.
+ *
+ *
+ *
+ */
+public class CancelTargetAssignmentEvent {
+
+ private final String controllerId;
+ private final Long actionId;
+ private final URI targetAdress;
+
+ /**
+ * Creates a new {@link CancelTargetAssignmentEvent}.
+ *
+ * @param controllerId
+ * the ID of the controller
+ * @param actionId
+ * the action id of the assignment
+ * @param targetAdress
+ * the targetAdress of the target
+ */
+ public CancelTargetAssignmentEvent(final String controllerId, final Long actionId, final URI targetAdress) {
+ this.controllerId = controllerId;
+ this.actionId = actionId;
+ this.targetAdress = targetAdress;
+ }
+
+ /**
+ * @return the action id of the assignment
+ */
+ public Long getActionId() {
+ return actionId;
+ }
+
+ /**
+ * @return the controllerId of the Target which has been assigned to the
+ * distribution set
+ */
+ public String getControllerId() {
+ return controllerId;
+ }
+
+ /**
+ *
+ * @return the targetr adress.
+ */
+ public URI getTargetAdress() {
+ return targetAdress;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "TargetAssignDistributionSetEvent [targetAdress=" + targetAdress + ", controllerId=" + controllerId
+ + ", actionId=" + actionId + "]";
+ }
+}
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/eventbus/event/CustomEvents.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/eventbus/event/CustomEvents.java
new file mode 100755
index 000000000..4416e1a63
--- /dev/null
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/eventbus/event/CustomEvents.java
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2015 Bosch Software Innovations GmbH and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.eclipse.hawkbit.eventbus.event;
+
+/**
+ * Events to be published to refresh data on UI.
+ *
+ *
+ *
+ *
+ */
+public enum CustomEvents {
+
+ TARGETS_CREATED_EVENT,
+
+ DISTRIBUTION_CREATED_EVENT
+
+}
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/eventbus/event/DistributedEvent.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/eventbus/event/DistributedEvent.java
new file mode 100755
index 000000000..b529afa91
--- /dev/null
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/eventbus/event/DistributedEvent.java
@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) 2015 Bosch Software Innovations GmbH and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.eclipse.hawkbit.eventbus.event;
+
+import java.io.Serializable;
+
+/**
+ * Marks an event to as an distributed event which will be distributed to other
+ * nodes.
+ *
+ *
+ *
+ *
+ */
+public interface DistributedEvent extends Event, NodeAware, Serializable {
+
+}
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/eventbus/event/DownloadProgressEvent.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/eventbus/event/DownloadProgressEvent.java
new file mode 100755
index 000000000..74679f02e
--- /dev/null
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/eventbus/event/DownloadProgressEvent.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2015 Bosch Software Innovations GmbH and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.eclipse.hawkbit.eventbus.event;
+
+/**
+ * Event that contains an updated download progress for a given Action.
+ *
+ *
+ *
+ *
+ */
+public class DownloadProgressEvent extends AbstractDistributedEvent {
+
+ private static final long serialVersionUID = 1L;
+
+ private final Long statusId;
+ private final int progressPercent;
+
+ /**
+ * Constructor.
+ *
+ * @param tenant
+ * the tenant for this event
+ * @param statusId
+ * of {@link UpdateActionStatus}
+ * @param progressPercent
+ * number (1-100)
+ */
+ public DownloadProgressEvent(final String tenant, final Long statusId, final int progressPercent) {
+ // the revision of the DownloadProgressEvent is just equal the
+ // progressPercentage due the
+ // percentage is going from 0 to 100.
+ super(statusId, tenant);
+ this.statusId = statusId;
+ this.progressPercent = progressPercent;
+ }
+
+ /**
+ * @return the statusId
+ */
+ public Long getStatusId() {
+ return statusId;
+ }
+
+ /**
+ * @return the progressPercent
+ */
+ public int getProgressPercent() {
+ return progressPercent;
+ }
+}
diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/eventbus/event/EntityEvent.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/eventbus/event/EntityEvent.java
new file mode 100755
index 000000000..688b261e1
--- /dev/null
+++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/eventbus/event/EntityEvent.java
@@ -0,0 +1,48 @@
+/**
+ * Copyright (c) 2015 Bosch Software Innovations GmbH and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.eclipse.hawkbit.eventbus.event;
+
+/**
+ * An event interface which declares event types that an entity has been
+ * changed. {@link EntityEvent}s should not implement {@link DistributedEvent}
+ * due all {@link EntityEvent}s will be distributed to other nodes.
+ *
+ * Retrieving an {@link EntityEvent} on a different node the entity will be load
+ * lazy.
+ *
+ *
+ *
+ *
+ */
+public interface EntityEvent extends Event, NodeAware {
+
+ /**
+ * A typesafe way to retrieve the entity from the event, which might be
+ * loaded lazy in case the event has been distributed from another node.
+ *
+ * @param entityClass
+ * the class of the entity to retrieve
+ * @return the entity might be lazy loaded. Might be {@code null} in case
+ * the entity e.g. is queried lazy on a different node and has been
+ * already deleted from the database
+ * @throws ClassCastException
+ * in case a wrong entity class is given for this event
+ */
+ true if the artifact binary can be overdiden
+ * if it already exists
+ * @param contentType
+ * the contentType of the file
+ * @return uploaded {@link LocalArtifact}
+ *
+ * @throws EntityNotFoundException
+ * if given software module does not exist
+ * @throws EntityAlreadyExistsException
+ * if File with that name already exists in the Software Module
+ * @throws ArtifactUploadFailedException
+ * if upload fails with internal server errors
+ * @throws InvalidMD5HashException
+ * if check against provided MD5 checksum failed
+ * @throws InvalidSHA1HashException
+ * if check against provided SHA1 checksum failed
+ */
+ @Modifying
+ @Transactional
+ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
+ public LocalArtifact createLocalArtifact(@NotNull final InputStream stream, @NotNull final Long moduleId,
+ @NotEmpty final String filename, final String providedMd5Sum, final String providedSha1Sum,
+ final boolean overrideExisting, final String contentType) {
+ DbArtifact result = null;
+
+ final SoftwareModule softwareModule = getModuleAndThrowExceptionIfThatFails(moduleId);
+
+ final LocalArtifact existing = checkForExistingArtifact(filename, overrideExisting, softwareModule);
+
+ try {
+ result = artifactRepository.store(stream, filename, contentType,
+ new DbArtifactHash(providedSha1Sum, providedMd5Sum));
+ } catch (final ArtifactStoreException e) {
+ throw new ArtifactUploadFailedException(e);
+ } catch (final HashNotMatchException e) {
+ if (e.getHashFunction().equals(HashNotMatchException.SHA1)) {
+ throw new InvalidSHA1HashException(e.getMessage(), e);
+ } else {
+ throw new InvalidMD5HashException(e.getMessage(), e);
+ }
+ }
+ if (result == null) {
+ return null;
+ }
+
+ return storeArtifactMetadata(softwareModule, filename, result, existing);
+ }
+
+ private LocalArtifact checkForExistingArtifact(final String filename, final boolean overrideExisting,
+ final SoftwareModule softwareModule) {
+ if (softwareModule.getLocalArtifactByFilename(filename).isPresent()) {
+ if (overrideExisting) {
+ LOG.debug("overriding existing artifact with new filename {}", filename);
+ return softwareModule.getLocalArtifactByFilename(filename).get();
+ } else {
+ throw new EntityAlreadyExistsException("File with that name already exists in the Software Module");
+ }
+ }
+ return null;
+ }
+
+ private SoftwareModule getModuleAndThrowExceptionIfThatFails(final Long moduleId) {
+ final SoftwareModule softwareModule = findSoftwareModuleWithDetails(moduleId);
+
+ if (softwareModule == null) {
+ LOG.debug("no software module with ID {} exists", moduleId);
+ throw new EntityNotFoundException("Software Module: " + moduleId);
+ }
+ return softwareModule;
+ }
+
+ /**
+ * Retrieves software module including details (
+ * {@link SoftwareModule#getArtifacts()}).
+ *
+ * @param id
+ * parameter
+ * @param isDeleted
+ * parameter
+ * @return the found {@link SoftwareModule}s
+ */
+ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
+ protected SoftwareModule findSoftwareModuleWithDetails(@NotNull final Long id) {
+ final SoftwareModule result = findSoftwareModuleById(id);
+ if (result != null) {
+ result.getArtifacts().size();
+ }
+
+ return result;
+ }
+
+ /**
+ * Find all local artifact by sha1 and return the first artifact.
+ *
+ * @param sha1
+ * the sha1
+ * @return the first local artifact
+ */
+ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR
+ + SpringEvalExpressions.IS_CONTROLLER)
+ public LocalArtifact findFirstLocalArtifactsBySHA1(final String sha1) {
+ return localArtifactRepository.findFirstByGridFsFileName(sha1);
+ }
+
+ /**
+ * Finds {@link SoftwareModule} by given id.
+ *
+ * @param id
+ * to search for
+ * @return the found {@link SoftwareModule}s or null if not
+ * found.
+ */
+ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR
+ + SpringEvalExpressions.IS_CONTROLLER)
+ protected SoftwareModule findSoftwareModuleById(@NotNull final Long id) {
+
+ final Specificationnull is it could not be
+ * found.
+ */
+ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
+ public Artifact findArtifact(@NotNull final Long id) {
+ return localArtifactRepository.findOne(id);
+ }
+
+ /**
+ * Loads {@link org.eclipse.hawkbit.artifact.server.json.model.Artifact}
+ * from store for given {@link LocalArtifact}.
+ *
+ * @param artifact
+ * to search for
+ * @return loaded
+ * {@link org.eclipse.hawkbit.artifact.server.json.model.Artifact}
+ *
+ * @throws GridFSDBFileNotFoundException
+ * if file could not be found in store
+ */
+ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_DOWNLOAD_ARTIFACT + SpringEvalExpressions.HAS_AUTH_OR
+ + SpringEvalExpressions.IS_CONTROLLER)
+ public DbArtifact loadLocalArtifactBinary(@NotNull final LocalArtifact artifact) {
+ final DbArtifact result = artifactRepository.getArtifactBySha1(artifact.getGridFsFileName());
+ if (result == null) {
+ throw new GridFSDBFileNotFoundException(artifact.getGridFsFileName());
+ }
+
+ return result;
+ }
+
+ /**
+ * Persists artifact binary as provided by given InputStream. assign the
+ * artifact in addition to given {@link SoftwareModule}.
+ *
+ * @param inputStream
+ * to read from for artifact binary
+ * @param moduleId
+ * to assign the new artifact to
+ * @param filename
+ * of the artifact
+ * @param overrideExisting
+ * to true if the artifact binary can be overdiden
+ * if it already exists
+ * @param contentType
+ * the contentType of the file
+ *
+ * @return uploaded {@link LocalArtifact}
+ *
+ * @throw ArtifactUploadFailedException if upload failes
+ */
+ @Modifying
+ @Transactional
+ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
+ public LocalArtifact createLocalArtifact(final InputStream inputStream, final Long moduleId, final String filename,
+ final boolean overrideExisting, final String contentType) {
+ return createLocalArtifact(inputStream, moduleId, filename, null, null, overrideExisting, contentType);
+ }
+
+ /**
+ * Persists artifact binary as provided by given InputStream. assign the
+ * artifact in addition to given {@link SoftwareModule}.
+ *
+ * @param inputStream
+ * to read from for artifact binary
+ * @param moduleId
+ * to assign the new artifact to
+ * @param filename
+ * of the artifact
+ * @param overrideExisting
+ * to true if the artifact binary can be overdiden
+ * if it already exists
+ *
+ * @return uploaded {@link LocalArtifact}
+ *
+ * @throw ArtifactUploadFailedException if upload failes
+ */
+ @Modifying
+ @Transactional
+ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
+ public LocalArtifact createLocalArtifact(final InputStream inputStream, final Long moduleId, final String filename,
+ final boolean overrideExisting) {
+ return createLocalArtifact(inputStream, moduleId, filename, null, null, overrideExisting, null);
+ }
+
+ /**
+ * Get local artifact for a base software module.
+ *
+ * @param pageReq
+ * Pageable
+ * @param swId
+ * software module id
+ * @return Page