001/* 002 * HA-JDBC: High-Availability JDBC 003 * Copyright (C) 2012 Paul Ferraro 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Lesser General Public License as published by 007 * the Free Software Foundation, either version 3 of the License, or 008 * (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018package net.sf.hajdbc.sql; 019 020import java.sql.SQLException; 021import java.util.ArrayList; 022import java.util.HashMap; 023import java.util.List; 024import java.util.Map; 025 026import javax.xml.bind.annotation.XmlAttribute; 027import javax.xml.bind.annotation.XmlElement; 028import javax.xml.bind.annotation.XmlType; 029 030import net.sf.hajdbc.Database; 031import net.sf.hajdbc.codec.Decoder; 032import net.sf.hajdbc.management.Description; 033import net.sf.hajdbc.management.ManagedAttribute; 034import net.sf.hajdbc.management.ManagedOperation; 035import net.sf.hajdbc.sql.AbstractDatabaseClusterConfiguration.Property; 036 037/** 038 * @author Paul Ferraro 039 * @param <Z> 040 */ 041@XmlType(propOrder = { "user", "password", "xmlProperties" }) 042public abstract class AbstractDatabase<Z> implements Database<Z> 043{ 044 @XmlAttribute(name = "id", required = true) 045 private String id; 046 @XmlAttribute(name = "location", required = true) 047 private String location; 048 @XmlElement(name = "user") 049 private String user; 050 @XmlElement(name = "password") 051 private String password; 052 053 @XmlAttribute(name = "weight") 054 private volatile Integer weight = 1; 055 @XmlAttribute(name = "local") 056 private Boolean local = false; 057 058 private Map<String, String> properties = new HashMap<String, String>(); 059 private boolean dirty = false; 060 private volatile boolean active = false; 061 062 @XmlElement(name = "property") 063 private Property[] getXmlProperties() 064 { 065 List<Property> properties = new ArrayList<Property>(this.properties.size()); 066 067 for (Map.Entry<String, String> entry: this.properties.entrySet()) 068 { 069 Property property = new Property(); 070 property.setName(entry.getKey()); 071 property.setValue(entry.getValue()); 072 properties.add(property); 073 } 074 075 return properties.toArray(new Property[properties.size()]); 076 } 077 078 @SuppressWarnings("unused") 079 private void setXmlProperties(Property[] properties) 080 { 081 for (Property property: properties) 082 { 083 this.properties.put(property.getName(), property.getValue()); 084 } 085 } 086 087 /** 088 * {@inheritDoc} 089 * @see net.sf.hajdbc.Database#getId() 090 */ 091 @ManagedAttribute 092 @Description("Uniquely identifies this database in the cluster") 093 @Override 094 public String getId() 095 { 096 return this.id; 097 } 098 099 public void setId(String id) 100 { 101 if (id.length() > ID_MAX_SIZE) 102 { 103 throw new IllegalArgumentException(String.format("Must be less than %d", ID_MAX_SIZE)); 104 } 105 this.id = id; 106 } 107 108 /** 109 * {@inheritDoc} 110 * @see net.sf.hajdbc.Database#getLocation() 111 */ 112 @ManagedAttribute 113 @Description("Identifies the location of this database") 114 @Override 115 public String getLocation() 116 { 117 return this.location; 118 } 119 120 @ManagedAttribute 121 public void setLocation(String name) 122 { 123 this.assertInactive(); 124 this.checkDirty(this.location, name); 125 this.location = name; 126 } 127 128 @ManagedAttribute 129 @Description("User ID for administrative connection authentication") 130 public String getUser() 131 { 132 return this.user; 133 } 134 135 @ManagedAttribute 136 public void setUser(String user) 137 { 138 this.assertInactive(); 139 this.checkDirty(this.user, user); 140 this.user = user; 141 } 142 143 @ManagedAttribute 144 @Description("Password for administrative connection authentication") 145 public String getPassword() 146 { 147 return this.password; 148 } 149 150 @ManagedAttribute 151 public void setPassword(String password) 152 { 153 this.assertInactive(); 154 this.checkDirty(this.password, password); 155 this.password = password; 156 } 157 158 /** 159 * {@inheritDoc} 160 * @see net.sf.hajdbc.Database#decodePassword(net.sf.hajdbc.codec.Decoder) 161 */ 162 @Override 163 public String decodePassword(Decoder decoder) throws SQLException 164 { 165 return (this.password != null) ? decoder.decode(this.password) : null; 166 } 167 168 /** 169 * {@inheritDoc} 170 * @see net.sf.hajdbc.Database#getWeight() 171 */ 172 @ManagedAttribute 173 @Description("Weight used in read request balancing") 174 @Override 175 public int getWeight() 176 { 177 return this.weight; 178 } 179 180 @ManagedAttribute 181 public void setWeight(int weight) 182 { 183 if (weight < 0) 184 { 185 throw new IllegalArgumentException(); 186 } 187 188 this.checkDirty(this.weight, weight); 189 this.weight = weight; 190 } 191 192 /** 193 * @see java.lang.Object#hashCode() 194 */ 195 @Override 196 public int hashCode() 197 { 198 return this.id.hashCode(); 199 } 200 201 /** 202 * @see java.lang.Object#equals(java.lang.Object) 203 */ 204 @Override 205 public boolean equals(Object object) 206 { 207 if ((object == null) || !(object instanceof Database<?>)) return false; 208 209 String id = ((Database<?>) object).getId(); 210 211 return (id != null) && id.equals(this.id); 212 } 213 214 /** 215 * @see java.lang.Object#toString() 216 */ 217 @Override 218 public String toString() 219 { 220 return this.id; 221 } 222 223 /** 224 * @see java.lang.Comparable#compareTo(Object) 225 */ 226 @Override 227 public int compareTo(Database<Z> database) 228 { 229 return this.id.compareTo(database.getId()); 230 } 231 232 @ManagedAttribute 233 @Description("Connection properties") 234 public Map<String, String> getProperties() 235 { 236 return this.properties; 237 } 238 239 @ManagedOperation 240 @Description("Removes the specified connection property") 241 public void removeProperty(String name) 242 { 243 this.assertInactive(); 244 245 String value = this.properties.remove(name); 246 247 this.dirty |= (value != null); 248 } 249 250 @ManagedOperation 251 @Description("Creates/updates the specified connection property") 252 public void setProperty(String name, String value) 253 { 254 this.assertInactive(); 255 256 if ((name == null) || (value == null)) 257 { 258 throw new IllegalArgumentException(); 259 } 260 261 String old = this.properties.put(name, value); 262 263 this.checkDirty(old, value); 264 } 265 266 @ManagedAttribute 267 public void setLocal(boolean local) 268 { 269 this.assertInactive(); 270 this.checkDirty(this.local, local); 271 this.local = local; 272 } 273 274 /** 275 * {@inheritDoc} 276 * @see net.sf.hajdbc.Database#isLocal() 277 */ 278 @ManagedAttribute 279 @Description("Indicates whether this database is local to this JVM") 280 @Override 281 public boolean isLocal() 282 { 283 return this.local; 284 } 285 286 /** 287 * {@inheritDoc} 288 * @see net.sf.hajdbc.Database#clean() 289 */ 290 @Override 291 public void clean() 292 { 293 this.dirty = false; 294 } 295 296 /** 297 * {@inheritDoc} 298 * @see net.sf.hajdbc.Database#isDirty() 299 */ 300 @Override 301 public boolean isDirty() 302 { 303 return this.dirty; 304 } 305 306 /** 307 * {@inheritDoc} 308 * @see net.sf.hajdbc.Database#isActive() 309 */ 310 @ManagedAttribute 311 @Description("Indicates whether or not this database is active") 312 @Override 313 public boolean isActive() 314 { 315 return this.active; 316 } 317 318 /** 319 * {@inheritDoc} 320 * @see net.sf.hajdbc.Database#setActive(boolean) 321 */ 322 @Override 323 public void setActive(boolean active) 324 { 325 this.active = active; 326 } 327 328 /** 329 * Set the dirty flag if the new value differs from the old value. 330 * @param oldValue 331 * @param newValue 332 */ 333 protected void checkDirty(Object oldValue, Object newValue) 334 { 335 this.dirty |= ((oldValue != null) && (newValue != null)) ? !oldValue.equals(newValue) : (oldValue != newValue); 336 } 337 338 /** 339 * Helper method to determine whether the connect() method requires authentication. 340 * @return true, if authentication is required, false otherwise 341 */ 342 protected boolean requiresAuthentication() 343 { 344 return this.user != null; 345 } 346 347 protected void assertInactive() 348 { 349 if (this.active) 350 { 351 throw new IllegalStateException(); 352 } 353 } 354}