
net.sf.eBus.messages.EReplyMessage Maven / Gradle / Ivy
//
// Copyright 2013, 2016 Charles W. Rapp
//
// Licensed 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.
//
package net.sf.eBus.messages;
import java.io.Serializable;
import java.util.Objects;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import net.sf.eBus.util.Validator;
/**
* Required base class for all application request messages. Adds
* the message fields reply status, reply reason, and final reply
* flag.
*
* This message is not abstract because it is a complete
* message in its own right and may be used to send a generic
* reply to any request.
*
* Note: messages are limited to 31 fields. This
* is due to eBus binary serialization format. This number is
* decreased to 27 fields for sub-classes because
* {@link EMessage} has two fields: {@code subject} and
* {@code timestamp} and {@code EReplyMessage} has two fields:
* {@link #replyStatus} and {@link #replyReason}.
*
* @see ENotificationMessage
* @see ERequestMessage
*
* @author Charles Rapp
*/
public class EReplyMessage
extends EMessage
implements Serializable
{
//---------------------------------------------------------------
// Enums.
//
/**
* An eBus reply is limited to either {@code OK} (success),
* {@code OK_CONTINUING} (in-progress reply with more to
* come), {@code CANCELED} (terminated before completion),
* {@code CANCEL_REJECT} (cancel request rejected), and
* {@code ERROR} (failure to process request and request is
* terminated).
*/
public enum ReplyStatus
{
/**
* The request handling is successfully completed.
*/
OK_FINAL (true),
/**
* The request handling is successful, so far. This
* status may change in the future.
*/
OK_CONTINUING (false),
/**
* The request handling is terminated either due to a
* cancel request or the replier unilaterally canceling
* the request.
*/
CANCELED (true),
/**
* An optional cancel request is rejected and the replier
* is still working the request. Repliers are not
* required to respond to a cancel request with this
* status but it is considered good programming to do so.
*/
CANCEL_REJECT (false),
/**
* The request handling failed. There will be no further
* replies.
*/
ERROR (true);
//-----------------------------------------------------------
// Member data.
//
//-------------------------------------------------------
// Locals.
//
/**
* If {@code true}, then this is the final reply and no
* more replies are expected for this request.
*/
private final boolean mFinalReply;
//-----------------------------------------------------------
// Member methods.
//
//-------------------------------------------------------
// Constructors.
//
/**
* Creates a reply status instance with the given final
* reply flag.
* @param finalReply {@code true} if this status means
* this is a final reply.
*/
private ReplyStatus(final boolean finalReply)
{
mFinalReply = finalReply;
} // end of ReplyStatus(boolean)
//
// end of Constructors.
//-------------------------------------------------------
//-------------------------------------------------------
// Get Methods.
//
/**
* Returns {@code true} if this is a final reply and
* {@code false} if more replies are expected.
* @return {@code true} if this is a final reply.
*/
public boolean isFinal()
{
return (mFinalReply);
} // end of isFinal()
//
// end of Get Methods.
//-------------------------------------------------------
} // end of enum ReplyStatus
//---------------------------------------------------------------
// Member data.
//
//-----------------------------------------------------------
// Constants.
//
/**
* Serialization version identifier.
*/
private static final long serialVersionUID = 0x060000L;
//-----------------------------------------------------------
// Locals.
//
/**
* The current request handling status. If
* {@link ReplyStatus#ERROR}, then {@link #replyReason} will
* contain text explaining why the request was rejected.
*
* This field is displayed first.
*
*/
@FieldDisplayIndex (index = 0)
public final ReplyStatus replyStatus;
/**
* The reason for a {@link ReplyStatus#ERROR} reply status.
* May be {@code null} or empty.
*
* This field is displayed second.
*
*/
@FieldDisplayIndex (index = 1)
@Nullable
public final String replyReason;
//---------------------------------------------------------------
// Member methods.
//
//-----------------------------------------------------------
// Constructors.
//
/**
* Creates a new eBus reply message based on the given reply
* message builder. {@code builder} is guaranteed to contain
* a valid message configuration at this point.
* @param builder contains the reply message configuration.
*/
protected EReplyMessage(final Builder, ?> builder)
{
super (builder);
this.replyStatus = builder.mReplyStatus;
this.replyReason = builder.mReplyReason;
} // end of EReplyMessage(EReplyMessageBuilder)
//
// end of Constructors.
//-----------------------------------------------------------
//-----------------------------------------------------------
// Object Method Overrides.
//
/**
* Returns {@code true} if {@code o} is a
* non-{@code null EReplyMessage} instance with a reply
* status and reply reason equal to
* {@code this EReplyMessage} instance and {@code false}
* otherwise.
* @param o comparison object.
* @return {@code true} if the message fields are equal
* and {@code false} otherwise.
*/
@SuppressWarnings({"java:S2159"})
@Override
public boolean equals(final Object o)
{
boolean retcode = super.equals(o);
if (!retcode)
{
// no-op.
}
else if (!(o instanceof EReplyMessage))
{
retcode = false;
}
else
{
final EReplyMessage msg = (EReplyMessage) o;
retcode =
(replyStatus == msg.replyStatus &&
(replyReason == null ?
msg.replyReason == null :
replyReason.equals(msg.replyReason)));
}
return (retcode);
} // end of equals(Object)
/**
* Returns the reply message hash code.
* @return the reply message hash code.
*/
@Override
public int hashCode()
{
return (Objects.hash(super.hashCode(),
replyStatus,
replyReason));
} // end of hashCode()
/**
* Returns the message subject, timestamp, and sequence
* number as a string.
* @return the message as text.
*/
@Override
public String toString()
{
return (
String.format(
"%s%n reply status: %s%n reply reason: %s",
super.toString(),
replyStatus,
(replyReason == null ?
"(no reason)" :
replyReason)));
} // end of toString()
//
// end of Object Method Overrides.
//-----------------------------------------------------------
//-----------------------------------------------------------
// Get Methods.
//
/**
* Returns {@code true} if this is the final reply and
* {@code false} if another reply is to be expected.
* @return {@code true} if this is the final reply.
*/
public boolean isFinal()
{
return (replyStatus.isFinal());
} // end of isFinal()
//
// end of Get Methods.
//-----------------------------------------------------------
/**
* Returns the {@code EReplyMessage} builder.
* @return {@link EReplyMessage.Builder} instance.
*/
@SuppressWarnings({"java:S1452"})
public static EReplyMessage.Builder, ?> builder()
{
return (new ConcreteBuilder());
} // end of builder()
//---------------------------------------------------------------
// Inner classes.
//
/**
* Base class for all {@link EReplyMessage} builders. Used by
* eBus when de-serializing an encoded message back into the
* target message object.
*
* @param builds this target message class.
* @param message builder subclass. Needed to return the
* correct builder type when setting fields. If this were not
* the case, field chaining would not work.
*/
@SuppressWarnings ("unchecked")
public abstract static class Builder>
extends EMessage.Builder
{
//-----------------------------------------------------------
// Member data.
//
//-------------------------------------------------------
// Locals.
//
/**
* Reply is either OK but continuing, OK and final, and
* error (which is always final). Must be set.
*/
@Nonnull
protected ReplyStatus mReplyStatus;
/**
* Generally text explaining a error status. May be
* {@code null} or an empty string.
*/
@Nullable
protected String mReplyReason;
//-----------------------------------------------------------
// Member methods.
//
//-------------------------------------------------------
// Constructors.
//
protected Builder(final Class extends EMessageObject> targetClass)
{
super (targetClass, MessageType.REPLY);
} // end of Builder(Class)
//
// end of Constructors.
//-------------------------------------------------------
//-------------------------------------------------------
// Abstract Method Overrides.
//
/**
* Checks if reply status is set. Note that the reply
* reason is not required to be set; it may be
* {@code null} or an empty string.
* @param problems used to check field validity and
* collect discovered invalid fields.
* @return {@code problems} to allow for method chaining.
*/
@Override
protected Validator validate(final Validator problems)
{
return (super.validate(problems)
.requireNotNull(mReplyStatus,
"reply status"));
} // end of validate(Validator)
//
// end of Abstract Method Overrides.
//-------------------------------------------------------
//-------------------------------------------------------
// Set Methods.
//
/**
* Sets the replier status for the matching request.
* @param status replier status.
* @return {@code this} builder instance.
* @throws NullPointerException
* if {@code status} is {@code null}.
*/
public final B replyStatus(final ReplyStatus status)
{
mReplyStatus =
Objects.requireNonNull(status, "status is null");
return ((B) this);
} // end of replyStatus(ReplyStatus)
/**
* Sets the reply status reason. Generally used to
* explain a {@link ReplyStatus#ERROR error} status.
* {@code reason} may be {@code null} or an empty string.
* @param reason reply status reason.
* @return {@code this} builder instance.
*/
public final B replyReason(final String reason)
{
mReplyReason = reason;
return ((B) this);
} // end of replyReason(String)
//
// end of Set Methods.
//-------------------------------------------------------
} // end of class EReplyMessageBuilder
/**
* Since {@code EReplyMessage} is not abstract and may be
* instantiated as a concrete message, this builder is used
* to create an {@code EReplyMessage} instance.
*/
public static final class ConcreteBuilder
extends EReplyMessage.Builder
{
//-----------------------------------------------------------
// Member data.
//
//-----------------------------------------------------------
// Member methods.
//
//-------------------------------------------------------
// Constructors.
//
private ConcreteBuilder()
{
super (EReplyMessage.class);
} // end of ConcreteBuilder()
//
// end of Constructors.
//-------------------------------------------------------
//-------------------------------------------------------
// Abstract Method Overrides.
//
@Override
protected EReplyMessage buildImpl()
{
return (new EReplyMessage(this));
} // end of buildImpl()
//
// end of Abstract Method Overrides.
//-------------------------------------------------------
} // end of class ConcreteBuilder
} // end of class EReplyMessage