KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mmbase > cache > BetterStrategy


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.cache;
11
12 import java.util.Iterator JavaDoc;
13 import java.util.List JavaDoc;
14 import java.util.Set JavaDoc;
15
16 import org.mmbase.core.event.*;
17 import org.mmbase.module.core.*;
18 import org.mmbase.storage.search.*;
19 import org.mmbase.storage.search.implementation.database.BasicSqlHandler;
20 import org.mmbase.util.logging.Logger;
21 import org.mmbase.util.logging.Logging;
22
23
24 /**
25  * This release strategy is a bit better than 'BasicReleaseStrategy, and also a bit more sophisticated.
26  *
27  * @since MMBase 1.8
28  * @author Ernst Bunders
29  * @version $Id: BetterStrategy.java,v 1.24 2006/07/06 10:55:06 michiel Exp $
30  */

31 public class BetterStrategy extends ReleaseStrategy {
32
33     //public BetterStrategy() {}
34
private static final BasicSqlHandler sqlHandler = new BasicSqlHandler();
35     private static final Logger log = Logging.getLoggerInstance(BetterStrategy.class);
36
37     // inheritdoc
38
public String JavaDoc getName() {
39         return "Better Release Strategy";
40     }
41
42     /*
43      * (non-Javadoc)
44      *
45      * @see org.mmbase.cache.QueryResultCacheReleaseStrategy#getDescription()
46      */

47     public String JavaDoc getDescription() {
48         return "This strategy performs all kinds of checks to test if the node or relation event actually matches the query. " +
49             "For node events the type is checked, as well as some other things. For relation events the type is checked as well as " +
50             "the source and destination. Then there are some other things like: 'new node events should not flush queries with " +
51             "more than one step, because they have no relation yet'. It also checks if a certain change in a node actually can affect the " +
52             "outcome of a query.";
53     }
54
55     protected boolean doEvaluate(RelationEvent event, SearchQuery query, List JavaDoc cachedResult) {
56         return shouldRelease(event, query);
57     }
58
59     /**
60      * @see org.mmbase.cache.ReleaseStrategy#doEvaluate(org.mmbase.core.event.NodeEvent,
61
62      * org.mmbase.storage.search.SearchQuery, java.util.List)
63      *
64      * @return true if query should be released
65      */

66     protected final boolean doEvaluate(NodeEvent event, SearchQuery query, List JavaDoc cachedResult) {
67         if (log.isDebugEnabled()) {
68             log.debug(event.toString());
69         }
70         return shouldRelease(event, query);
71     }
72
73     /**
74      * Check all the rules that concern node events. if no rules match we return <code>true</code>.
75      * @param event
76      * @param query
77      * @return
78      */

79     private boolean shouldRelease(NodeEvent event, SearchQuery query) {
80         switch (event.getType()) {
81         case Event.TYPE_NEW:
82             // query has more than one step, all 'new node' events can be ignored, because this
83
// node has no relations yet.
84
if (query.getSteps().size() > 1) {
85                 logResult("no flush: 'new node' event in multistep query", query, event);
86                 return false; // don't release
87
}
88             if(! checkSteps(event, query)) {
89                 logResult("no flush: the query has nodes set and this event's node is not one of them, or this step has no steps of corresponding type", query, event);
90                 return false;
91             }
92             break;
93
94         case Event.TYPE_DELETE:
95             if(! checkSteps(event, query)) {
96                 logResult("no flush: the query has nodes set and this event's node is not one of them, or this step has no steps of corresponding type", query, event);
97                 return false;
98             }
99             break;
100
101         case Event.TYPE_CHANGE:
102             if(! checkSteps(event, query)) {
103                 logResult("no flush: the query has nodes set and this event's node is not one of them, or this step has no steps of corresponding type", query, event);
104                 return false;
105             }
106             //if the changed field(s) do not occur in the fields or constraint section
107
//of the query, it does not have to be flushed
108
if(! checkChangedFieldsMatch(event, query)) {
109                 logResult("no flush: the fields that have changed are not used in the query", query, event);
110                 return false;
111             }
112
113             //if the query is aggregating, and of type count, and the changed fields(s) do
114
//not occur in the constraint: don't flush the query
115
if(checkAggregationCount(event, query)) {
116                 logResult("query is aggregating and fields are of type count, changed fields do not affect the query result", query, event);
117                 return false;
118             }
119
120
121         }
122         logResult("flush: no reason not to", query, event);
123         return true;
124     }
125
126     /**
127      * check all the rules that concern relation events. if no rules match we return
128      * <code>true</code>.
129      * @param event
130      * @param query
131      * @return
132      */

133     private boolean shouldRelease(RelationEvent event, SearchQuery query) {
134
135         /*
136          * Here are all the preconditions that must be met to proceed. Basic checks to determin
137          * if this event has to be evaluated on this query at all
138          */

139
140          //query has one step and the event is a relation event
141
if (query.getSteps().size() == 1 ){
142              logResult("no flush: query has one step and event is relation event", query, event);
143              return false ;//don't release
144
}
145
146          // if a query has more steps that one and the event is a relation event
147
// we check if the role of the relation is allso in the query.
148
if (! checkPathMatches(event, query)){
149              logResult("no flush: either source, destination or role does not match to the query", query, event);
150              return false;
151          }
152
153
154          switch (event.getType()) {
155          case Event.TYPE_NEW:
156              log.debug(">> relation event type new");
157              /*
158               * Put all rules here that apply to new relation events
159               */

160
161              break;
162
163          case Event.TYPE_DELETE:
164              log.debug(">> relation event type delete");
165              /*
166               * Put all rules here that apply to removed relation events
167               */

168
169              break;
170
171          case Event.TYPE_CHANGE:
172              log.debug(">> relation event type changed");
173              /*
174               * Put all rules here that apply to changed relation events
175               */

176
177              //if the changed field(s) do not occur in the fields or constraint section
178
//of the query, it does not have to be flushed
179
if(! checkChangedFieldsMatch(event.getNodeEvent(), query)) {
180                  logResult("no flush: the changed relation fields do not match the fields or constraints of the query", query, event);
181                  return false;
182              }
183
184              break;
185
186          }
187          logResult("flush: no reason not to", query, event);
188          return true;
189     }
190
191     /**
192      * @param event
193      * @param query
194      * @return true if query is aggragating, of type count, and the changed fields do
195      * not occur in the constraint (no flush)
196      */

197     private boolean checkAggregationCount(NodeEvent event, SearchQuery query) {
198         log.debug("method: checkAggregationCount()");
199         if(!query.isAggregating()){
200             return false;
201         }
202         //test if all changed fields are aggreagting and of type count, if not: return false;
203
for(Iterator JavaDoc i = query.getFields().iterator(); i.hasNext(); ){
204             StepField field = (StepField) i.next();
205             if(event.getChangedFields().contains(field.getFieldName()) ){
206                 if( ! (field instanceof AggregatedField)) {
207                     return false;
208                 }
209                 if( ! (((AggregatedField)field).getAggregationType() == AggregatedField.AGGREGATION_TYPE_COUNT) ){
210                     return false;
211                 }
212             }
213         }
214         //now check the constraints: if there are any constraints for any of the changed fields: false;
215
Constraint constraint = query.getConstraint();
216         if(constraint == null){
217             return true;
218         }
219         MMObjectBuilder eventBuilder = MMBase.getMMBase().getBuilder(event.getBuilderName());
220         for (Iterator JavaDoc i = event.getChangedFields().iterator(); i.hasNext();) {
221             String JavaDoc fieldName = (String JavaDoc) i.next();
222             if(getConstraintsForField(fieldName, eventBuilder, constraint, query).size() > 0){
223                 return false;
224             }
225         }
226         //all tests survived, query should not be flushed
227
return true;
228     }
229
230     /**
231      * @param event
232      * @param query
233      * @return true if sourcetype, role and destination from relation event match query
234      */

235     private boolean checkPathMatches(RelationEvent event, SearchQuery query){
236         // check if the path in the query maches the relation event:
237
// - the source and destination objects should be there
238
// - the role either matches or is not specified
239
if (log.isDebugEnabled()) {
240             log.debug("method: checkPathMatches()");
241             log.debug(event.toString());
242             log.debug("query: " + query.toString());
243         }
244         MMBase mmb = MMBase.getMMBase();
245         String JavaDoc eventSourceType = event.getRelationSourceType();
246         String JavaDoc eventDestType = event.getRelationDestinationType();
247         MMObjectBuilder eventSource = mmb.getBuilder(eventSourceType);
248         MMObjectBuilder eventDest = mmb.getBuilder(eventDestType);
249
250
251         Iterator JavaDoc i = query.getSteps().iterator();
252         Step prevStep = (Step) i.next();
253         String JavaDoc stepDest = prevStep.getTableName();
254         while (i.hasNext()) {
255             String JavaDoc stepSource = stepDest;
256             RelationStep step = (RelationStep) i.next();
257             Step nextStep = (Step) i.next();
258             stepDest = nextStep.getTableName();
259             boolean matchesProper =
260                 (eventSourceType.equals(stepSource) || eventSource.isExtensionOf(mmb.getBuilder(stepSource))) &&
261                 (eventDestType.equals(stepDest) || eventDest.isExtensionOf(mmb.getBuilder(stepDest)));
262             boolean matches = matchesProper ||
263                 ( // matchesInverse
264
(eventDestType.equals(stepSource) || eventDest.isExtensionOf(mmb.getBuilder(stepSource))) &&
265                  (eventSourceType.equals(stepDest) || eventSource.isExtensionOf(mmb.getBuilder(stepDest)))
266                  );
267
268             Integer JavaDoc role = step.getRole();
269             if (matches &&
270                 (role == null || role.intValue() == event.getRole())) {
271                 return true;
272             }
273         }
274         return false;
275     }
276
277
278
279
280
281
282     /**
283      * Checks if a query object contains reference to (one of) the changed field(s).
284      * Matches are looked for in the stepfields and in the constraints.
285      * @param event
286      * @param query
287      * @return true if the type of the node for this event matches either a stepfield or a constriant
288      */

289     private boolean checkChangedFieldsMatch(NodeEvent event, SearchQuery query){
290         if (log.isDebugEnabled()) {
291             log.debug("method: checkChangedFieldsMatch(). changed fields: " + event.getChangedFields().size());
292         }
293         boolean constraintsFound = false;
294         boolean fieldsFound = false;
295         boolean sortordersFound = false;
296         String JavaDoc eventBuilderName = event.getBuilderName();
297         MMBase mmb = MMBase.getMMBase();
298         MMObjectBuilder eventBuilder = mmb.getBuilder(eventBuilderName);
299         search:
300         for (Iterator JavaDoc i = event.getChangedFields().iterator(); i.hasNext();) {
301             String JavaDoc fieldName = (String JavaDoc) i.next();
302
303             //first test the constraints
304
List JavaDoc constraintsForFieldList = getConstraintsForField(fieldName, eventBuilder, query.getConstraint(), query);
305             if(constraintsForFieldList.size() > 0) {
306                 constraintsFound = true;
307                 if (log.isDebugEnabled()) {
308                     log.debug("matching constraint found: " + constraintsForFieldList.size());
309                 }
310                 break search;
311             }
312
313             // then test the fields (only if no constraint match was found)
314
for (Iterator JavaDoc fieldIterator = query.getFields().iterator(); fieldIterator.hasNext();) {
315                 StepField field = (StepField) fieldIterator.next();
316                 if (field.getFieldName().equals(fieldName)
317                     && (field.getStep().getTableName().equals(eventBuilderName) ||
318                         eventBuilder.isExtensionOf(mmb.getBuilder(field.getStep().getTableName())))
319                         ) {
320                     fieldsFound = true;
321                     if(log.isDebugEnabled()) {
322                         log.debug("matching field found: " + field.getStep().getTableName() + "." + field.getFieldName());
323                     }
324                     break search;
325                 }
326             }
327
328             //test the sortorders
329
List JavaDoc sortordersForFieldList = getSortordersForField(fieldName, eventBuilder, query.getSortOrders(), query);
330             if(sortordersForFieldList.size() > 0) {
331                 sortordersFound = true;
332                 if (log.isDebugEnabled()) {
333                     log.debug("matching sortorders found: " + sortordersForFieldList.size());
334                 }
335                 break search;
336             }
337         }
338         if(log.isDebugEnabled()){
339             String JavaDoc logMsg ="";
340             if (!sortordersFound) logMsg = logMsg + " no matching sortorders found";
341             if (!fieldsFound) logMsg = "no matching fields found, ";
342             if (!constraintsFound) logMsg = logMsg + " no matching constraints found";
343             log.debug(logMsg);
344         }
345         //now test the result
346
return sortordersFound || fieldsFound || constraintsFound;
347     }
348
349     /**
350      * This method investigates all the steps of a query that correspond to the nodetype of the
351      * node event. for each step a check is made if this step has 'nodes' set, and so, if the changed
352      * node is one of them.
353      *
354      * Also it checks if the step is of a corresponding type. It returns also false if no step
355      * matched the type of the node event.
356      * @param event a NodeEvent
357      * @param query
358      * @return true if (all) the step(s) matching this event have nodes set, and non of these
359      * match the number of the changed node (in which case the query should not be flused)
360      */

361     private boolean checkSteps(NodeEvent event, SearchQuery query){
362         //this simple optimization only works for nodeEvents
363
MMBase mmb = MMBase.getMMBase();
364         String JavaDoc eventTable = event.getBuilderName();
365         MMObjectBuilder eventBuilder = mmb.getBuilder(eventTable);
366         Iterator JavaDoc i = query.getSteps().iterator();
367         while (i.hasNext()) {
368             Step step = (Step) i.next();
369             String JavaDoc table = step.getTableName();
370             if (! (table.equals(eventTable) ||
371                    eventBuilder.isExtensionOf(mmb.getBuilder(table)))) continue;
372             Set JavaDoc nodes = step.getNodes();
373             if (nodes == null || nodes.size() == 0 || nodes.contains(new Integer JavaDoc(event.getNodeNumber()))) {
374                 return true;
375             }
376         }
377         return false;
378     }
379
380     private void logResult(String JavaDoc comment, SearchQuery query, Event event){
381         if(log.isDebugEnabled()){
382             String JavaDoc role="";
383             // a small hack to limit the output
384
if (event instanceof RelationEvent) {
385                 //get the role name
386
RelationEvent revent = (RelationEvent) event;
387                 MMObjectNode relDef = MMBase.getMMBase().getBuilder("reldef").getNode(revent.getRole());
388                 role = " role: " + relDef.getStringValue("sname") + "/" + relDef.getStringValue("dname");
389                 //filter the 'object' events
390
if (revent.getRelationSourceType().equals("object")
391                         || revent.getRelationDestinationType().equals("object"))
392                     return;
393             }
394             try {
395                 log.debug("\n******** \n**" + comment + "\n**" + event.toString() + role + "\n**" + sqlHandler.toSql(query, sqlHandler) + "\n******");
396             } catch (SearchQueryException e) {
397                 log.warn(e);
398             }
399         }
400     }
401 }
402
Popular Tags