/*******************************************************************************
* 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.impl;
import static java.util.stream.Collectors.toSet;
import java.time.Instant;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.eclipse.packagedrone.repo.channel.deploy.DeployGroup;
import org.eclipse.packagedrone.repo.channel.deploy.DeployKey;
import org.eclipse.packagedrone.repo.utils.Tokens;
import org.eclipse.packagedrone.utils.Holder;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
public class ChannelServiceModify implements ChannelServiceAccess
{
private final ChannelServiceModel model;
private final BiMap<String, String> map;
private final Map<String, String> unmodMap;
private final Map<String, DeployGroup> deployGroups;
private final Map<String, DeployKey> deployKeys;
public ChannelServiceModify ( final ChannelServiceModel model )
{
this.model = new ChannelServiceModel ( model );
this.map = HashBiMap.create ( model.getNameMap () );
this.unmodMap = Collections.unmodifiableMap ( this.map );
this.deployGroups = model.getDeployGroups ().stream ().collect ( Collectors.toMap ( DeployGroup::getId, i -> i ) );
this.deployKeys = model.getDeployGroups ().stream ().flatMap ( group -> group.getKeys ().stream () ).collect ( Collectors.toMap ( DeployKey::getId, i -> i ) );
}
public ChannelServiceModify ( final ChannelServiceModify other )
{
this ( other.model );
}
@Override
public Map<String, String> getNameMap ()
{
return this.unmodMap;
}
@Override
public String mapToId ( final String name )
{
return this.map.inverse ().get ( name );
}
@Override
public String mapToName ( final String id )
{
return this.map.get ( id );
}
public void putMapping ( final String id, final String name )
{
if ( name == null || name.isEmpty () )
{
this.model.getNameMap ().remove ( id );
this.map.remove ( id );
return;
}
final String oldId = this.map.inverse ().get ( name );
if ( oldId != null )
{
if ( oldId.equals ( id ) )
{
// no change
return;
}
throw new IllegalStateException ( String.format ( "There already is a channel with the name '%s'", name ) );
}
// put mapping
this.model.getNameMap ().put ( id, name );
this.map.put ( id, name );
}
public String deleteMapping ( final String channelId, final String name )
{
this.map.remove ( channelId, name );
return this.model.getNameMap ().remove ( channelId, name ) ? channelId : null;
}
public void deleteChannel ( final String channelId )
{
// delete channel name mapping
this.map.remove ( channelId );
this.model.getNameMap ().remove ( channelId );
// delete channel group mapping
this.model.getDeployGroupMap ().remove ( channelId );
}
ChannelServiceModel getModel ()
{
return this.model;
}
public DeployGroup createGroup ( final String name )
{
final DeployGroup result = new DeployGroup ( UUID.randomUUID ().toString (), name, Collections.emptyList () );
internalAdd ( result );
return result;
}
@Override
public DeployGroup getDeployGroup ( final String groupId )
{
return this.deployGroups.get ( groupId );
}
@Override
public Map<String, Set<String>> getDeployGroupMap ()
{
return Collections.unmodifiableMap ( this.model.getDeployGroupMap () );
}
public void updateGroup ( final String groupId, final String name )
{
modifyGroup ( groupId, old -> new DeployGroup ( old.getId (), name, old.getKeys () ) );
}
private void internalAdd ( final DeployGroup group )
{
this.model.getDeployGroups ().add ( group );
this.deployGroups.put ( group.getId (), group );
group.getKeys ().forEach ( key -> this.deployKeys.put ( key.getId (), key ) );
}
private DeployGroup internalRemove ( final String groupId )
{
final DeployGroup result = this.deployGroups.remove ( groupId );
if ( result != null )
{
this.model.getDeployGroups ().remove ( result );
// remove all keys from the deployKey map
result.getKeys ().stream ().map ( DeployKey::getId ).forEach ( this.deployKeys::remove );
}
return result;
}
public void deleteGroup ( final String groupId )
{
final DeployGroup group = internalRemove ( groupId );
if ( group != null )
{
// iterate over all channel -> group assignments and remove the groups
boolean cleanup = false;
for ( final Set<String> groups : this.model.getDeployGroupMap ().values () )
{
if ( groups.remove ( groupId ) )
{
cleanup = true;
}
}
if ( cleanup )
{
// remove empty groups from the model map
final Iterator<Set<String>> i = this.model.getDeployGroupMap ().values ().iterator ();
while ( i.hasNext () )
{
if ( i.next ().isEmpty () )
{
i.remove ();
}
}
}
}
}
@Override
public List<DeployGroup> getDeployGroups ()
{
return Collections.unmodifiableList ( this.model.getDeployGroups () );
}
private void modifyGroup ( final String groupId, final Function<DeployGroup, DeployGroup> func )
{
final DeployGroup group = internalRemove ( groupId );
if ( groupId == null )
{
throw new IllegalArgumentException ( String.format ( "Deploy group '%s' is unknown.", groupId ) );
}
final DeployGroup newGroup = func.apply ( group );
if ( newGroup != null )
{
internalAdd ( newGroup );
}
}
public DeployKey createKey ( final String groupId, final String name )
{
final Holder<DeployKey> result = new Holder<> ();
modifyGroup ( groupId, old -> new DeployGroup ( old.getId (), old.getName (), old.getKeys (), ( newGroup ) -> {
result.value = new DeployKey ( newGroup, UUID.randomUUID ().toString (), name, makeKey (), Instant.now () );
return Collections.singletonList ( result.value );
} ) );
return result.value;
}
public DeployKey deleteKey ( final String keyId )
{
final DeployKey key = this.deployKeys.remove ( keyId );
if ( key != null )
{
this.deployKeys.remove ( key.getId () );
modifyGroup ( key.getGroup ().getId (), old -> new DeployGroup ( old.getId (), old.getName (), subKeys ( old.getKeys (), key ) ) );
}
return key;
}
private static List<DeployKey> subKeys ( final List<DeployKey> keys, final DeployKey key )
{
final List<DeployKey> result = new CopyOnWriteArrayList<> ( keys );
result.remove ( key );
return result;
}
@Override
public DeployKey getDeployKey ( final String keyId )
{
return this.deployKeys.get ( keyId );
}
public DeployKey updateKey ( final String keyId, final String name )
{
final DeployKey key = this.deployKeys.get ( keyId );
if ( key == null )
{
return null;
}
final Holder<DeployKey> result = new Holder<> ();
modifyGroup ( key.getGroup ().getId (), old -> new DeployGroup ( old.getId (), old.getName (), subKeys ( old.getKeys (), key ), newGroup -> {
result.value = new DeployKey ( newGroup, key.getId (), name, key.getKey (), key.getCreationTimestamp () );
return Collections.singleton ( result.value );
} ) );
return result.value;
}
private String makeKey ()
{
return Tokens.createToken ( 32 /* FIXME: make configurable */ );
}
public Set<DeployGroup> getDeployGroupsForChannel ( final String channelId )
{
final Set<String> forChannel = this.model.getDeployGroupMap ().get ( channelId );
if ( forChannel == null )
{
return Collections.emptySet ();
}
return forChannel.stream ().map ( this.deployGroups::get ).collect ( toSet () );
}
public void assignDeployGroup ( final String channelId, final String groupId )
{
if ( !this.deployGroups.containsKey ( groupId ) )
{
throw new IllegalArgumentException ( String.format ( "Deploy group %s does not exists", groupId ) );
}
Set<String> list = this.model.getDeployGroupMap ().get ( channelId );
if ( list == null )
{
list = new HashSet<> ();
this.model.getDeployGroupMap ().put ( channelId, list );
}
list.add ( groupId );
}
public void unassignDeployGroup ( final String channelId, final String groupId )
{
final Set<String> list = this.model.getDeployGroupMap ().get ( channelId );
if ( list == null )
{
return;
}
list.remove ( groupId );
if ( list.isEmpty () )
{
this.model.getDeployGroupMap ().remove ( channelId );
}
}
/**
* Clear all mappings
*/
public void clear ()
{
this.model.getNameMap ().clear ();
this.map.clear ();
}
}