/*
* RHQ Management Platform
* Copyright (C) 2005-2008 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2, as
* published by the Free Software Foundation, and/or the GNU Lesser
* General Public License, version 2.1, also as published by the Free
* Software Foundation.
*
* This program 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 General Public License and the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* and the GNU Lesser General Public License along with this program;
* if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.rhq.core.domain.cloud.composite;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
* @author Joseph Marques
*/
public class FailoverListComposite implements Iterator<FailoverListComposite.ServerEntry>, Serializable {
private static final long serialVersionUID = 1L;
private List<ServerEntry> servers;
private int nextIndex = 0;
public static class ServerEntry implements Serializable {
private static final long serialVersionUID = 1L;
public final int serverId;
public final String address;
public final int port;
public final int securePort;
public ServerEntry(String address, int port, int securePort) {
this(-1, address, port, securePort);
}
public ServerEntry(int serverId, String address, int port, int securePort) {
this.serverId = serverId;
this.address = address;
this.port = port;
this.securePort = securePort;
}
@Override
public String toString() {
// its very important that the format of this returned string looks like "address:port/securePort"
return address + ":" + port + "/" + securePort;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof ServerEntry))
return false;
ServerEntry se = (ServerEntry) obj;
return (this.address.equals(se.address) && (this.port == se.port) && (this.securePort == se.securePort));
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + port;
result = prime * result + securePort;
result = prime * result + ((address == null) ? 0 : address.hashCode());
return result;
}
}
public FailoverListComposite(List<ServerEntry> servers) {
/*
* make a new list populated with the immutable elements of the passed list, this
* way if the passed list is later modified it doesn't indirectly modify this one;
* also, put this new list into an unmodifiable wrapper, as this is intended to be
* an immutable list, with read-only characteristics
*/
this.servers = Collections.unmodifiableList(new ArrayList<ServerEntry>(servers));
}
public int size() {
if (servers == null) {
return 0;
}
return servers.size();
}
public boolean hasNext() {
return (servers != null && servers.size() > 0);
}
public ServerEntry next() {
if (!hasNext()) {
return null;
}
ServerEntry nextOne = servers.get(nextIndex);
nextIndex++;
nextIndex %= servers.size();
return nextOne;
}
/**
* Same as {@link #next()} except this doesn't move the iterator
* pointer forward. Calling this method multiple times in a row
* results in the same server being returned, unlike {@link #next()}.
* If you call {@link #peek()} and then {@link #next()}, both will
* return the same server.
*
* @return the server entry that is next on the list to be returned (may be null)
*/
public ServerEntry peek() {
if (!hasNext()) {
return null;
}
ServerEntry nextOne = servers.get(nextIndex);
return nextOne;
}
public ServerEntry get(int index) {
return servers.get(index);
}
/**
* Call this method if you want the iterator to start over at the
* first, topmost, server in the failover list.
*/
public void resetIndex() {
nextIndex = 0;
}
public void remove() {
throw new UnsupportedOperationException(this.getClass().getName().substring(
this.getClass().getName().lastIndexOf(".") + 1)
+ " are immutable lists, removal is disallowed");
}
/**
* Used to "serialize" this list in a human-readable form (useful for storing the list
* in a text file that humans can read and potentially edit).
* @return the list in a human readable format
*/
public String writeAsText() {
StringBuilder text = new StringBuilder();
for (ServerEntry entry : servers) {
if (text.length() > 0) {
text.append("\n");
}
text.append(entry);
}
return text.toString();
}
/**
* Factory method that takes text generated from a previous instance's {@link #writeAsText()} string.
*
* @param text the failover list, in text form
*
* @return a new instance of FailoverListComposite whose servers are found in <code>text</code>
*/
public static FailoverListComposite readAsText(String text) {
List<ServerEntry> servers = new ArrayList<ServerEntry>();
String[] failoverListArray = text.split("\n");
for (String row : failoverListArray) {
try {
String[] addressPorts = row.split(":");
String[] ports = addressPorts[1].split("/");
servers.add(new ServerEntry(addressPorts[0], Integer.parseInt(ports[0]), Integer.parseInt(ports[1])));
} catch (Exception e) {
// why isn't this line valid? just ignore the bad line and continue on
}
}
return new FailoverListComposite(servers);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("\nServerList:\n ");
for (ServerEntry server : this.servers) {
sb.append(server.toString());
sb.append("\n ");
}
return sb.toString();
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof FailoverListComposite))
return false;
FailoverListComposite flc = (FailoverListComposite) obj;
return this.servers.equals(flc.servers);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((servers == null) ? 0 : servers.hashCode());
return result;
}
}