KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mmbase > module > builders > DayMarkers


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.builders;
11
12 import java.util.*;
13 import java.text.DateFormat JavaDoc;
14 import org.mmbase.module.core.*;
15 import org.mmbase.storage.search.implementation.*;
16 import org.mmbase.storage.search.*;
17 import org.mmbase.util.*;
18
19 import org.mmbase.util.logging.*;
20
21 /**
22  * Daymarkers are used to calculate the age of MMBase objects.
23  * Every day a daymarker is added to the daymarks table. Such an entry
24  * consists of a daycount (number of days from 1970), and a count
25  * (current object number of that day).
26  *
27  * @author Daniel Ockeloen,Rico Jansen
28  * @author Michiel Meeuwissen
29  * @version $Id: DayMarkers.java,v 1.42 2005/10/04 22:52:58 michiel Exp $
30  */

31 public class DayMarkers extends MMObjectBuilder {
32
33     public static final String JavaDoc FIELD_DAYCOUNT = "daycount";
34     public static final String JavaDoc FIELD_MARK = "mark";
35     public static final long SECONDS_IN_A_DAY = 24*3600;
36     public static final long MILLISECONDS_IN_A_DAY = SECONDS_IN_A_DAY*1000;
37
38     private static final Logger log = Logging.getLoggerInstance(DayMarkers.class);
39
40     private int day = 0; // current day number/count
41
private Map daycache = new TreeMap(); // day -> mark, but ordered
42

43     private int smallestDay; // will be queried when this builder is started
44

45     /**
46      * Put in cache. This function essentially does the casting to
47      * Integer and wrapping in 'synchronized' for you.
48      */

49     private void cachePut(int day, int mark) {
50         synchronized(daycache) {
51             daycache.put(new Integer JavaDoc(day), new Integer JavaDoc(mark));
52         }
53     }
54
55     /**
56      * set the current day. This is the number of days from 1970.
57      */

58     public DayMarkers() {
59         day = currentDay();
60     }
61
62     /**
63      * Calculate smallestMark, and smallestDay.
64      * smallestMark is the smallest object number for which a daymark exists.
65      * smallestDay is the first daymarker that was set.
66      */

67     public boolean init() {
68         log.debug("Init of DayMarkers");
69         boolean result;
70         result = super.init();
71         smallestDay = 0;
72
73         try {
74             NodeSearchQuery query = new NodeSearchQuery(this);
75             StepField field = query.getField(getField(FIELD_NUMBER));
76             query.addSortOrder(field);
77             query.setMaxNumber(1);
78             List resultList = getNodes(query);
79             if (resultList.size() > 0) {
80                 MMObjectNode mark = (MMObjectNode) resultList.get(0);
81                 smallestDay = mark.getIntValue(FIELD_DAYCOUNT);
82             }
83             if (smallestDay < day) {
84                 smallestDay = day; // currentDay();
85
createMarker();
86             }
87         } catch (SearchQueryException e) {
88             log.error("SQL Exception " + e + ". Could not find smallestMarker, smallestDay");
89             result = false;
90         }
91
92         return result;
93     }
94
95     /**
96      * The current time in days since 1-1-1970
97      */

98     private int currentDay() {
99         return (int)(System.currentTimeMillis()/MILLISECONDS_IN_A_DAY);
100     }
101
102
103     /**
104      * Creates a mark in the database, if necessary.
105      */

106     private void createMarker() {
107         // test if the node for today exists
108
NodeSearchQuery query = new NodeSearchQuery(this);
109         query.setMaxNumber(1);
110         StepField daycountField = query.getField(getField(FIELD_DAYCOUNT));
111         BasicFieldValueConstraint constraint = new BasicFieldValueConstraint(daycountField, new Integer JavaDoc(day));
112         query.setConstraint(constraint);
113         try {
114             List resultList = getNodes(query);
115             if (resultList.size() == 0) {
116                 // if not, retrieve the mark (highest node number) for today
117
MMObjectBuilder root = mmb.getRootBuilder();
118                 query = new NodeSearchQuery(root);
119                 ModifiableQuery modifiedQuery = new ModifiableQuery(query);
120                 Step step = (Step) query.getSteps().get(0);
121                 AggregatedField field = new BasicAggregatedField(
122                     step, root.getField(FIELD_NUMBER), AggregatedField.AGGREGATION_TYPE_MAX);
123                 List newFields = new ArrayList(1);
124                 newFields.add(field);
125                 modifiedQuery.setFields(newFields);
126                 List results = mmb.getSearchQueryHandler().getNodes(modifiedQuery, new ResultBuilder(mmb, modifiedQuery));
127                 ResultNode result = (ResultNode) results.get(0);
128                 int max = result.getIntValue(FIELD_NUMBER);
129                 // add a new daymarker node
130
MMObjectNode node = getNewNode(SYSTEM_OWNER);
131                 node.setValue(FIELD_DAYCOUNT,day);
132                 node.setValue(FIELD_MARK,max);
133                 insert(SYSTEM_OWNER,node);
134             }
135         } catch (SearchQueryException e) {
136             log.error(Logging.stackTrace(e));
137         }
138
139     }
140
141     /**
142      * This gets called every hour to see if the day has past.
143      */

144     public void probe() {
145         int newday;
146         newday=currentDay();
147         //debug("Days "+newday+" current "+day);
148
if (newday>day) {
149             day = newday;
150             createMarker();
151         }
152     }
153
154
155     /**
156      * Returns the age, in days, of a node. So, this does the inverse of most methods in this
157      * class. It converts a node number (which is like a mark) to a day.
158      */

159     public int getAge(int nodeNumber) {
160         // first, check if it accidentily can be found with the cache:
161
Set days = daycache.entrySet();
162         Iterator i = days.iterator();
163         if (i.hasNext()) { // cache not empty
164
Map.Entry current = (Map.Entry)i.next();
165             Map.Entry previous = null;
166             while (i.hasNext() && ((Integer JavaDoc)current.getValue()).intValue() < nodeNumber) { // search until current > nodeNumber
167
previous = current;
168                 current = (Map.Entry)i.next();
169             }
170             if ((previous != null) && ((Integer JavaDoc)current.getValue()).intValue() >= nodeNumber) { // found in cache
171
// if we found a lower and a higher mark on two consecutive days, return the lower.
172
if (((Integer JavaDoc)current.getKey()).intValue() - ((Integer JavaDoc)previous.getKey()).intValue() == 1) {
173                     return day - ((Integer JavaDoc)previous.getKey()).intValue();
174                 }
175             }
176
177         }
178         log.debug("Could not find with daycache " + nodeNumber + ", searching in database now");
179
180         try {
181             NodeSearchQuery query = new NodeSearchQuery(this);
182             StepField dayCount = query.getField(getField(FIELD_DAYCOUNT));
183             BasicSortOrder sortOrder = query.addSortOrder(dayCount);
184             sortOrder.setDirection(SortOrder.ORDER_DESCENDING);
185             StepField markField = query.getField(getField(FIELD_MARK));
186             BasicFieldValueConstraint cons = new BasicFieldValueConstraint(markField, new Integer JavaDoc(nodeNumber));
187             cons.setOperator(FieldCompareConstraint.LESS);
188             query.setConstraint(cons);
189             query.setMaxNumber(1);
190
191             List resultList = getNodes(query);
192             // mark < in stead of mark = will of course only be used in database with are not on line always, such
193
// that some days do not have a mark.
194
if (log.isDebugEnabled()) {
195                 log.debug(query);
196             }
197
198             // String query = "select mark, daycount from " + mmb.baseName + "_" + tableName + " where mark < "+ nodeNumber + " order by daycount desc";
199
if (resultList.size() > 0) {
200                 // search the first daycount of which' mark is lower.
201
// that must be the day which we were searching (at least a good estimate)
202
MMObjectNode markNode = (MMObjectNode) resultList.get(0);
203                 int mark = markNode.getIntValue(FIELD_MARK);
204                 int daycount = markNode.getIntValue(FIELD_DAYCOUNT);
205                 cachePut(daycount, mark); // found one, could as well cache it
206
getDayCount(daycount + 1); // next time, this can be count with the cache as well
207
return day - daycount;
208             } else {
209                 // hmm, strange, perhaps we have to seek the oldest daycount, but for the moment:
210
log.service("daycount could not be found for node " + nodeNumber);
211                 // determining the oldest daycount:
212
query = new NodeSearchQuery(this);
213                 StepField number = query.getField(getField(FIELD_NUMBER));
214                 sortOrder = query.addSortOrder(number);
215                 sortOrder.setDirection(SortOrder.ORDER_ASCENDING);
216                 query.setMaxNumber(1);
217                 resultList = getNodes(query);
218
219                 if (resultList.size() > 0) {
220                     MMObjectNode markNode = (MMObjectNode) resultList.get(0);
221                     int mark = markNode.getIntValue(FIELD_MARK);
222                     int daycount = markNode.getIntValue(FIELD_DAYCOUNT);
223                     cachePut(daycount, mark); // found one, could as well cache it
224
getDayCount(daycount + 1); // next time, this can be count with the cache as well
225
return day - daycount;
226                 } else {
227                     // no daymarks found at all.
228
return 0; // everything from today.
229
}
230
231             }
232         } catch(SearchQueryException e) {
233             log.error(Logging.stackTrace(e));
234             return -1;
235         }
236
237     }
238
239     /**
240      * The current day count.
241      * @return the number of days from 1970 of today.
242      **/

243     public int getDayCount() {
244         return day;
245     }
246
247     /**
248      * Given an age, this function returns a mark, _not a day count_, and also _not an age_!
249      * @param daysold a time in days ago.
250      * @return the smallest object number of all objects that are younger than given parameter daysold.
251      **/

252     public int getDayCountAge(int daysold) {
253         int wday = day - daysold;
254         return getDayCount(wday);
255     }
256
257     /**
258      * Calculates the smallest object number of all objects that are younger than the specified age.
259      * @param wday teh age in number of days from 1970
260      * @return the smallest object number, 0 if it can't be found
261      */

262     private int getDayCount(int wday) {
263         log.debug("finding mark of day " + wday);
264         Integer JavaDoc result = (Integer JavaDoc)daycache.get(new Integer JavaDoc(wday));
265         if (result!=null) { // already in cache
266
return result.intValue();
267         }
268         log.debug("could not be found in cache");
269
270         if (wday < smallestDay) { // will not be possible to find in database
271
if (log.isDebugEnabled() ) {
272                 log.debug("Day " + wday + " is smaller than smallest in database");
273             }
274             return 0;
275         }
276         if (wday <= day) {
277             NodeSearchQuery query = new NodeSearchQuery(this);
278             query.setMaxNumber(1);
279             StepField daycountField = query.getField(getField(FIELD_DAYCOUNT));
280             BasicFieldValueConstraint constraint = new BasicFieldValueConstraint(daycountField, new Integer JavaDoc(wday));
281             constraint.setOperator(FieldCompareConstraint.GREATER_EQUAL);
282             query.setConstraint(constraint);
283             int mark = 0;
284             try {
285                 List resultList = getNodes(query);
286                 if (resultList.size() != 0) {
287                     MMObjectNode resultNode = (MMObjectNode) resultList.get(0);
288                     mark = resultNode.getIntValue(FIELD_MARK);
289                     int daycount = resultNode.getIntValue(FIELD_DAYCOUNT);
290                     if (daycount != wday) {
291                         log.error("Could not find day " + wday + ", surrogated with " + daycount);
292                     } else {
293                         log.debug("Found in db, will be inserted in cache");
294                     }
295                     cachePut(wday, mark);
296                 }
297             } catch (SearchQueryException e) {
298                 log.error(Logging.stackTrace(e));
299             }
300             return mark;
301         } else {
302             return Integer.MAX_VALUE;
303         }
304     }
305
306     /**
307      * Scan. Known tokens are:
308      * COUNT-X gets an object number of X days after 1970
309      * COUNTAGE-X gets an object number of X days old
310      * COUNTMONTH-X gets an object number of X months after 1970
311      * COUNTNEXTMONTH-X gets an object number of X+1 months after 1970
312      * COUNTPREVMONTH-X gets an object number of X-1 months after 1970
313      * COUNTPREVDELTAMONTH-X-Y gets an object number of X-Y months after 1970
314      * COUNTNEXTDELTAMONTH-X-Y gets an object number of X+Y months after 1970
315      * TIMETOOBJECTNUMBER gets an object number of X seconds after 1970
316      **/

317     public String JavaDoc replace(PageInfo sp, StringTokenizer command) {
318         String JavaDoc rtn="";
319         int ival;
320         if (command.hasMoreTokens()) {
321             String JavaDoc token=command.nextToken();
322             if (token.equals("COUNT")) {
323                 ival=fetchIntValue(command);
324                 rtn=""+getDayCount(ival);
325             } else if (token.equals("COUNTAGE")) {
326                 ival=fetchIntValue(command);
327                 rtn=""+getDayCountAge(ival);
328             } else if (token.equals("COUNTMONTH")) {
329                 ival=fetchIntValue(command);
330                 rtn=""+getDayCount(getDayCountMonth(ival));
331             } else if (token.equals("COUNTNEXTMONTH")) {
332                 ival=fetchIntValue(command);
333                 rtn=""+getDayCount(getDayCountNextMonth(ival));
334             } else if (token.equals("COUNTPREVMONTH")) {
335                 ival=fetchIntValue(command);
336                 rtn=""+getDayCount(getDayCountPreviousMonth(ival));
337             } else if (token.equals("COUNTPREVDELTAMONTH")) {
338                 ival=fetchIntValue(command);
339                 int delta=0-fetchIntValue(command);
340                 rtn=""+getDayCount(getDayCountDeltaMonth(ival,delta));
341             } else if (token.equals("COUNTNEXTDELTAMONTH")) {
342                 ival=fetchIntValue(command);
343                 int delta=fetchIntValue(command);
344                 rtn=""+getDayCount(getDayCountDeltaMonth(ival,delta));
345             } else if (token.equals("TIMETOOBJECTNUMBER")){
346                 ival=fetchIntValue(command);
347                 rtn=""+getDayCount((int)(ival/SECONDS_IN_A_DAY));
348             } else {
349                 rtn="UnknownCommand";
350             }
351         }
352         return rtn;
353     }
354
355     /**
356      * @javadoc
357      */

358     private int fetchIntValue(StringTokenizer command) {
359         String JavaDoc val;
360         int ival;
361         if (command.hasMoreTokens()) {
362             val=command.nextToken();
363         } else {
364             val="0";
365         }
366         try {
367             ival=Integer.parseInt(val);
368         } catch (NumberFormatException JavaDoc e) {
369             ival=0;
370         }
371         return ival;
372     }
373
374     /**
375      * get a Calendar
376      * @param months number of months from 1970
377      * @return calendar with date specified in months from 1970
378      */

379     private Calendar getCalendarMonths(int months) {
380         int year,month;
381         year=months/12;
382         month=months%12;
383         GregorianCalendar cal=new GregorianCalendar();
384         cal.set(year+1970,month,1,0,0,0);
385         return cal;
386     }
387
388     /**
389      * @javadoc
390      */

391     private Calendar getCalendarDays(int days) {
392         GregorianCalendar cal = new GregorianCalendar();
393         java.util.Date JavaDoc d = new java.util.Date JavaDoc((days)*MILLISECONDS_IN_A_DAY);
394         cal.setTime(d);
395         return cal;
396     }
397
398     /**
399      * @javadoc
400      */

401     private int getDayCountMonth(int months) {
402         Calendar cal = getCalendarMonths(months);
403         return (int)(cal.getTime().getTime()/MILLISECONDS_IN_A_DAY);
404     }
405
406     /**
407      * @javadoc
408      */

409     private int getDayCountPreviousMonth(int months) {
410         Calendar cal = getCalendarMonths(months);
411         cal.add(Calendar.MONTH,-1);
412         return (int)(cal.getTime().getTime()/MILLISECONDS_IN_A_DAY);
413     }
414
415     /**
416      * @javadoc
417      */

418     private int getDayCountNextMonth(int months) {
419         Calendar cal = getCalendarMonths(months);
420         cal.add(Calendar.MONTH,1);
421         return (int)(cal.getTime().getTime()/MILLISECONDS_IN_A_DAY);
422     }
423
424     /**
425      * @javadoc
426      */

427     private int getDayCountDeltaMonth(int months,int delta) {
428         Calendar cal = getCalendarMonths(months);
429         cal.add(Calendar.MONTH,delta);
430         return (int)(cal.getTime().getTime()/MILLISECONDS_IN_A_DAY);
431     }
432
433     /**
434      * @javadoc
435      */

436     public int getDayCountByObject(int number) {
437         NodeSearchQuery query = new NodeSearchQuery(this);
438         query.setMaxNumber(1);
439         StepField markField = query.getField(getField(FIELD_MARK));
440         BasicFieldValueConstraint constraint = new BasicFieldValueConstraint(markField, new Integer JavaDoc(number));
441         constraint.setOperator(FieldCompareConstraint.LESS);
442         query.setConstraint(constraint);
443         ModifiableQuery modifiedQuery = new ModifiableQuery(query);
444         Step step = (Step) query.getSteps().get(0);
445         AggregatedField field = new BasicAggregatedField(
446             step, getField(FIELD_DAYCOUNT), AggregatedField.AGGREGATION_TYPE_MAX);
447         List newFields = new ArrayList(1);
448         newFields.add(field);
449         modifiedQuery.setFields(newFields);
450         try {
451             List results = mmb.getSearchQueryHandler().getNodes(modifiedQuery, new ResultBuilder(mmb, modifiedQuery));
452             ResultNode result = (ResultNode) results.get(0);
453             return result.getIntValue(FIELD_DAYCOUNT);
454         } catch (SearchQueryException e) {
455             log.error(Logging.stackTrace(e));
456             return 0;
457         }
458
459     }
460
461     /**
462      * @javadoc
463      */

464     public int getMonthsByDayCount(int daycount) {
465         int year,month;
466         Calendar calendar;
467
468         calendar = getCalendarDays(daycount);
469         year = calendar.get(Calendar.YEAR)-1970;
470         month = calendar.get(Calendar.MONTH);
471         return month + year * 12;
472     }
473
474
475     /**
476      * Returns the date of a daymarker
477      * @param node The node of which the date is wanted
478      * @return a <code>Date</code> which is the date
479      */

480     public java.util.Date JavaDoc getDate(MMObjectNode node) {
481         int dayCount = node.getIntValue(FIELD_DAYCOUNT);
482         return new java.util.Date JavaDoc(dayCount*MILLISECONDS_IN_A_DAY);
483     }
484
485     /**
486      * Returns gui information for a specific node. This value is retrieved by retrieving the field 'gui()' of the node (node.getStringValue("gui()") )
487      * @param node The node of which the gui information is wanted
488      * @return a <code>String</code> in which the current date is shown
489      */

490     public String JavaDoc getLocaleGUIIndicator(Locale locale, MMObjectNode node) {
491         return DateFormat.getDateInstance(DateFormat.LONG, locale).format(getDate(node));
492
493     }
494
495 }
496
Popular Tags