All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.eBus.messages.EMessageObject Maven / Gradle / Ivy

//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later
// version.
//
// This library is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the GNU Lesser General Public License for more
// details.
//
// You should have received a copy of the GNU Lesser General
// Public License along with this library; if not, write to the
//
// Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330,
// Boston, MA
// 02111-1307 USA
//
// The Initial Developer of the Original Code is Charles W. Rapp.
// Portions created by Charles W. Rapp are
// Copyright 2013, 2016. Charles W. Rapp
// All Rights Reserved.
//

package net.sf.eBus.messages;

import java.io.Serializable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import net.sf.eBus.messages.type.DataType;

/**
 * This abstract class defines the two eBus message classes:
 * {@link net.sf.eBus.messages.EMessage} and
 * {@link net.sf.eBus.messages.EField}. Message instances are
 * used to publish notifications, requests, and replies. Field
 * instances are used to implement user-defined fields within
 * both messages and other fields.
 * 

* All {@code EMessageObject} sub-classes must have a * {@link EFieldInfo} class attribute which specifies the * {@code public final} class fields. This attribute is used to * define field order for serialization and construction. For * example, the user-defined field {@code UserInfo}: *

 *   
 * @EFieldInfo(fields = {"name", "age"})
 * public class UserInfo
 *     extends EField
 * {
 *     public UserInfo(String name, int age) {
 *         this.name = name;
 *         this.age = age;
 *     }
 *
 *     public final String name;
 *     public final int age;
 * }
 *   
 * 
* The {@code UserInfo} constructor arguments must match the * {@code @EFieldInfo.fields} order. Also, * {@code @EFieldInfo.fields} must appear as {@code public final} * class data members. If either requirement fails, then the * field will be declared invalid. *

* If another user-defined field is created which extends * {@code UserInfo} as follows: *

 *   
 * @EFieldInfo(fields = {"department"})
 * public class Employee
 *     extends UserInfo
 * {
 *     public Employee(String name, int age, String dept) {
 *         super (name, age);
 *         this.department = dept;
 *     }
 *
 *     public final String department;
 * }
 *   
 * 
* The {@code Employee} constructor arguments must first be the * super class fields and then the sub-class fields. *

* The field-ordering rules also apply to {@link EMessage} * classes. Because {@code EMessage} has two fields: * {@code public final String subject} and * {@code public final long timestamp}, all user-defined messages * must include these fields as the first two constructor * arguments. {@link EReplyMessage} has two more fields: * {@code public final EReplyMessage.ReplyStatus replyStatus} and * {@code public final String replyReason}. User-defined reply * message constructor must have as its first four arguments: * subject, timestamp, replyStatus, and replyReason. *

* {@code EMessageObject} constructor validates the above rules * the first time a concrete sub-class is constructed. If the * validation fails, then a {@link InvalidMessageException} is * thrown. This failure is cached so that the validation is done * only once. * * @author Charles Rapp */ public abstract class EMessageObject implements Serializable { //--------------------------------------------------------------- // Member data. // //----------------------------------------------------------- // Constants. // /** * A message object may have at most 31 fields. This is * because there are 31 usable bits in a 4-byte, signed * integer. */ public static final int MAX_FIELDS = 31; /** * Serialization version identifier. */ private static final long serialVersionUID = 0x040400L; //----------------------------------------------------------- // Statics. // /** * Maps the {@code EMessageObject} class to its validation * flag: {@code null} means the class has not been validated, * {@code true} means the class is valid, and {@code false} * means invalid. */ private static final ConcurrentMap, ValidationInfo> mValidMap = new ConcurrentHashMap<>(); //--------------------------------------------------------------- // Member methods. // //----------------------------------------------------------- // Constructors. // /** * Creates a new message object instance. The point behind * this constructor is to validate a user-defined message * or field as soon as it is instantiated. * @throws InvalidMessageException * if the user-defined message or field is invalid. */ protected EMessageObject() throws InvalidMessageException { final Class mc = this.getClass(); ValidationInfo info = mValidMap.get(mc); // Has this message been validated before? if (info == null) { // No, validate it now and store away the results. info = validate(mc); mValidMap.put(mc, info); } if (info.isValid() == false) { throw ( new InvalidMessageException( mc, info.reason(), info.error())); } } // end of EMessageObject() // // end of Constructors. //----------------------------------------------------------- /** * Returns the validation information for the specified * {@code EMessageObject} sub-class. * @param mc validate this message class. * @return the validation result. */ private static ValidationInfo validate(final Class mc) { boolean flag = true; String reason = null; Throwable t = null; try { // Called for effect only. This will create a // MessageType for this class. If mc is not a valid // message, then an InvalidMessageException is // thrown with the appropriate reason. DataType.findType(mc); } catch (InvalidMessageException msgex) { flag = false; reason = msgex.getMessage(); t = msgex; } return (new ValidationInfo(flag, reason, t)); } // end of validate(Class) //--------------------------------------------------------------- // Inner classes. // /** * This immutable class tracks the EMessageObject validity. */ private static final class ValidationInfo { //----------------------------------------------------------- // Member data. // //------------------------------------------------------- // Locals. // /** * {@code true} if the message is valid and * {@code false} otherwise. */ private final boolean mFlag; /** * Text explaining why {@code _flag} is {@code false}. * Set to {@code null} when {@code _flag} is * {@code true}. */ private final String mReason; /** * Thrown error which caused the exception. */ private final Throwable mError; //----------------------------------------------------------- // Member methods. // //------------------------------------------------------- // Constructors. // /** * Sets the message object validation to the given flag * and reason. * @param flag {@code true} if the message object is * valid and {@code false} otherwise. * @param reason text explaining the invalidity. * @param t error behind a {@code false flag}. */ private ValidationInfo(final boolean flag, final String reason, final Throwable t) { mFlag = flag; mReason = reason; mError = t; } // end of ValidationInfo(boolean, String, Throwable) // // end of Constructors. //------------------------------------------------------- //------------------------------------------------------- // Get Methods. // /** * Returns {@code true} if the message object is valid * and {@code false} otherwise. * @return {@code true} if the message object is valid. */ private boolean isValid() { return (mFlag); } // end of isValid() /** * Returns text explaining why the message is invalid. * May return {@code null}. * @return text explaining why the message is invalid. */ private String reason() { return (mReason); } // end of reason() /** * Returns the error behind an invalid message. * @return {@code Throwable} error. */ private Throwable error() { return (mError); } // end of error() // // end of Get Methods. //------------------------------------------------------- } // end of class ValidationInfo } // end of class EMessageObject





© 2015 - 2025 Weber Informatics LLC | Privacy Policy