/*******************************************************************************
* Copyright (c) 2015 IBH SYSTEMS GmbH.
* 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
*
* Contributors:
* IBH SYSTEMS GmbH - initial API and implementation
*******************************************************************************/
package org.eclipse.packagedrone.repo.channel;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.eclipse.packagedrone.repo.channel.deploy.DeployGroup;
import org.eclipse.packagedrone.repo.channel.deploy.DeployKey;
import org.eclipse.packagedrone.repo.channel.provider.ProviderInformation;
import org.eclipse.packagedrone.repo.channel.stats.ChannelStatistics;
/**
* The service for working with channels
* <h2>Channel access</h2>
* <p>
* Accessing a channel is primarily done by using the
* {@link #accessRun(By, Class, ChannelOperationVoid)} and
* {@link #accessCall(By, Class, ChannelOperation)} methods. Each of those
* methods access an interface class and operation. The interface class chooses
* in which way the channel will be accessed (e.g. read-only, writing,
* configuration). Not all channels must support all operations. The operation
* receives an instance of the requested interface class and may perform
* operations on this instance as long as the operation method does not return.
* After the operation is completed the instance will be disposed can further
* calls to this instance will cause exceptions.
* </p>
* <p>
* Supported interface classes are:
* </p>
* <dl>
* <dt>{@link ReadableChannel}</dt>
* <dd>A read only instance to the channel. Will lock the channel in shared read
* mode. No write access is possible.</dd>
* <dt>{@link ModifiableChannel}</dt>
* <dd>A modifiable instance to the channel. Will lock the channel in exclusive
* mode. No other read or write access is possible. Modifications will not be
* persisted when the operation returns with an exception.
* <dt>{@link DescriptorAdapter}</dt>
* <dd>An instance for accessing the id and name mapping of the channel. Won't
* lock the channel itself.</dd>
* <dt>{@link DeployKeysChannelAdapter}</dt>
* <dd>An instance for managing the deploy keys of a channel. Won't lock the
* channel itself.</dt>
* </dl>
*/
public interface ChannelService
{
@FunctionalInterface
public interface ChannelOperation<R, T>
{
public R process ( T channel ) throws Exception;
}
@FunctionalInterface
public interface ChannelOperationVoid<T>
{
public void process ( T channel ) throws Exception;
}
@FunctionalInterface
public interface ArtifactReceiver
{
public void consume ( ArtifactInformation artifact, InputStream stream ) throws IOException;
}
/**
* Locator for channels
*/
public static final class By
{
public static enum Type
{
ID,
NAME,
COMPOSITE;
}
private final Type type;
private final Object qualifier;
private By ( final Type type, final Object qualifier )
{
this.type = type;
this.qualifier = qualifier;
}
public Type getType ()
{
return type;
}
public Object getQualifier ()
{
return qualifier;
}
/**
* Locate a channel by its ID
*
* @param channelId
* the channel id
* @return the locator instance
*/
public static By id ( final String channelId )
{
return new By ( Type.ID, channelId );
}
/**
* Locate a channel by its name
*
* @param name
* the name of the channel
* @return the locator instance
*/
public static By name ( final String name )
{
return new By ( Type.NAME, name );
}
/**
* Locate a channel either by its name, falling back to its id
* <p>
* First it will be tried to locate the channel by name and then by id.
* </p>
*
* @param nameOrId
* the name or id of the channel
* @return the locator instance
*/
public static By nameOrId ( final String nameOrId )
{
return new By ( Type.COMPOSITE, new By[] { id ( nameOrId ), name ( nameOrId ) } );
}
}
public Collection<ProviderInformation> getProviders ();
/**
* List all channels
*
* @return the list of channel information
*/
public Collection<ChannelInformation> list ();
/**
* Get the state of a single channel
*
* @param by
* the channel locator
* @return the optional channel information, never returns {@code null}
*/
public Optional<ChannelInformation> getState ( By by );
public ChannelId create ( String providerId, ChannelDetails details );
/**
* Delete a channel
*
* @param by
* the channel locator
* @return
* {@code true} if the channel was present and got deleted,
* {@code false otherwise}
*/
public boolean delete ( By by );
/**
* Access a channel
* <p>
* It is guaranteed that either the operation will be called or the
* ChannelNotFoundException will be thrown
* </p>
*
* @param by
* locator of the channel to access
* @param clazz
* the interface of the channel which should be accessed
* @param operation
* the operation which should be performed on the channel
* @return
* returns the value of the operation
* @throws ChannelNotFoundException
* if the channel was not found
*/
public <R, T> R accessCall ( By by, Class<T> clazz, ChannelOperation<R, T> operation );
/**
* Access a channel
* <p>
* It is guaranteed that either the operation will be called or the
* ChannelNotFoundException will be thrown
* </p>
*
* @param by
* locator of the channel to access
* @param clazz
* the interface of the channel which should be accessed
* @param operation
* the operation which should be performed on the channel
* @throws ChannelNotFoundException
* if the channel was not found
*/
public default <T> void accessRun ( final By by, final Class<T> clazz, final ChannelOperationVoid<T> operation )
{
accessCall ( by, clazz, channel -> {
operation.process ( channel );
return null;
} );
}
/**
* Get all name to channel mappings which are currently unclaimed.
*
* @return the list of unclaimed mappings.
*/
public Map<String, String> getUnclaimedMappings ();
public void deleteMapping ( String id, String name );
public default Optional<Collection<DeployKey>> getChannelDeployKeys ( final By by )
{
return getChannelDeployGroups ( by ).map ( groups -> groups.stream ().flatMap ( group -> group.getKeys ().stream () ).collect ( toList () ) );
}
public default Optional<Set<String>> getChannelDeployKeyStrings ( final By by )
{
return getChannelDeployGroups ( by ).map ( groups -> groups.stream ().flatMap ( group -> group.getKeys ().stream () ).map ( DeployKey::getKey ).collect ( toSet () ) );
}
public Optional<Collection<DeployGroup>> getChannelDeployGroups ( By by );
public default boolean streamArtifact ( final String channelId, final String artifactId, final ArtifactReceiver receiver )
{
try
{
return accessCall ( By.id ( channelId ), ReadableChannel.class, channel -> {
final Optional<ChannelArtifactInformation> artifact = channel.getArtifact ( artifactId );
if ( !artifact.isPresent () )
{
return false;
}
return channel.getContext ().stream ( artifactId, stream -> {
receiver.consume ( artifact.get (), stream );
} );
} );
}
catch ( final ChannelNotFoundException e )
{
return false;
}
}
/**
* Delete all and everything
*/
public void wipeClean ();
public ChannelStatistics getStatistics ();
}