001: /**
002: * Sequoia: Database clustering technology.
003: * Copyright (C) 2005 Emic Networks.
004: * Contact: sequoia@continuent.org
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * 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, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: *
018: * Initial developer(s): Emmanuel Cecchet.
019: * Contributor(s): ______________________.
020: */package org.continuent.sequoia.driver.connectpolicy;
021:
022: import java.sql.SQLException;
023: import java.util.ArrayList;
024: import java.util.StringTokenizer;
025:
026: import org.continuent.sequoia.common.exceptions.NoMoreControllerException;
027: import org.continuent.sequoia.driver.ControllerInfo;
028: import org.continuent.sequoia.driver.SequoiaUrl;
029: import org.continuent.sequoia.driver.connectpolicy.AbstractControllerConnectPolicy.ControllerAndVdbState;
030:
031: /**
032: * This class defines a PreferredListConnectPolicy
033: *
034: * @author <a href="mailto:emmanuel.cecchet@emicnetworks.com">Emmanuel Cecchet
035: * </a>
036: * @version 1.0
037: */
038: public class PreferredListConnectPolicy extends
039: AbstractControllerConnectPolicy {
040: private int index = -1;
041: private int preferredIndex = -1;
042: private ArrayList preferredControllers;
043: private ArrayList originalPreferredControllers;
044:
045: /**
046: * Creates a new <code>PreferredListConnectPolicy</code> object
047: *
048: * @param controllerList the controller list on which the policy applies
049: * @param preferredControllerList comma separated list of preferred
050: * controllers
051: * @param pingDelayInMs Interval in milliseconds between two pings of a
052: * controller
053: * @param controllerTimeoutInMs timeout in milliseconds after which a
054: * controller is considered as dead if it did not respond to pings
055: * @param debugLevel the debug level to use
056: * @see org.continuent.sequoia.driver.SequoiaUrl#DEBUG_LEVEL_OFF
057: */
058: public PreferredListConnectPolicy(ControllerInfo[] controllerList,
059: String preferredControllerList, int pingDelayInMs,
060: int controllerTimeoutInMs, int debugLevel) {
061: super (controllerList, pingDelayInMs, controllerTimeoutInMs,
062: debugLevel);
063:
064: // Check the validity of each controller in the list
065: StringTokenizer controllers = new StringTokenizer(
066: preferredControllerList, ",", true);
067: int tokenNumber = controllers.countTokens();
068: preferredControllers = new ArrayList(tokenNumber - 1);
069: int i = 0;
070: String s;
071: boolean lastTokenWasComma = false;
072: while (controllers.hasMoreTokens()) {
073: s = controllers.nextToken().trim();
074: if (s.equals(",")) {
075: if (lastTokenWasComma || (i == 0)
076: || (i == tokenNumber - 1))
077: // ',' cannot be the first or the last token
078: // another ',' cannot follow a ','
079: throw new RuntimeException(
080: "Syntax error in controller list for preferredController attribute '"
081: + preferredControllerList + "'");
082: else {
083: lastTokenWasComma = true;
084: continue;
085: }
086: }
087: lastTokenWasComma = false;
088: try {
089: // ensure that the preferred controller is in the list of all
090: // controllers
091: ControllerInfo pref = SequoiaUrl.parseController(s);
092: boolean found = false;
093: for (int idx = 0; idx < controllerList.length; idx++)
094: if (controllerList[idx].equals(pref)) {
095: found = true;
096: break;
097: }
098: if (!found) {
099: throw new RuntimeException("Preferred controller "
100: + pref
101: + " is not in the list of controllers ("
102: + aliveControllers + ")");
103: }
104: preferredControllers.add(pref);
105: } catch (SQLException e) {
106: throw new RuntimeException(
107: "Invalid controller "
108: + s
109: + " in controller list for preferredController attribute");
110: }
111: i++;
112: }
113: // make a copy of the preferred controllers to be able to find them back
114: // (see controllerUp())
115: originalPreferredControllers = new ArrayList(
116: preferredControllers);
117: }
118:
119: /**
120: * @see org.continuent.sequoia.driver.connectpolicy.AbstractControllerConnectPolicy#controllerUp(org.continuent.sequoia.driver.ControllerInfo)
121: */
122: public synchronized void controllerUp(ControllerInfo controller) {
123: super .controllerUp(controller);
124: // re integrate controller in preferred list if it was in the original list
125: if (originalPreferredControllers.contains(controller)) {
126: // determine where to put the controller in the preferred list
127: int prefIndex = originalPreferredControllers
128: .indexOf(controller);
129: if (prefIndex > preferredControllers.size())
130: prefIndex = preferredControllers.size() - 1;
131: preferredControllers.add(prefIndex, controller);
132: }
133: }
134:
135: /**
136: * @see org.continuent.sequoia.driver.connectpolicy.AbstractControllerConnectPolicy#controllerDown(org.continuent.sequoia.driver.ControllerInfo)
137: */
138: public synchronized void controllerDown(ControllerInfo controller) {
139: super .controllerDown(controller);
140: while (preferredControllers.remove(controller))
141: ;
142: }
143:
144: /**
145: * Gets the controllers as follow:
146: * <ul>
147: * <li> if there are preferred controllers left, get one of them in round
148: * robin mode
149: * <li> else, get one of the other controllers in round robin mode
150: * </ul>
151: */
152: public synchronized ControllerInfo getController()
153: throws NoMoreControllerException {
154: // preferredController is a subset of aliveControllers => no double check
155: if (aliveControllers.isEmpty())
156: throw new NoMoreControllerException();
157:
158: ControllerInfo selectedController = null;
159: int nbPreferredTested = 0;
160: // first, try in the preferred controllers list
161: while (selectedController == null
162: && !preferredControllers.isEmpty()
163: && nbPreferredTested < preferredControllers.size()) {
164: preferredIndex++;
165: if (preferredIndex >= preferredControllers.size())
166: preferredIndex = 0;
167: selectedController = (ControllerInfo) preferredControllers
168: .get(preferredIndex);
169: // check that the selected controller can be used (ie. its vdb is up)
170: if (selectedController != null
171: && !super .isVdbUpOnController(selectedController)) {
172: selectedController = null;
173: nbPreferredTested++;
174: }
175: }
176: if (selectedController == null) {
177: // No preferred => find 1st available controller in round-robin mode.
178: // No problem re-using the index because it is "modded" (%) with the list
179: // size before getting it
180: index++;
181: if (index >= aliveControllers.size())
182: index = 0;
183: selectedController = super .getControllerByNum(index);
184: }
185: if (debugLevel == SequoiaUrl.DEBUG_LEVEL_DEBUG)
186: System.out.println("Selected controller: "
187: + selectedController);
188: return selectedController;
189: }
190: }
|