// -----------------------------------------------------------------------
//
// Copyright 2011-2012 GRNET S.A. All rights reserved.
//
// Redistribution and use in source and binary forms, with or
// without modification, are permitted provided that the following
// conditions are met:
//
// 1. Redistributions of source code must retain the above
// copyright notice, this list of conditions and the following
// disclaimer.
//
// 2. Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials
// provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// The views and conclusions contained in the software and
// documentation are those of the authors and should not be
// interpreted as representing official policies, either expressed
// or implied, of GRNET S.A.
//
// -----------------------------------------------------------------------
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.IO;
namespace Pithos.Core
{
public enum NetworkOperation
{
None,
Uploading,
Downloading,
Deleting,
Renaming
}
//The NetworkGate prevents starting download/uploads for files that are already in the process of downloading,
//uploading.
public class NetworkGate:IDisposable
{
public string FilePath { get; private set; }
public NetworkOperation Operation { get; private set; }
[ContractInvariantMethod]
private void Invariants()
{
Contract.Invariant(!String.IsNullOrWhiteSpace(FilePath));
Contract.Invariant(Path.IsPathRooted(FilePath));
}
//The state of each file is stored in a thread-safe dictionary
static readonly ConcurrentDictionary NetworkState = new ConcurrentDictionary();
public static NetworkOperation GetNetworkState(string path)
{
if (String.IsNullOrWhiteSpace(path))
throw new ArgumentNullException("path");
if (!Path.IsPathRooted(path))
throw new ArgumentException("path must be a rooted path", "path");
Contract.EndContractBlock();
NetworkOperation operation;
var lower = path.ToLower();
if (NetworkState.TryGetValue(lower, out operation))
return operation;
return NetworkOperation.None;
}
//Store a network operation for the specified path
public static void SetNetworkState(string path, NetworkOperation operation)
{
if (String.IsNullOrWhiteSpace(path))
throw new ArgumentNullException("path");
if (!Path.IsPathRooted(path))
throw new ArgumentException("path must be a rooted path", "path");
Contract.EndContractBlock();
var lower = path.ToLower();
NetworkState[lower] = operation;
//By default, None values don't need to be stored. They are stored anyway
//because TryRemove may fail.
if (operation == NetworkOperation.None)
{
NetworkOperation oldOperation;
NetworkState.TryRemove(lower, out oldOperation);
}
}
//Clients should acquire a NetworkGate before starting any network operation.
//If Acquire fails, another network operation is already in progress
public static NetworkGate Acquire(string path, NetworkOperation operation)
{
if (String.IsNullOrWhiteSpace(path))
throw new ArgumentNullException("path");
if (!Path.IsPathRooted(path))
throw new ArgumentException("path must be a rooted path", "path");
Contract.EndContractBlock();
var lower = path.ToLower();
var state = GetNetworkState(lower);
//If no operation is in progress, return a NetworkGate
return (state == NetworkOperation.None)
? new NetworkGate(lower, operation)
//otherwise return a gate with Fail flagged
: new NetworkGate(lower, NetworkOperation.None);
}
private NetworkGate(string path,NetworkOperation operation)
{
if (String.IsNullOrWhiteSpace(path))
throw new ArgumentNullException("path");
if (!Path.IsPathRooted(path))
throw new ArgumentException("path must be rooted","path");
Contract.EndContractBlock();
Operation = operation;
FilePath = path.ToLower();
//Skip dummy operations (those with Operation == None)
if (Operation != NetworkOperation.None)
//and store the file's operation
SetNetworkState(FilePath, operation);
}
//A NetworkGate has Failed if its operation is None
public bool Failed { get { return Operation == NetworkOperation.None; } }
//Release a gate by setting the NetworkOperation to None
public void Release()
{
//Skip Failed flags
if (!Failed)
//And reset the operation state for the file
SetNetworkState(FilePath,NetworkOperation.None);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose(bool disposing)
{
if (disposing)
{
Release();
}
}
}
}