KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mmbase > module > core > ClusterNode


1 /*
2
3 This software is OSI Certified Open Source Software.
4 OSI Certified is a certification mark of the Open Source Initiative.
5
6 The license (Mozilla version 1.0) can be read at the MMBase site.
7 See http://www.MMBase.org/license
8
9 */

10 package org.mmbase.module.core;
11
12 import java.util.*;
13
14 import org.mmbase.bridge.Field;
15 import org.mmbase.util.logging.*;
16 import org.mmbase.util.Casting;
17
18
19 /**
20  * ClusterNode combines fields of different nodes in a single "virtual" node.
21  * This corresponds to the way that an SQL "join" select statement combines
22  * fields of different tables in result rows.
23  * <p>
24  * The individual fields are retrieved from a set of related nodes using a
25  * multilevel query, i.e. a query joining tables using the relations between
26  * the tables.
27  * <p>
28  * This class overrides a number of methods, allowing direct access to data in
29  * the nodes which form the 'virtual' node.
30  * <br />
31  * In future releases, data will NOT be stored directkly in this node anymore.
32  * Instead, it will be stored in the underlying MMObjectNodes.
33  * For reasons of optiomalization, however, we cannot do this right now.
34  * MMObjectNode will need a status field that allows us to recognize whether
35  * it is fully loaded, partially loaded, or being edited.
36  * This can then be checked in 'retrievevalue'.
37  * In addition, to prevent caching conflicts, nodes will need to maintain
38  * their references. This allows for a secure caching mechanism.
39  * <br />
40  * Among other things, this allows one to change values in a multilevel node,
41  * or to access functionality that would otherwise be restricted to 'real'
42  * nodes.
43  *
44  * @author Pierre van Rooden
45  * @version $Id: ClusterNode.java,v 1.27 2006/04/10 15:10:36 daniel Exp $
46  * @see ClusterBuilder
47  */

48 public class ClusterNode extends VirtualNode {
49
50     private static final Logger log = Logging.getLoggerInstance(ClusterNode.class);
51
52     /**
53      * Main contructor.
54      * @param parent the node's parent
55      */

56     public ClusterNode(ClusterBuilder parent) {
57         super(parent);
58     }
59
60     /**
61      * Main contructor.
62      * @param parent the node's parent
63      * @param nrofnodes Nr of referenced nodes.
64      */

65     public ClusterNode(ClusterBuilder parent, int nrofnodes) {
66         super(parent);
67     }
68
69     /**
70      * Tests whether the data in a node is valid (throws an exception if this is not the case).
71      * The call is performed on all loaded 'real' nodes. If a 'real' node has not previously been
72      * forcably loaded, it is assumed to be correct.
73      * @throws org.mmbase.module.core.InvalidDataException
74      * If the data was unrecoverably invalid
75      * (the references did not point to existing objects)
76      */

77     public void testValidData() throws InvalidDataException { // why is it public?
78
throw new UnsupportedOperationException JavaDoc("ClusterNode " + this.getClass().getName() + " removed since 1.8");
79     };
80
81     /**
82       * commit : commits the node to the database or other storage system.
83       * This can only be done on a existing (inserted) node. it will use the
84       * changed Vector as its base of what to commit/changed
85       * @return <code>true</code> if the commit was succesfull, <code>false</code> is it failed
86       */

87     public boolean commit() {
88     throw new UnsupportedOperationException JavaDoc("ClusterNode " + this.getClass().getName() + " removed since 1.8");
89     }
90
91     /**
92      * Obtain the 'real' nodes, associated with a specified objectbuilder.
93      * @param builderName the name of the builder of the requested node, as known
94      * within the virtual node
95      * @return the node, or <code>null</code> if it does not exist or is unknown
96      */

97     public MMObjectNode getRealNode(String JavaDoc builderName) {
98         if (builderName == null) return null;
99         Integer JavaDoc number = (Integer JavaDoc) retrieveValue(builderName + ".number");
100         if (number != null) {
101             return parent.getNode(number.intValue());
102         }
103         return null;
104     }
105
106     /**
107      * Stores a value in the values hashtable.
108      * If the value is not stored in the virtualnode,
109      * the 'real' node is used instead.
110      * @param fieldName the name of the field to change
111      * @param fieldValue the value to assign
112      */

113     public void storeValue(String JavaDoc fieldName, Object JavaDoc fieldValue) {
114             super.storeValue(fieldName, fieldValue);
115     }
116
117     /**
118      * Sets a key/value pair in the main values of this node.
119      * Note that if this node is a node in cache, the changes are immediately visible to
120      * everyone, even if the changes are not committed.
121      * The fieldname is added to the (public) 'changed' vector to track changes.
122      * @param fieldName the name of the field to change
123      * @param fieldValue the value to assign
124      * @return always <code>true</code>
125      */

126     public boolean setValue(String JavaDoc fieldName, Object JavaDoc fieldValue) {
127         // Circument interference by the database during initial loading of the node
128
// This is not pretty, but the alternative is rewriting all support classes...
129
if (initializing) {
130             if (! (parent instanceof ClusterBuilder)) {
131                 values.put(ClusterBuilder.getFieldNameFromField(fieldName), fieldValue);
132             } else {
133                 values.put(fieldName, fieldValue);
134             }
135             return true;
136         }
137         String JavaDoc builderName = getBuilderName(fieldName);
138
139         MMObjectNode n = getRealNode(builderName);
140         if (n != null) {
141             String JavaDoc realFieldName = ClusterBuilder.getFieldNameFromField(fieldName);
142             n.setValue(realFieldName, fieldValue);
143             values.remove(fieldName);
144             return true;
145         }
146         log.warn("Could not set field '" + fieldName + "')");
147         return false; // or throw exception?
148
}
149
150     /**
151      * Determines the builder name of a specified fieldname, i.e.
152      * "news" in "news.title",
153      * @param fieldName the name of the field
154      * @return the buidler name of the field
155      */

156     protected String JavaDoc getBuilderName(String JavaDoc fieldName) {
157         int pos = fieldName.indexOf(".");
158         if (pos == -1) {
159             return null;
160         } else {
161             String JavaDoc builderName = fieldName.substring(0, pos);
162             int pos2 = builderName.lastIndexOf("(");
163             builderName = builderName.substring(pos2 + 1);
164             // XXX: we should check on commas and semicolons too... ?
165
return builderName;
166         }
167     }
168
169     // MM: special arrangment for if parent is not ClusterBuilder.
170
// could give NPE so this is a fix... (1.7)
171
public MMObjectBuilder getBuilder() {
172         if (parent instanceof ClusterBuilder) {
173             return super.getBuilder();
174         } else {
175             return parent;
176         }
177
178     }
179
180     /**
181      * Get a value of a certain field.
182      * @param fieldName the name of the field who's data to return
183      * @return the field's value as an <code>Object</code>
184      */

185     public Object JavaDoc getValue(String JavaDoc fieldName) {
186         String JavaDoc builder = getBuilderName(fieldName);
187         if (builder == null) {
188             // there is no 'builder' specified,
189
// so the fieldname itself is a builder name
190
// -> so return the MMObjectNode for that buidler
191
if (parent instanceof ClusterBuilder) {
192                 return getRealNode(fieldName);
193             }
194
195         }
196         Object JavaDoc o = super.getValue(fieldName);
197         if (o == null) {
198             // the normal approach does not yield results.
199
// get the value from the original builder
200
String JavaDoc builderName = getBuilderName(fieldName);
201             MMObjectNode n = getRealNode(builderName);
202             if (n!=null) {
203                 o = n.getValue(ClusterBuilder.getFieldNameFromField(fieldName));
204             } else {
205                 // fall back to builder if this node doesn't contain a number to fetch te original
206
MMObjectBuilder bul = parent.mmb.getMMObject(builderName);
207                 if (bul != null) {
208                     o = bul.getValue(this, fieldName);
209                 } else {
210                     throw new RuntimeException JavaDoc("Builder with name '" + builderName + "' does not exist");
211                 }
212             }
213         }
214         return o;
215     }
216
217
218     /**
219      * Get a value of a certain field.
220      * The value is returned as a String. Non-string values are automatically converted to String.
221      * @param fieldName the name of the field who's data to return
222      * @return the field's value as a <code>String</code>
223      */

224     public String JavaDoc getStringValue(String JavaDoc fieldName) {
225
226         // try to get the value from the values table
227
String JavaDoc tmp = Casting.toString(getValue(fieldName));
228
229         // check if the object is shorted
230
if (tmp.equals(MMObjectNode.VALUE_SHORTED)) {
231             log.debug("getStringValue(): node=" + this + " -- fieldName " + fieldName);
232             // obtain the database type so we can check if what
233
// kind of object it is. this have be changed for
234
// multiple database support.
235
int type = getDBType(fieldName);
236
237             log.debug("getStringValue(): fieldName " + fieldName + " has type " + type);
238             // check if for known mapped types
239
if (type == Field.TYPE_STRING) {
240
241                 // determine actual node number for this field
242
// takes into account when in a multilevel node
243
int number = getIntValue(getBuilderName(fieldName) + ".number");
244                 tmp = parent.getShortedText(fieldName, parent.getNode(number));
245
246                 // did we get a result then store it in the values for next use
247
if (tmp != null) {
248                     // store the unmapped value (replacing the $SHORTED text)
249
storeValue(fieldName, tmp);
250                 }
251             }
252         }
253         // return the found value
254
return tmp;
255     }
256
257
258     /**
259      * Get a binary value of a certain field.
260      * @param fieldName the name of the field who's data to return
261      * @return the field's value as an <code>byte []</code> (binary/blob field)
262      */

263     public byte[] getByteValue(String JavaDoc fieldName) {
264         // try to get the value from the values table
265
Object JavaDoc obj = getValue(fieldName);
266
267         // we signal with a empty byte[] that its not obtained yet.
268
if (obj instanceof byte[]) {
269             // was allready unmapped so return the value
270
return (byte[])obj;
271         } else {
272             // determine actual node number for this field
273
// takes into account when in a multilevel node
274
int number = getIntValue(getBuilderName(fieldName) + ".number");
275             // call our builder with the convert request this will probably
276
// map it to the database we are running.
277
byte[] b = parent.getShortedByte(fieldName, parent.getNode(number));
278
279             // we could in the future also leave it unmapped in the values
280
// or make this programmable per builder ?
281
storeValue(fieldName, b);
282             // return the unmapped value
283
return b;
284         }
285     }
286
287     /**
288      * Tests whether one of the values of this node was changed since the last commit/insert.
289      * @return <code>true</code> if changes have been made, <code>false</code> otherwise
290      */

291     public boolean isChanged() {
292     throw new UnsupportedOperationException JavaDoc("ClusterNode " + this.getClass().getName() + " removed since 1.8");
293     }
294
295     /**
296      * Return the relations of this node.
297      * This is not allowed on a cluster node
298      * @throws <code>RuntimeException</code>
299      */

300     public Enumeration getRelations() {
301         throw new RuntimeException JavaDoc("Cannot follow relations on a cluster node. ");
302     }
303
304 }
305
Popular Tags