1    /*
2     * Copyright 2009 :torweg free software group
3     *
4     * This program is free software: you can redistribute it and/or modify
5     * it under the terms of the GNU General Public License as published by
6     * the Free Software Foundation, either version 3 of the License, or
7     * (at your option) any later version.
8     * 
9     * This program is distributed in the hope that it will be useful,
10    * but WITHOUT ANY WARRANTY; without even the implied warranty of
11    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    * GNU General Public License for more details.
13    * 
14    * You should have received a copy of the GNU General Public License
15    * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16    *
17    */
18   package org.torweg.pulse.component.statistics.aggregator;
19   
20   import java.util.List;
21   
22   import org.hibernate.Session;
23   import org.torweg.pulse.component.statistics.model.AbstractRegexVersioned;
24   import org.torweg.pulse.component.statistics.model.Visit;
25   import org.torweg.pulse.component.statistics.model.aggregation.AbstractRegexVersionedCounter;
26   import org.torweg.pulse.component.statistics.model.aggregation.AbstractRegexVersionedCounterAggregation;
27   import org.torweg.pulse.service.PulseException;
28   import org.torweg.pulse.util.time.Duration;
29   
30   /**
31    * Passes the given {@code Visit} to {@code &lt;T&gt;} and stores the
32    * result of the aggregation to {@code &lt;U&gt;} on a per
33    * {@code Visit} basis.
34    * <p>
35    * Here <strong>on a per {@code Visit} basis</strong> means:<br/>
36    * A {@code Visit} extending over two days (e.g. first PIRecord [2009.01.01
37    * 23:59:59:999], second PIRecord [2009.01.02 00:00:00:000]) will only be
38    * aggregated once for - the first/start day of - the {@code Visit}.
39    * </p>
40    * 
41    * @param <T>
42    *            the {@code AbstractRegexVersioned} being processed by the
43    *            {@code AbstractRegexVersionedPerVisitAggregator}
44    * @param <U>
45    *            the matching
46    *            {@code AbstractRegexVersionedCounterAggregation&lt;?,?&gt;}
47    *            for {@code &lt;T&gt;}
48    * 
49    * @author Daniel Dietz
50    * @version $Revision: 1541 $
51    * 
52    */
53   public abstract class AbstractRegexVersionedPerVisitAggregator<T extends AbstractRegexVersioned, U extends AbstractRegexVersionedCounterAggregation<?, ?>>
54           extends AbstractBaseAggregator {
55   
56       /**
57        * Aggregates information about the {@code AbstractVersioned}.
58        * 
59        * @param visit
60        *            the {@code Visit} to be processed
61        * @param s
62        *            the current hibernate<sup>TM</sup>-{@code Session}
63        * 
64        * @see org.torweg.pulse.component.statistics.aggregator.AbstractBaseAggregator
65        *      #aggregate(org.torweg.pulse.component.statistics.model.Visit,
66        *      org.hibernate.Session,
67        *      org.torweg.pulse.component.statistics.DataAggregationJobletConfiguration)
68        */
69       @Override
70       public final void aggregate(final Visit visit, final Session s) {
71   
72           List<T> knownAbstractVersioneds = getKnownAbstractRegexVersioneds(s);
73   
74           boolean foundMatch = false;
75           for (T abstractVersioned : knownAbstractVersioneds) {
76               if (abstractVersioned.isMatch(getCheckValue(visit))) {
77                   countAndStore(abstractVersioned, visit,
78                           visit.getFirstFullDay(), s);
79                   foundMatch = true;
80                   break;
81               }
82           }
83           if (!foundMatch) {
84               countAndStore(null, visit, visit.getFirstFullDay(), s);
85           }
86       }
87   
88       /**
89        * Performs the actual aggregation.
90        * 
91        * @param abstractVersioned
92        *            the current {@code T}
93        * @param visit
94        *            the current {@code Visit}
95        * @param fullDay
96        *            a full day-{@code Duration} from the current visit
97        * @param s
98        *            the current {@code Session}
99        * 
100       * @throws PulseException
101       *             if
102       *             <ul>
103       *             <li>
104       *             <tt>abstractVersioned</tt> is {@code null} and the
105       *             <strong>"unknown"</strong> could not be retrieved</li>
106       *             <li>{@code &lt;U&gt;.aggregate(visit)} returns
107       *             {@code false}</li>
108       *             </ul>
109       */
110      private void countAndStore(final T abstractVersioned, final Visit visit,
111              final Duration fullDay, final Session s) {
112  
113          T checkedAbstractVersioned = abstractVersioned;
114          if (checkedAbstractVersioned == null) {
115              // load user-agent "unknown"
116              checkedAbstractVersioned = getUnknownAbstractRegexVersioned(s);
117              if (checkedAbstractVersioned == null) {
118                  throw new PulseException("Failed loading \"unknown\".");
119              }
120          }
121  
122          // try load existing aggregation
123          U aggregation = getAbstractRegexVersionedCounterAggregation(
124                  checkedAbstractVersioned, visit, fullDay, s);
125  
126          if (aggregation == null) {
127              // create new aggregation
128              aggregation = getNewAbstractRegexVersionedCounterAggregation(
129                      checkedAbstractVersioned, visit, fullDay);
130              s.saveOrUpdate(aggregation);
131          }
132  
133          // aggregate
134          boolean success = aggregation.aggregate(visit, s);
135          if (!success) {
136              throw new PulseException(success + ": " + aggregation
137                      + ".aggregate(" + visit + ")");
138          }
139  
140          // save
141          for (AbstractRegexVersionedCounter<?> counter : aggregation
142                  .getVersionCounters()) {
143              s.saveOrUpdate(counter);
144          }
145          s.saveOrUpdate(aggregation);
146  
147      }
148  
149      /**
150       * Returns all the known {@code AbstractRegexVersioned}s of the type
151       * {@code &lt;T&gt;}.
152       * 
153       * @param s
154       *            the current Hibernate<sup>TM</sup>-{@code Session}
155       * 
156       * @return {@code List&lt;AbstractRegexVersioned&gt;} of the type
157       *         {@code &lt;T&gt;}
158       */
159      protected abstract List<T> getKnownAbstractRegexVersioneds(final Session s);
160  
161      /**
162       * Returns the string-value to be checked from the given {@code Visit}.
163       * 
164       * @param visit
165       *            the {@code Visit}
166       * 
167       * @return the string-value to be checked
168       */
169      protected abstract String getCheckValue(final Visit visit);
170  
171      /**
172       * Returns the {@code AbstractRegexVersioned} of the type
173       * {@code &lt;T&gt;} which represents the "unknown".
174       * 
175       * @param s
176       *            the current Hibernate<sup>TM</sup>-{@code Session}
177       * 
178       * @return the {@code AbstractRegexVersioned} of the type
179       *         {@code &lt;T&gt;} which represents the "unknown"
180       */
181      protected abstract T getUnknownAbstractRegexVersioned(final Session s);
182  
183      /**
184       * Tries to load a matching
185       * {@code AbstractRegexVersionCounterAggregation&lt;?,?&gt;} for the
186       * given {@code AbstractRegexVersioned} of the type
187       * {@code &lt;T&gt;}, the given {@code Visit} and the given
188       * {@code Duration}.
189       * 
190       * @param checkedAbstractRegexVersioned
191       *            the {@code AbstractRegexVersioned} of the type
192       *            {@code &lt;T&gt;}
193       * @param visit
194       *            the {@code Visit}
195       * @param fullDay
196       *            the {@code Duration}
197       * @param s
198       *            the current Hibernate<sup>TM</sup>-{@code Session}
199       * 
200       * @return a matching
201       *         {@code AbstractRegexVersionCounterAggregation&lt;?,?&gt;} if
202       *         available, {@code null} otherwise
203       */
204      protected abstract U getAbstractRegexVersionedCounterAggregation(
205              final T checkedAbstractRegexVersioned, final Visit visit,
206              final Duration fullDay, final Session s);
207  
208      /**
209       * Returns a new
210       * {@code AbstractRegexVersionCounterAggregation&lt;?,?&gt;} for the
211       * given {@code AbstractRegexVersioned} of the type
212       * {@code &lt;T&gt;}, the given {@code Visit} and the given
213       * {@code Duration}.
214       * 
215       * @param checkedAbstractRegexVersioned
216       *            the {@code AbstractRegexVersioned} of the type
217       *            {@code &lt;T&gt;}
218       * @param visit
219       *            the {@code Visit}
220       * @param fullDay
221       *            the {@code Duration}
222       * 
223       * @return a new
224       *         {@code AbstractRegexVersionCounterAggregation&lt;?,?&gt;}
225       */
226      protected abstract U getNewAbstractRegexVersionedCounterAggregation(
227              final T checkedAbstractRegexVersioned, final Visit visit,
228              final Duration fullDay);
229  
230  }
231