/**
* Licensed to The Apereo Foundation under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
*
* The Apereo Foundation licenses this file to you under the Educational
* Community License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License
* at:
*
* http://opensource.org/licenses/ecl2.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
*/
package org.opencastproject.event.handler;
import org.opencastproject.message.broker.api.BaseMessage;
import org.opencastproject.message.broker.api.MessageReceiver;
import org.opencastproject.message.broker.api.MessageSender;
import org.opencastproject.message.broker.api.series.SeriesItem;
import org.opencastproject.security.api.SecurityService;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Serializable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
/**
* Very simple approach to serialize the work of all three dependend update handlers. Todo: Merge all handlers into one
* to avoid unnecessary distribution updates etc.
*/
public class ConductingSeriesUpdatedEventHandler {
private static final Logger logger = LoggerFactory.getLogger(ConductingSeriesUpdatedEventHandler.class);
private static final String QUEUE_ID = "SERIES.Conductor";
private SecurityService securityService;
private MessageReceiver messageReceiver;
private AssetManagerPermissionsUpdatedEventHandler assetManagerPermissionsUpdatedEventHandler;
private SeriesUpdatedEventHandler seriesUpdatedEventHandler;
private WorkflowPermissionsUpdatedEventHandler workflowPermissionsUpdatedEventHandler;
private OaiPmhUpdatedEventHandler oaiPmhUpdatedEventHandler;
// Use a single thread executor to ensure that only one update is handled at a time.
// This is because Matterhorn lacks a distributed synchronization model on media packages and/or series.
// Note that this measure only _reduces_ the chance of data corruption cause by concurrent modifications.
private final ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
private MessageWatcher messageWatcher;
public void activate(ComponentContext cc) {
logger.info("Activating {}", ConductingSeriesUpdatedEventHandler.class.getName());
messageWatcher = new MessageWatcher();
singleThreadExecutor.execute(messageWatcher);
}
public void deactivate(ComponentContext cc) {
logger.info("Deactivating {}", ConductingSeriesUpdatedEventHandler.class.getName());
if (messageWatcher != null)
messageWatcher.stopListening();
singleThreadExecutor.shutdown();
}
private class MessageWatcher implements Runnable {
private final Logger logger = LoggerFactory.getLogger(MessageWatcher.class);
private boolean listening = true;
private FutureTask<Serializable> future;
private final ExecutorService executor = Executors.newSingleThreadExecutor();
public void stopListening() {
this.listening = false;
future.cancel(true);
}
@Override
public void run() {
logger.info("Starting to listen for series update Messages");
while (listening) {
future = messageReceiver.receiveSerializable(QUEUE_ID, MessageSender.DestinationType.Queue);
executor.execute(future);
try {
BaseMessage baseMessage = (BaseMessage) future.get();
securityService.setOrganization(baseMessage.getOrganization());
securityService.setUser(baseMessage.getUser());
SeriesItem seriesItem = (SeriesItem) baseMessage.getObject();
if (SeriesItem.Type.UpdateCatalog.equals(seriesItem.getType())
|| SeriesItem.Type.UpdateAcl.equals(seriesItem.getType())
|| SeriesItem.Type.Delete.equals(seriesItem.getType())) {
seriesUpdatedEventHandler.handleEvent(seriesItem);
assetManagerPermissionsUpdatedEventHandler.handleEvent(seriesItem);
workflowPermissionsUpdatedEventHandler.handleEvent(seriesItem);
// the OAI-PMH handler is a dynamic dependency
if (oaiPmhUpdatedEventHandler != null) {
oaiPmhUpdatedEventHandler.handleEvent(seriesItem);
}
}
} catch (InterruptedException e) {
logger.error("Problem while getting series update message events {}", ExceptionUtils.getStackTrace(e));
} catch (ExecutionException e) {
logger.error("Problem while getting series update message events {}", ExceptionUtils.getStackTrace(e));
} catch (CancellationException e) {
logger.trace("Listening for series update messages has been cancelled.");
} catch (Throwable t) {
logger.error("Problem while getting series update message events {}", ExceptionUtils.getStackTrace(t));
} finally {
securityService.setOrganization(null);
securityService.setUser(null);
}
}
logger.info("Stopping listening for series update Messages");
}
}
/** OSGi DI callback. */
public void setAssetManagerPermissionsUpdatedEventHandler(AssetManagerPermissionsUpdatedEventHandler h) {
this.assetManagerPermissionsUpdatedEventHandler = h;
}
/** OSGi DI callback. */
public void setSeriesUpdatedEventHandler(SeriesUpdatedEventHandler h) {
this.seriesUpdatedEventHandler = h;
}
/** OSGi DI callback. */
public void setWorkflowPermissionsUpdatedEventHandler(WorkflowPermissionsUpdatedEventHandler h) {
this.workflowPermissionsUpdatedEventHandler = h;
}
/** OSGi DI callback. */
public void setOaiPmhUpdatedEventHandler(OaiPmhUpdatedEventHandler h) {
this.oaiPmhUpdatedEventHandler = h;
}
/** OSGi DI callback. */
public void setMessageReceiver(MessageReceiver messageReceiver) {
this.messageReceiver = messageReceiver;
}
/** OSGi DI callback. */
public void setSecurityService(SecurityService securityService) {
this.securityService = securityService;
}
}