blob: 56f84462a31e41ccb33509b3c7ad1039918280cf [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache 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://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
using System;
using System.Collections.Generic;
using Apache.Qpid.Proton.Types;
using Apache.Qpid.Proton.Types.Messaging;
using Apache.Qpid.Proton.Types.Transactions;
namespace Apache.Qpid.Proton.Client.Implementation
{
/// <summary>
/// Client implementation of a delivery state mapping to the proton types
/// </summary>
public abstract class ClientDeliveryState : IDeliveryState
{
public bool IsAccepted => Type == DeliveryStateType.Accepted;
public abstract DeliveryStateType Type { get; }
public abstract Types.Transport.IDeliveryState ProtonDeliveryState { get; }
}
/// <summary>
/// Client version of the proton Accepted delivery state
/// </summary>
public sealed class ClientAccepted : ClientDeliveryState
{
public static readonly ClientAccepted Instance = new ClientAccepted();
private ClientAccepted() { }
public override DeliveryStateType Type => DeliveryStateType.Accepted;
public override Types.Transport.IDeliveryState ProtonDeliveryState => Types.Messaging.Accepted.Instance;
}
/// <summary>
/// Client version of the proton Released delivery state
/// </summary>
public sealed class ClientReleased : ClientDeliveryState
{
public static readonly ClientReleased Instance = new ClientReleased();
private ClientReleased() { }
public override DeliveryStateType Type => DeliveryStateType.Released;
public override Types.Transport.IDeliveryState ProtonDeliveryState => Types.Messaging.Released.Instance;
}
/// <summary>
/// Client version of the proton Rejected delivery state
/// </summary>
public sealed class ClientRejected : ClientDeliveryState
{
private readonly Rejected rejected = new Rejected();
internal ClientRejected(Rejected rejected)
{
this.rejected.Error = rejected.Error?.Copy();
}
/// <summary>
/// Create a new Rejected client delivery state with the provided information.
/// </summary>
/// <param name="condition">The condition value to convey to the remote</param>
/// <param name="description">The description value to convey to the remote</param>
public ClientRejected(string condition, string description)
{
this.rejected.Error = new Types.Transport.ErrorCondition(condition, description);
}
/// <summary>
/// Create a new Rejected client delivery state with the provided information.
/// </summary>
/// <param name="condition">The condition value to convey to the remote</param>
/// <param name="description">The description value to convey to the remote</param>
/// <param name="info">The information map value to convey to the remote</param>
public ClientRejected(string condition, string description, IDictionary<string, object> info)
{
if (condition != null || description != null)
{
rejected.Error = new Types.Transport.ErrorCondition(
Symbol.Lookup(condition), description, ClientConversionSupport.ToSymbolKeyedMap(info));
}
}
public override DeliveryStateType Type => DeliveryStateType.Rejected;
public override Types.Transport.IDeliveryState ProtonDeliveryState => rejected;
}
/// <summary>
/// Client version of the proton Modifed delivery state
/// </summary>
public sealed class ClientModified : ClientDeliveryState
{
private readonly Modified modified = new Modified();
internal ClientModified(Modified modified)
{
this.modified.DeliveryFailed = modified.DeliveryFailed;
this.modified.UndeliverableHere = modified.UndeliverableHere;
this.modified.MessageAnnotations = new Dictionary<Symbol, object>(modified.MessageAnnotations);
}
/// <summary>
/// Create a new instance with the given outcome values.
/// </summary>
/// <param name="failed">did the delivery fail for some reason</param>
/// <param name="undeliverable">should the delivery be treated an not deliverable here any longer</param>
public ClientModified(bool failed, bool undeliverable)
{
modified.DeliveryFailed = failed;
modified.UndeliverableHere = undeliverable;
}
/// <summary>
/// Create a new instance with the given outcome values.
/// </summary>
/// <param name="failed">did the delivery fail for some reason</param>
/// <param name="undeliverable">should the delivery be treated an not deliverable here any longer</param>
/// <param name="annotations">modification to existing message annotations</param>
public ClientModified(bool failed, bool undeliverable, IDictionary<string, object> annotations)
{
modified.DeliveryFailed = failed;
modified.UndeliverableHere = undeliverable;
modified.MessageAnnotations = ClientConversionSupport.ToSymbolKeyedMap(annotations);
}
public override DeliveryStateType Type => DeliveryStateType.Modified;
public override Types.Transport.IDeliveryState ProtonDeliveryState => modified;
}
/// <summary>
/// Client version of the proton Modifed delivery state
/// </summary>
public sealed class ClientTransactional : ClientDeliveryState
{
private readonly TransactionalState txnState = new TransactionalState();
internal ClientTransactional(TransactionalState txnState)
{
this.txnState.Outcome = txnState.Outcome;
this.txnState.TxnId = txnState.TxnId.Copy();
}
public override DeliveryStateType Type => DeliveryStateType.Transactional;
public override Types.Transport.IDeliveryState ProtonDeliveryState => txnState;
}
#region Extension types for Proton and Client delivery state types
internal static class DeliveryStateExtensions
{
public static IDeliveryState ToClientDeliveryState(this Types.Messaging.IOutcome outcome)
{
if (outcome == null)
{
return null;
}
if (outcome is Accepted)
{
return ClientAccepted.Instance;
}
else if (outcome is Released)
{
return ClientReleased.Instance;
}
else if (outcome is Rejected)
{
return new ClientRejected((Rejected)outcome);
}
else if (outcome is Modified)
{
return new ClientModified((Modified)outcome);
}
throw new ArgumentException("Cannot map to unknown Proton Outcome to a client delivery state: " + outcome);
}
public static IDeliveryState ToClientDeliveryState(this Types.Transport.IDeliveryState deliveryState)
{
if (deliveryState == null)
{
return null;
}
if (deliveryState is Accepted)
{
return ClientAccepted.Instance;
}
else if (deliveryState is Released)
{
return ClientReleased.Instance;
}
else if (deliveryState is Rejected)
{
return new ClientRejected((Rejected)deliveryState);
}
else if (deliveryState is Modified)
{
return new ClientModified((Modified)deliveryState);
}
else if (deliveryState is TransactionalState)
{
return new ClientTransactional((TransactionalState)deliveryState);
}
throw new ArgumentException("Cannot map to unknown Proton delivery state to a client delivery state: " + deliveryState);
}
public static Types.Transport.IDeliveryState AsProtonType(this IDeliveryState state)
{
if (state == null)
{
return null;
}
else if (state is ClientDeliveryState)
{
return ((ClientDeliveryState)state).ProtonDeliveryState;
}
else
{
switch (state.Type)
{
case DeliveryStateType.Accepted:
return Accepted.Instance;
case DeliveryStateType.Released:
return Released.Instance;
case DeliveryStateType.Rejected:
return new Rejected(); // TODO - How do we aggregate the different values into one DeliveryState Object
case DeliveryStateType.Modified:
return new Modified(); // TODO - How do we aggregate the different values into one DeliveryState Object
case DeliveryStateType.Transactional:
throw new ArgumentException("Cannot manually enlist delivery in AMQP Transactions");
default:
throw new InvalidOperationException("Client does not support the given Delivery State type: " + state.Type);
}
}
}
public static DeliveryStateType ToDeliveryStateType(this Symbol outcome)
{
try
{
return Enum.Parse<DeliveryStateType>(outcome?.ToString(), true);
}
catch (Exception)
{
throw new ArgumentException("Cannot map outcome name to unknown Proton DeliveryState.Type");
}
}
public static Symbol ToSymbolicType(this DeliveryStateType type)
{
return Symbol.Lookup(type.ToString().ToUpper());
}
}
#endregion
}