001/* 002 * #%L 003 * Netarchivesuite - common 004 * %% 005 * Copyright (C) 2005 - 2014 The Royal Danish Library, the Danish State and University Library, 006 * the National Library of France and the Austrian National Library. 007 * %% 008 * This program is free software: you can redistribute it and/or modify 009 * it under the terms of the GNU Lesser General Public License as 010 * published by the Free Software Foundation, either version 2.1 of the 011 * License, or (at your option) any later version. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Lesser Public License for more details. 017 * 018 * You should have received a copy of the GNU General Lesser Public 019 * License along with this program. If not, see 020 * <http://www.gnu.org/licenses/lgpl-2.1.html>. 021 * #L% 022 */ 023package dk.netarkivet.common.distribute; 024 025import java.io.ObjectInputStream; 026import java.io.ObjectOutputStream; 027import java.io.Serializable; 028 029import dk.netarkivet.common.exceptions.ArgumentNotValid; 030import dk.netarkivet.common.exceptions.IOFailure; 031import dk.netarkivet.common.exceptions.PermissionDenied; 032import dk.netarkivet.common.utils.ExceptionUtils; 033 034/** 035 * Common base class for all messages exchanged in the NetarchiveSuite. 036 */ 037@SuppressWarnings({"serial"}) 038public abstract class NetarkivetMessage implements Serializable { 039 040 // contains the error messages when isOk=false 041 private String errMsg; 042 043 // when false an error occurred processing the message 044 private boolean isOk = true; 045 046 // id of this message. Is set when sent and null until then 047 private String id; 048 049 // Channel to which this message is to be sent 050 private ChannelID to; 051 052 // Channel on which replies are expected 053 private ChannelID replyTo; 054 055 // a delimiter to separate error messages 056 static final String ERROR_DELIMITTER = "\n-----------------\n"; 057 058 protected String replyOfId; 059 060 /** 061 * Creates a new NetarkivetMessage. 062 * 063 * @param to the initial receiver of the message 064 * @param replyTo the initial sender of the message 065 * @throws ArgumentNotValid if to==replyTo, the replyTo parameter is a topic instead of a queue, or there is a null 066 * parameter. 067 */ 068 protected NetarkivetMessage(ChannelID to, ChannelID replyTo) { 069 ArgumentNotValid.checkNotNull(to, "to"); 070 ArgumentNotValid.checkNotNull(replyTo, "replyTo"); 071 072 if (to.getName().equals(replyTo.getName())) { 073 throw new ArgumentNotValid("to and replyTo should not be equal."); 074 } 075 076 // Have not implemented replying to a topic because there is no use 077 // for it in our current architecture 078 if (Channels.isTopic(replyTo.getName())) { 079 throw new ArgumentNotValid("Reply channel must be queue but " + replyTo.toString() + " is a Topic"); 080 } 081 082 this.to = to; 083 this.replyTo = replyTo; 084 this.id = null; 085 this.replyOfId = null; 086 } 087 088 /** 089 * Did an error occur when processing the message. 090 * 091 * @return true if no error occurred, otherwise false 092 */ 093 public boolean isOk() { 094 return isOk; 095 } 096 097 /** 098 * Set or append error message. Sets isOk field to false. 099 * 100 * @param err error message 101 */ 102 public void setNotOk(String err) { 103 if (isOk) { 104 errMsg = err; 105 this.isOk = false; 106 } else { 107 errMsg += ERROR_DELIMITTER; 108 errMsg += err; 109 } 110 } 111 112 /** 113 * Set error message based on an exception. 114 * 115 * @param e An exception thrown during processing. 116 */ 117 public void setNotOk(Throwable e) { 118 setNotOk(e.toString() + "\n" + ExceptionUtils.getStackTrace(e)); 119 } 120 121 /** 122 * Retrieve error message. 123 * 124 * @return error message 125 * @throws PermissionDenied if the message is not an error message 126 */ 127 public String getErrMsg() throws PermissionDenied { 128 if (isOk) { 129 throw new PermissionDenied("Can't get error message for message '" + this + " that has had no error"); 130 } 131 return errMsg; 132 } 133 134 /** 135 * Retrieve message id. Note that message ID is not set until message is sent, and this method must not be called 136 * before then. 137 * 138 * @return message id 139 * @throws PermissionDenied If the message has not yet been sent. 140 */ 141 public synchronized String getID() { 142 if (id == null) { 143 throw new PermissionDenied("This message has not been sent, and does not yet have an ID"); 144 } 145 return id; 146 } 147 148 /** 149 * Sets the ID of this message if it has not already been set. 150 * 151 * @param newId The new ID 152 */ 153 synchronized void updateId(String newId) { 154 if (this.id == null) { 155 this.id = newId; 156 } 157 if (this.replyOfId == null) { 158 this.replyOfId = newId; 159 } 160 } 161 162 /** 163 * Retrieve replyOfId. This is set by subclasses of NetarkivetMessage, to indicate that this is a reply of some 164 * other message. If the subclass doesn't set replyOfId, this method behaves like getId. 165 * 166 * @return replyOfId 167 */ 168 public synchronized String getReplyOfId() { 169 if (replyOfId != null) { 170 return replyOfId; 171 } else { 172 return getID(); 173 } 174 } 175 176 /** 177 * Retrieve initial destination. 178 * 179 * @return initial destination 180 */ 181 public ChannelID getTo() { 182 return to; 183 } 184 185 /** 186 * Retrieve specified reply channel. 187 * 188 * @return initial origin 189 */ 190 public ChannelID getReplyTo() { 191 return replyTo; 192 } 193 194 /** 195 * Returns a string containing: <id>: To <toName> ReplyTo <replyToName> <isOK> [:error message]. 196 * 197 * @return String representation of Message. 198 */ 199 public String toString() { 200 String s = (id == null ? "NO ID" : id); 201 s += ": To " + to.getName() + " ReplyTo " + replyTo.getName(); 202 s += isOk() ? " OK" : " Error: " + errMsg; 203 return s; 204 } 205 206 /** 207 * Invoke default method for deserializing object. 208 * 209 * @param s The stream the object is read from. 210 */ 211 private void readObject(ObjectInputStream s) { 212 try { 213 s.defaultReadObject(); 214 } catch (Exception e) { 215 throw new IOFailure("Unexpected error during deserialization", e); 216 } 217 } 218 219 /** 220 * Invoke default method for serializing object. 221 * 222 * @param s The stream the object is written to. 223 */ 224 private void writeObject(ObjectOutputStream s) { 225 try { 226 s.defaultWriteObject(); 227 } catch (Exception e) { 228 throw new IOFailure("Unexpected error during serialization", e); 229 } 230 } 231 232 /** 233 * Check, if a given message has been sent yet. If the message has a null id, it hasn't been sent yet. 234 * 235 * @return true, if message has been sent yet, false otherwise. 236 */ 237 public synchronized boolean hasBeenSent() { 238 return (this.id != null); 239 } 240 241}