net.sf.eBus.messages.ENotificationMessage Maven / Gradle / Ivy
//
// Copyright 2013, 2016, 2019, 2021 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;
/**
* Required base class for all application publish/subscribe
* notification messages. As of eBus release 5.6.0 this class
* has two optional fields: {@link #publisherId} and
* {@link #position}. {@code publisherId} can be used to uniquely
* define a publisher when there are multiple publishers for
* the same message key within a system. It is the application's
* responsibility to define the meaning and uniqueness of
* publisher identifiers.
*
* Position is used to define message ordering when notification
* messages are published at a rate faster than wallclock time
* granularity. In other words, there are multiple notification
* messages per timestamp. If these messages are persisted then
* message ordering may be lost on retrieval. The
* {@code position} field may be used as an index within a
* given time limit to guarantee correct ordering when retrieved
* from persistent store.
*
*
* Combining {@code subject}, {@code timestamp},
* {@code publisherId}, and {@code position} guarantees a unique
* index for any notification message placed into persistent
* store if publisher identifier and message position
* are correctly used.
*
*
* @see ERequestMessage
* @see EReplyMessage
*
* @author Charles Rapp
*/
public abstract class ENotificationMessage
extends EMessage
implements Serializable
{
//---------------------------------------------------------------
// Member data.
//
//-----------------------------------------------------------
// Constants.
//
/**
* {@link #publisherId} defaults to {@value} when not set.
*/
public static final long NO_PUB_ID = 0;
/**
* Serialization version identifier.
*/
private static final long serialVersionUID = 0x060000L;
//-----------------------------------------------------------
// Locals.
//
/**
* Optional field used to define the publisher which
* produced this notification. This identifier is set by the
* application. eBus has no responsibility in assigning this
* value or guaranteeing identifier uniqueness.
*
* Therefore the meaning of this identifier is
* within the application. If this identifier needs to be
* unique between application instances and across networks,
* then it is up to developers to guarantee identifier
* uniqueness.
*
*
* If the publisher identifier is not defined then it
* defaults to {@link #NO_PUB_ID}.
*
*/
public final long publisherId;
/**
* This optional field is meant to be used to maintain
* notification order when placed in persistent store. The
* problem is that system timestamps have a set granularity
* (whether millisecond, microsecond, or nanosecond). If more
* than one notification message is published within that
* timestamp granularity, then message ordering may
* be lost when retrieved from persistent store when ordering
* by {@link #timestamp} alone.
*
* The solution is to combine {@code timestamp} with
* {@code position} which is incremented after each
* publish. There is one position index per unique
* {@link EMessageKey message key} and {@link #publisherId}.
* Depending on message publishing rate this position index
* should be reset at a fixed rate to prevent the index
* from going past {@link Integer#MAX_VALUE}
* (2,147,483,647).
*
*
* This value defaults to zero when not defined.
*
*/
public final int position;
//---------------------------------------------------------------
// Member methods.
//
//-----------------------------------------------------------
// Constructors.
//
/**
* Creates a new eBus notification message based on the given
* message builder. {@code builder} is guaranteed to contain
* a valid message configuration at this point.
* @param builder contains the eBus notification message
* configuration.
*/
protected ENotificationMessage(final Builder, ?> builder)
{
super (builder);
publisherId = builder.mPubId;
position = builder.mPosition;
} // end of ENotificationMessage(EMessageBuilder)
//
// end of Constructors.
//-----------------------------------------------------------
//-----------------------------------------------------------
// Object Method Overrides.
//
/**
* Returns {@code true} if {@code o} is a
* non-{@code null ENotificationMessage} instance with a
* publisher identifier and position equal to
* {@code this ENotificationMessage} 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 = (this == o);
if (!retcode && o instanceof ENotificationMessage)
{
final ENotificationMessage msg =
(ENotificationMessage) o;
retcode = (super.equals(msg) &&
publisherId == msg.publisherId &&
position == msg.position);
}
return (retcode);
} // end of equals(Object)
/**
* Returns notification message hash code.
* @return notification message hash code.
*/
@Override
public int hashCode()
{
return (Objects.hash(super.hashCode(),
publisherId,
position));
} // end of hashCode()
/**
* Returns notification message publisher ID and position as
* a string.
* @return notification message publisher ID and position as
* a string.
*/
@Override
public String toString()
{
return (
String.format(
"%s%n publisherId: %d%n position: %,d",
super.toString(),
publisherId,
position));
} // end of toString()
//
// end of Object Method Overrides.
//-----------------------------------------------------------
//---------------------------------------------------------------
// Inner classes.
//
/**
* Base class for all {@link ENotificationMessage} 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.
//
/**
* Optional publisher identifier.
*/
protected long mPubId;
/**
* Notification message position in feed stream.
*/
protected int mPosition;
//-----------------------------------------------------------
// Member methods.
//
//-------------------------------------------------------
// Constructors.
//
/**
* Creates a new notification message builder for the
* given message class.
* @param targetClass notification message sub-class.
*/
protected Builder(final Class extends EMessageObject> targetClass)
{
super (targetClass, MessageType.NOTIFICATION);
mPubId = NO_PUB_ID;
mPosition = 0;
} // end of Builder(Class)
/**
* Creates a new notification message builder for the
* given message class and subject.
* @param targetClass notification message sub-class.
* @param subject message subject.
*/
protected Builder(final Class extends EMessageObject> targetClass,
final String subject)
{
super (targetClass, subject, MessageType.NOTIFICATION);
mPubId = NO_PUB_ID;
mPosition = 0;
} // end of Builder(Class, String)
//
// end of Constructors.
//-------------------------------------------------------
//-------------------------------------------------------
// Set Methods.
//
/**
* Sets publisher identifier to given value. There is no
* limitations on {@code id} value. Returns {@code this}
* builder configuration so that field configuration may
* be chained.
* @param id publisher identifier.
* @return {@code this} builder instance.
*/
public final B publisherId(final long id)
{
mPubId = id;
return ((B) this);
} // end of publisherId(long)
/**
* Sets notification position to given value which must
* be ≥ zero. Returns {@code this} builder
* configuration so that field configuration may be
* chained.
* @param pos notification message position in feed
* publish order.
* @return {@code this} builder instance.
* @throws IllegalArgumentException
* if {@code pos} is < zero.
*/
public final B position(final int pos)
{
if (pos < 0)
{
throw (
new IllegalArgumentException(
"position < zero"));
}
mPosition = pos;
return ((B) this);
} // end of position(int)
//
// end of Set Methods.
//-------------------------------------------------------
} // end of class Builder
} // end of class ENotificationMessage