1 /***
2 *
3 * Copyright 2004 Protique Ltd
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 **/
18 package org.activecluster.group;
19
20 import org.activecluster.Node;
21
22 import java.util.ArrayList;
23 import java.util.HashMap;
24 import java.util.Iterator;
25 import java.util.LinkedList;
26 import java.util.List;
27 import java.util.Map;
28
29 /***
30 * Represents a collection of zero or more groups in a cluster.
31 * The default implementation will create groups as nodes are added to the cluster; filling
32 * the groups with its required number of buddies / slaves until a new group can be created.
33 * <p/>
34 * Nodes which are not allowed to be master nodes will be kept around in a pool ready to be added
35 * as slaves when a new master arrives and forces the creation of a group.
36 *
37 * @version $Revision: 1.1 $
38 * @see Group
39 */
40 public class GroupModel {
41 private int maximumGroups = -1;
42 private int minimumMemberCount = 2;
43 private int maximumMemberCount = 3;
44 private List groups = new ArrayList();
45 private LinkedList incompleteGroups = new LinkedList();
46 private LinkedList completeGroups = new LinkedList();
47 private LinkedList fullGroups = new LinkedList();
48 private LinkedList unusedNodes = new LinkedList();
49 private NodeFilter masterFilter;
50 private Map nodeMemberships = new HashMap();
51
52
53 private int maximumWeighting = 10;
54
55 /***
56 * Adds the new node to this group model; we assume the node has not been added before.
57 *
58 * @param node
59 */
60 public synchronized void addNode(Node node) {
61 if (!addToExistingGroup(node)) {
62 Group group = makeNewGroup(node);
63 if (group == null) {
64 addToUnusedNodes(node);
65 }
66 else {
67 addGroup(group);
68 }
69 }
70 }
71
72 /***
73 * Removes the node from the group model
74 *
75 * @param node
76 */
77 public synchronized void removeNode(Node node) {
78 unusedNodes.remove(node);
79
80
81 for (Iterator iter = groups.iterator(); iter.hasNext();) {
82 Group group = (Group) iter.next();
83 boolean wasFull = group.isFull();
84 boolean wasUsable = group.isUsable();
85
86 if (removeNodeFromGroup(group, node)) {
87 updateGroupCollections(group, wasFull, wasUsable);
88 }
89 }
90 }
91
92
93
94
95 /***
96 * Returns a snapshot of the groups currently available
97 */
98 public synchronized List getGroups() {
99 return new ArrayList(groups);
100 }
101
102 public NodeFilter getMasterFilter() {
103 return masterFilter;
104 }
105
106 public void setMasterFilter(NodeFilter masterFilter) {
107 this.masterFilter = masterFilter;
108 }
109
110 public int getMaximumGroups() {
111 return maximumGroups;
112 }
113
114 public void setMaximumGroups(int maximumGroups) {
115 this.maximumGroups = maximumGroups;
116 }
117
118 public int getMaximumMemberCount() {
119 return maximumMemberCount;
120 }
121
122 public void setMaximumMemberCount(int maximumMemberCount) {
123 this.maximumMemberCount = maximumMemberCount;
124 }
125
126 public int getMinimumMemberCount() {
127 return minimumMemberCount;
128 }
129
130 public void setMinimumMemberCount(int minimumMemberCount) {
131 this.minimumMemberCount = minimumMemberCount;
132 }
133
134 public int getMaximumWeighting() {
135 return maximumWeighting;
136 }
137
138 public void setMaximumWeighting(int maximumWeighting) {
139 this.maximumWeighting = maximumWeighting;
140 }
141
142
143
144
145
146
147 /***
148 * Attempt to make a new group with the current node as the master
149 * or if the node cannot be a master node
150 *
151 * @return the newly created group or false if none was created.
152 */
153 protected Group makeNewGroup(Node node) {
154
155 if (canCreateGroup(node)) {
156 Group group = createGroup(node);
157 addNodeToGroup(group, node);
158 return group;
159 }
160 else {
161 return null;
162 }
163 }
164
165 protected void tryToFillGroupWithBuddies(Group group) {
166 boolean continueFillingGroups = true;
167 while (!group.isUsable() && continueFillingGroups) {
168 continueFillingGroups = tryToAddBuddy(group);
169 }
170
171 if (continueFillingGroups) {
172
173 for (Iterator iter = new ArrayList(incompleteGroups).iterator(); iter.hasNext() && continueFillingGroups;) {
174 group = (Group) iter.next();
175
176 boolean wasFull = group.isFull();
177 boolean wasUsable = group.isUsable();
178
179 while (!group.isUsable() && continueFillingGroups) {
180 continueFillingGroups = tryToAddBuddy(group);
181 }
182
183 if (group.isUsable()) {
184 updateGroupCollections(group, wasFull, wasUsable);
185 }
186 }
187 }
188 }
189
190 protected boolean tryToAddBuddy(Group group) {
191 boolean continueFillingGroups = true;
192
193 NodeMemberships lowest = null;
194 int lowestWeight = 0;
195 for (Iterator iter = nodeMemberships.values().iterator(); iter.hasNext();) {
196 NodeMemberships memberships = (NodeMemberships) iter.next();
197 if (!memberships.isMember(group)) {
198 int weighting = memberships.getWeighting();
199 if ((lowest == null || weighting < lowestWeight) && weighting < maximumWeighting) {
200 lowest = memberships;
201 lowestWeight = weighting;
202 }
203 }
204 }
205 if (lowest == null) {
206 continueFillingGroups = false;
207 }
208 else {
209 addNodeToGroup(group, lowest.getNode());
210 }
211 return continueFillingGroups;
212 }
213
214 /***
215 * Lets move the group from its current state collection to the new collection if its
216 * state has changed
217 */
218 protected void updateGroupCollections(Group group, boolean wasFull, boolean wasUsable) {
219 boolean full = group.isFull();
220 if (wasFull && !full) {
221 fullGroups.remove(group);
222 }
223 boolean usable = group.isUsable();
224 if (wasUsable && !usable) {
225 completeGroups.remove(group);
226 }
227 if ((!usable || !full) && (wasFull || wasUsable)) {
228 incompleteGroups.add(group);
229 }
230 }
231
232 protected void addToUnusedNodes(Node node) {
233
234 unusedNodes.add(node);
235 }
236
237 /***
238 * Attempts to add the node to an incomplete group, or
239 * a not-full group and returns true if its possible - else returns false
240 *
241 * @return true if the node has been added to a groupu
242 */
243 protected boolean addToExistingGroup(Node node) {
244 if (!addToIncompleteGroup(node)) {
245 if (!addToNotFullGroup(node)) {
246 return false;
247 }
248 }
249 return true;
250 }
251
252 protected boolean addToNotFullGroup(Node node) {
253 return addToPendingGroup(completeGroups, node);
254 }
255
256 protected boolean addToIncompleteGroup(Node node) {
257 return addToPendingGroup(incompleteGroups, node);
258 }
259
260 /***
261 * Adds the given node to the first pending group if possible
262 *
263 * @return true if the node was added to the first available group
264 */
265 protected boolean addToPendingGroup(LinkedList list, Node node) {
266 if (!list.isEmpty()) {
267 Group group = (Group) list.getFirst();
268 addNodeToGroup(group, node);
269 if (group.isFull()) {
270 list.removeFirst();
271 fullGroups.add(group);
272 }
273 else if (group.isUsable()) {
274 list.removeFirst();
275 completeGroups.add(group);
276 }
277 return true;
278 }
279 return false;
280 }
281
282 protected void addNodeToGroup(Group group, Node node) {
283 NodeMemberships memberships = (NodeMemberships) nodeMemberships.get(node);
284 if (memberships == null) {
285 memberships = new NodeMemberships(node);
286 nodeMemberships.put(node, memberships);
287 }
288 memberships.addToGroup(group);
289 }
290
291 protected boolean removeNodeFromGroup(Group group, Node node) {
292 NodeMemberships memberships = (NodeMemberships) nodeMemberships.get(node);
293 if (memberships != null) {
294 return memberships.removeFromGroup(group);
295 }
296 return false;
297 }
298
299
300 protected void addGroup(Group group) {
301 groups.add(group);
302 if (group.isFull()) {
303 fullGroups.add(group);
304 }
305 else if (group.isUsable()) {
306 completeGroups.add(group);
307 }
308 else {
309 incompleteGroups.add(group);
310 }
311 }
312
313 protected Group createGroup(Node node) {
314 return new Group(minimumMemberCount, maximumMemberCount);
315 }
316
317 /***
318 * Returns true if we can add a new group to the cluster
319 */
320 protected boolean canCreateGroup(Node node) {
321 return (maximumGroups < 0 || groups.size() < maximumGroups) && canBeMaster(node);
322 }
323
324 /***
325 * Returns true if the given node can be a master
326 */
327 protected boolean canBeMaster(Node node) {
328 return masterFilter == null || masterFilter.evaluate(node);
329 }
330 }