001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019 020package org.apache.isis.core.metamodel.adapter.version; 021 022import java.io.IOException; 023import java.io.Serializable; 024import java.util.Date; 025 026import org.apache.isis.core.commons.encoding.DataInputExtended; 027import org.apache.isis.core.commons.encoding.DataOutputExtended; 028import org.apache.isis.core.commons.encoding.Encodable; 029import org.apache.isis.core.commons.lang.DateExtensions; 030import org.apache.isis.core.metamodel.adapter.ObjectAdapter; 031import org.apache.isis.core.metamodel.adapter.oid.OidMarshaller; 032 033/** 034 * An instance of this class is held by each {@link ObjectAdapter} and is used 035 * to represent a particular version (at a point in time) of domain object 036 * wrapped by that adapter. 037 * 038 * <p> 039 * This is normally done using some form of incrementing number or timestamp, 040 * which would be held within the implementing class. The numbers, timestamps, 041 * etc should change for each changed object, and the different() method should 042 * indicate that the two Version objects are different. 043 * 044 * <p> 045 * The user's name and a timestamp should alos be kept so that when an message 046 * is passed to the user it can be of the form "user has change object at time" 047 */ 048public class Version implements Serializable, Encodable { 049 050 051 private static final long serialVersionUID = 1L; 052 053 private final Long sequence; 054 private final String user; 055 private final Long utcTimestamp; 056 057 // /////////////////////////////////////////////////////// 058 // factory methods 059 // /////////////////////////////////////////////////////// 060 061 062 public static Version create(final Long sequence) { 063 return create(sequence, null, (Long)null); 064 } 065 066 public static Version create(String sequence, String user, String utcTimestamp) { 067 if(sequence == null) { 068 return null; 069 } 070 return create(Long.parseLong(sequence), user, utcTimestamp != null?Long.parseLong(utcTimestamp):null); 071 } 072 073 public static Version create(final Long sequence, final String user, final Date time) { 074 return create(sequence, user, time !=null? time.getTime(): null); 075 } 076 077 public static Version create(Long sequence, String user, Long utcTimestamp) { 078 if(sequence == null) { 079 return null; 080 } 081 return new Version(sequence, user, utcTimestamp); 082 } 083 084 private Version(Long sequence, String user, Long utcTimestamp) { 085 this.sequence = sequence; 086 this.user = user; 087 this.utcTimestamp = utcTimestamp; 088 } 089 090 091 // /////////////////////////////////////////////////////// 092 // encodable 093 // /////////////////////////////////////////////////////// 094 095 public Version(final DataInputExtended input) throws IOException { 096 this(input.readLong(), input.readUTF(), input.readLong()); 097 } 098 099 @Override 100 public void encode(final DataOutputExtended output) throws IOException { 101 output.writeLong(sequence); 102 output.writeUTF(user); 103 output.writeLong(utcTimestamp); 104 } 105 106 107 // /////////////////////////////////////////////////////// 108 // getters 109 // /////////////////////////////////////////////////////// 110 111 /** 112 * The internal, strictly monotonically increasing, version number. 113 * 114 * <p> 115 * This might be the timestamp of the change, or it might be simply a number incrementing 1,2,3... 116 */ 117 public long getSequence() { 118 return sequence; 119 } 120 121 /** 122 * Returns the user who made the last change (used for display/reporting only) 123 * 124 * <p> 125 * May be null. 126 */ 127 public String getUser() { 128 return user; 129 } 130 131 /** 132 * The time of the last change, as UTC milliseconds. 133 * 134 * <p> 135 * May be null. 136 * 137 * @see #getTime() 138 */ 139 public Long getUtcTimestamp() { 140 return utcTimestamp; 141 } 142 143 /** 144 * Returns the time of the last change (used for display/reporting only, not comparison) 145 * 146 * <p> 147 * May be null. 148 * 149 * @see #getUtcTimestamp() 150 */ 151 public Date getTime() { 152 return utcTimestamp != null? new Date(this.utcTimestamp): null; 153 } 154 155 156 // /////////////////////////////////////////////////////// 157 // enString 158 // /////////////////////////////////////////////////////// 159 160 public String enString(OidMarshaller oidMarshaller) { 161 return oidMarshaller.marshal(this); 162 } 163 164 // /////////////////////////////////////////////////////// 165 // equals, hashCode 166 // /////////////////////////////////////////////////////// 167 168 @Override 169 public int hashCode() { 170 final int prime = 31; 171 int result = 1; 172 result = prime * result + ((sequence == null) ? 0 : sequence.hashCode()); 173 return result; 174 } 175 176 @Override 177 public boolean equals(Object obj) { 178 if (this == obj) 179 return true; 180 if (obj == null) 181 return false; 182 if (getClass() != obj.getClass()) 183 return false; 184 Version other = (Version) obj; 185 if (sequence == null) { 186 if (other.sequence != null) 187 return false; 188 } else if (!sequence.equals(other.sequence)) 189 return false; 190 return true; 191 } 192 193 /** 194 * Compares this version against the specified version and returns true if 195 * they are different versions (by checking {@link #getSequence()}). 196 * 197 * <p> 198 * This is use for optimistic checking, where the existence of a different 199 * version will normally cause a concurrency exception. 200 */ 201 public boolean different(Version version) { 202 return !equals(version); 203 } 204 205 206 ////////////////////////////////////////////////////////////// 207 // sequence 208 ////////////////////////////////////////////////////////////// 209 210 211 @Override 212 public String toString() { 213 return "#" + sequence + " " + getUser() + " " + DateExtensions.asTimestamp(getTime()); 214 } 215 216 /** 217 * Returns the sequence for printing/display 218 */ 219 public String sequence() { 220 return Long.toString(sequence, 16); 221 } 222 223}