/**
* 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.mediapackage;
import org.apache.commons.lang3.StringUtils;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Implementation of a {@link MediaPackageSerializer} that will rewrite urls of a Mediapackage.
*/
public class RedirectingMediaPackageSerializer implements MediaPackageSerializer, ManagedService {
/** The logging facility */
private static final Logger logger = LoggerFactory.getLogger(RedirectingMediaPackageSerializer.class);
/** The redirect serializer should be invoked after the default serializer */
public static final int RANKING = 100;
/** Configuration option for the source prefix */
public static final String OPT_SOURCE_PREFIX = "source";
/** Configuration option for the destination prefix */
public static final String OPT_DESINATION_PREFIX = "destination";
/** Map containing source and destination prefixes */
private final Map<URI, URI> redirects = new HashMap<URI, URI>();
/**
* Creates a new and unconfigured package serializer that will not be able to perform any redirecting.
*/
public RedirectingMediaPackageSerializer() {
}
/**
* Creates a new package serializer that enables rewriting of urls starting with <code>sourcePrefix</code> to strart
* with <code>destintionPrefix</code>.
*
* @param sourcePrefix
* the original url prefix
* @param destinationPrefix
* the new url prefix
*/
public RedirectingMediaPackageSerializer(URI sourcePrefix, URI destinationPrefix) {
addRedirect(sourcePrefix, destinationPrefix);
}
/**
* Returns the current set of redirects.
*
* @return the redirects
*/
public Map<URI, URI> getRedirections() {
return Collections.unmodifiableMap(redirects);
}
/**
* Adds a redirect to the set of configured redirections.
*
* @param sourcePrefix
* the source prefix
* @param destinationPrefix
* the destination prefix
* @throws IllegalArgumentException
* if <code>sourcePrefix</code> or <code>destinationPrefix</code> is <code>null</code>
* @throws IllegalStateException
* if a redirect for <code>sourcePrefix</code> has already been configured
*/
public void addRedirect(URI sourcePrefix, URI destinationPrefix) {
if (sourcePrefix == null)
throw new IllegalArgumentException("Source prefix must not be null");
if (destinationPrefix == null)
throw new IllegalArgumentException("Destination prefix must not be null");
if (redirects.containsKey(sourcePrefix))
throw new IllegalStateException("Source prefix '" + sourcePrefix + "' already registered");
redirects.put(sourcePrefix, destinationPrefix);
}
/**
* {@inheritDoc}
*
* @see org.opencastproject.mediapackage.MediaPackageSerializer#encodeURI(URI)
*/
@Override
public URI encodeURI(URI uri) throws URISyntaxException {
if (uri == null)
throw new IllegalArgumentException("Argument uri is null");
return rewrite(uri, false);
}
/**
* {@inheritDoc}
*
* @see org.opencastproject.mediapackage.MediaPackageSerializer#decodeURI(URI)
*/
@Override
public URI decodeURI(URI uri) throws URISyntaxException {
if (uri == null)
throw new IllegalArgumentException("Argument uri is null");
return rewrite(uri, true);
}
/**
* This method is rewriting the URI with regards to its prefix.
*
* @param uri
* the URI to rewrite
* @param reverse
* whether to decode instead of encode the URI
*
* @return the rewritten URI
* @throws URISyntaxException
* if the rewritten URI contains syntax errors
*/
private URI rewrite(URI uri, boolean reverse) throws URISyntaxException {
String path = uri.toString();
List<String> variations = new ArrayList<String>();
boolean changed = true;
while (changed) {
changed = false;
// Make sure we are not getting into an endless loop
if (variations.contains(path))
throw new IllegalStateException("Rewriting of mediapackage element '" + uri + "' experienced an endless loop");
variations.add(path);
// Loop over all configured redirects
for (Map.Entry<URI, URI> entry : redirects.entrySet()) {
URI oldPrefix = (reverse) ? entry.getValue() : entry.getKey();
URI newPrefix = (reverse) ? entry.getKey() : entry.getValue();
// Does the URI match the source prefix?
String sourcePrefixString = oldPrefix.toString();
if (!path.startsWith(sourcePrefixString))
continue;
// Cut off the source prefix
path = path.substring(sourcePrefixString.length());
// Prepend the destination prefix
path = new StringBuilder(newPrefix.toString()).append(path).toString();
changed = true;
break;
}
}
return new URI(path);
}
/**
* {@inheritDoc}
*
* @see org.osgi.service.cm.ManagedService#updated(java.util.Dictionary)
*/
@SuppressWarnings("rawtypes")
@Override
public void updated(Dictionary properties) throws ConfigurationException {
if (properties == null) {
logger.warn("Mediapackage serializer is unconfigured");
return;
}
// Clear the current set of redirects
redirects.clear();
String sourceKey = null;
String destinationKey = null;
int i = 1;
while (true) {
// Create the configuration keys
sourceKey = new StringBuilder(OPT_SOURCE_PREFIX).append(".").append(i).toString();
destinationKey = new StringBuilder(OPT_DESINATION_PREFIX).append(".").append(i).toString();
logger.debug("Looking for configuration of {} and {}", sourceKey, destinationKey);
// Read the source and destination prefixes
String sourcePrefixOpt = StringUtils.trimToNull((String) properties.get(sourceKey));
String destinationPrefixOpt = StringUtils.trimToNull((String) properties.get(destinationKey));
// Has the rewriter been fully configured
if (sourcePrefixOpt == null || destinationPrefixOpt == null) {
logger.info("Mediapackage serializer configured to transparent mode");
break;
}
URI sourcePrefix = null;
URI destinationPrefix = null;
try {
sourcePrefix = new URI(sourcePrefixOpt);
} catch (URISyntaxException e) {
throw new ConfigurationException(sourceKey, e.getMessage());
}
// Read the source prefix
try {
destinationPrefix = new URI(destinationPrefixOpt);
} catch (URISyntaxException e) {
throw new ConfigurationException(destinationKey, e.getMessage());
}
// Store the redirect
try {
addRedirect(destinationPrefix, sourcePrefix);
logger.info("Mediapackage serializer will rewrite element uris from starting with '{}' to start with '{}'", destinationPrefix, sourcePrefix);
} catch (IllegalStateException e) {
throw new ConfigurationException(sourceKey, e.getMessage());
}
i++;
}
// Has the rewriter been fully configured
if (redirects.size() == 0) {
logger.info("Mediapackage serializer configured to transparent mode");
return;
}
}
@Override
public int getRanking() {
return RANKING;
}
}