net.sf.eBus.client.sysmessages.AbstractKeyMessage Maven / Gradle / Ivy
The newest version!
//
// Copyright 2015, 2016, 2019, 2020 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.client.sysmessages;
import com.google.common.base.Strings;
import java.io.Serializable;
import net.sf.eBus.messages.EMessage;
import net.sf.eBus.messages.EMessageKey;
import net.sf.eBus.messages.EMessageObject;
import net.sf.eBus.messages.ESystemMessage;
import net.sf.eBus.messages.UnknownMessageException;
import net.sf.eBus.util.Validator;
/**
* Base class for all system messages which contain a message
* class name and message subject. The reason for sending a
* message key as a separate message class name and subject is
* due to the receiving eBus application failing to de-serialize
* the message key because {@code Class.forName} throwing
* {@code ClassNotFoundException}. It is entirely reasonable for
* connected eBus applications not to use exactly the same
* message set. Therefore, if an eBus application receives a
* message class name for a class it doesn't know about, that
* inbound message will successfully deserialize but is ignored.
*
* @author Charles W. Rapp
*/
public abstract class AbstractKeyMessage
extends ESystemMessage
implements Serializable
{
//---------------------------------------------------------------
// Member data.
//
//-----------------------------------------------------------
// Constants.
//
/**
* Serialization version identifier.
*/
private static final long serialVersionUID = 0x050200L;
//-----------------------------------------------------------
// Locals.
//
/**
* The message key class name. The class name is transmitted
* rather than the {@code Class} instance because the far-end
* deserialization will fail if the {@code Class} is
* unknown.
*/
public final String messageClass;
/**
* The message key subject.
*/
public final String messageSubject;
//---------------------------------------------------------------
// Member methods.
//
//-----------------------------------------------------------
// Constructors.
//
/**
* Creates a new abstract key message from the configured
* message builder.
* @param builder message builder.
*/
protected AbstractKeyMessage(final Builder, ?> builder)
{
super (builder);
this.messageClass = builder.mMessageClass;
this.messageSubject = builder.mMessageSubject;
} // end of AbstractKeyMessage(Builder)
//
// end of Constructors.
//-----------------------------------------------------------
//-----------------------------------------------------------
// Object Method Overrides.
//
/**
* Returns {@code true} if {@code o} is a
* non-{@code null AdMessage} instance with ad status,
* message class, and message type equal to
* {@code this AdMessage} instance and {@code false}
* otherwise.
* @param o comparison object.
* @return {@code true} if the message fields are equal
* and {@code false} otherwise.
*/
// Calling super.equals is *never* silly.
@SuppressWarnings({"java:S2159"})
@Override
public boolean equals(final Object o)
{
boolean retcode = super.equals(o);
if (!retcode && o instanceof AbstractKeyMessage)
{
final AbstractKeyMessage keyMsg =
(AbstractKeyMessage) o;
retcode =
(messageClass.equals(keyMsg.messageClass) &&
messageSubject.equals(keyMsg.messageSubject));
}
return (retcode);
} // end of equals(Object)
/**
* Returns the advertisement message hash code.
* @return the advertisement message hash code.
*/
@Override
public int hashCode()
{
return (
(((super.hashCode() * 37) +
messageClass.hashCode()) * 37) +
messageSubject.hashCode());
} // end of hashCode()
/**
* Returns a human-readable text version of this message.
* @return text version of this message.
*/
@Override
public String toString()
{
return (
String.format(
"%s%n message key: %s/%s",
super.toString(),
messageClass,
messageSubject));
} // end of toString()
//
// end of Object Method Overrides.
//-----------------------------------------------------------
//-----------------------------------------------------------
// Get Methods.
//
/**
* Returns the message key based on the message class name
* and subject.
* @return the message key.
* @throws UnknownMessageException
* if {@link #messageClass} is an unknown class name.
*/
@SuppressWarnings ("unchecked")
public final EMessageKey messageKey()
throws UnknownMessageException
{
final Class extends EMessage> mc;
try
{
mc =
(Class extends EMessage>)
Class.forName(messageClass);
}
catch (ClassNotFoundException classex)
{
throw (new UnknownMessageException(messageClass));
}
return (new EMessageKey(mc, messageSubject));
} // end of messageKey()
//
// end of Get Methods.
//-----------------------------------------------------------
//---------------------------------------------------------------
// Member methods.
//
/**
* Base class for all {@link AbstractKeyMessage} builders.
* Provides message class and subject validation.
*
* @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.
*/
public abstract static class Builder>
extends ESystemMessage.Builder
{
//-----------------------------------------------------------
// Member data.
//
//-------------------------------------------------------
// Locals.
//
/**
* Configured message class.
*/
private String mMessageClass;
/**
* Configured message subject.
*/
private String mMessageSubject;
//-----------------------------------------------------------
// Member methods.
//
//-------------------------------------------------------
// Constructors.
//
protected Builder(final Class extends EMessageObject> targetClass)
{
super (targetClass);
} // end of Builder(Class)
//
// end of Constructors.
//-------------------------------------------------------
//-------------------------------------------------------
// Builder Method Overrides.
//
/**
* Checks if the message class and subject are
* configured. If not, then appends those problems to the
* list.
* @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)
{
// Note: only check for null since message class
// and subject cannot be set to an empty string.
return (super.validate(problems)
.requireNotNull(mMessageClass,
"message class")
.requireNotNull(mMessageSubject,
"message subject"));
} // end of validate(Validator)
//
// end of Builder Method Overrides.
//-------------------------------------------------------
//-------------------------------------------------------
// Set Methods.
//
/**
* Sets the message class name to the given value.
* @param mc message class name.
* @return {@code this} message builder.
* @throws IllegalArgumentException
* if {@code mc} is {@code null} or empty.
*/
@SuppressWarnings ("unchecked")
public final B messageClass(final String mc)
{
if (Strings.isNullOrEmpty(mc))
{
throw (
new IllegalArgumentException(
"mc is null or empty"));
}
mMessageClass = mc;
return ((B) this);
} // end of messageClass(String)
/**
* Sets the message subject to the given value.
* @param subject message subject.
* @return {@code this} message builder.
* @throws IllegalArgumentException
* if {@code subject} is {@code null} or empty.
*/
@SuppressWarnings ("unchecked")
public final B messageSubject(final String subject)
{
if (Strings.isNullOrEmpty(subject))
{
throw (
new IllegalArgumentException(
"subject is null or empty"));
}
mMessageSubject = subject;
return ((B) this);
} // end of messageSubject(final String subject)
/**
* Sets both the message class name and subject based on
* the given message key.
* @param key message key containing message class and
* subject.
* @return {@code this} message builder.
* @throws NullPointerException
* if {@code key} is {@code null}.
*/
@SuppressWarnings ("unchecked")
public final B messageKey(final EMessageKey key)
{
if (key == null)
{
throw (new NullPointerException("key is null"));
}
mMessageClass = key.className();
mMessageSubject = key.subject();
return ((B) this);
} // end of messageKey(final EMessageKey key)
//
// end of Set Methods.
//-------------------------------------------------------
} // end of class Builder
} // end of class AbstractKeyMessage