Ticket 668: Compositing route filter possibility jenkins-Beast-364
authorMats Vernersson <mats.vernersson@smhi.se>
Fri, 3 Feb 2017 10:00:35 +0000 (11:00 +0100)
committerMats Vernersson <mats.vernersson@smhi.se>
Fri, 3 Feb 2017 10:00:35 +0000 (11:00 +0100)
Possibility to filter in the compositing rules added.

Some redesign in CompositingRule was done while enabling the support for filtering. To ensure that correct information on previous period is collected (to check which sources and angles (in case of scans) are to be expected in this period) a different scheme for this had to be implemented. Now, complete file entry information has to be collected for the previous period and the filtering then applied on the result, to see which entries in the previous period matched the filters. This is however only done once for each period. After the first query, the information is stored in the CompositeTimerData.

32 files changed:
itest/eu/baltrad/beast/router/BltRouterCompositeDBITest-context.xml
itest/eu/baltrad/beast/rules/composite/CompositingRuleITest.java
itest/eu/baltrad/beast/rules/composite/fixtures/Z_SCAN_C_ESWI_20101023181500_searl_malfunc_000000.h5 [new file with mode: 0644]
itest/eu/baltrad/beast/rules/composite/fixtures/Z_SCAN_C_ESWI_20170202080000_seang_000000.h5 [new file with mode: 0644]
itest/eu/baltrad/beast/rules/composite/fixtures/Z_SCAN_C_ESWI_20170202080000_searl_000000.h5 [new file with mode: 0644]
itest/eu/baltrad/beast/rules/composite/fixtures/Z_SCAN_C_ESWI_20170202080000_sease_malfunc_000000.h5 [new file with mode: 0644]
itest/eu/baltrad/beast/rules/composite/fixtures/Z_SCAN_C_ESWI_20170202080500_seang_000000.h5 [new file with mode: 0644]
itest/eu/baltrad/beast/rules/composite/fixtures/Z_SCAN_C_ESWI_20170202080500_searl_000000.h5 [new file with mode: 0644]
itest/eu/baltrad/beast/rules/composite/fixtures/Z_SCAN_C_ESWI_20170202080500_sease_000000.h5 [new file with mode: 0644]
itest/eu/baltrad/beast/rules/composite/fixtures/scan_sehud_0.5_20110126T184500Z.h5 [deleted file]
itest/eu/baltrad/beast/rules/composite/fixtures/scan_sehud_0.5_20110126T184600Z.h5 [deleted file]
itest/eu/baltrad/beast/rules/composite/fixtures/scan_sehud_1.0_20110126T184500Z.h5 [deleted file]
itest/eu/baltrad/beast/rules/composite/fixtures/scan_sehud_1.0_20110126T184600Z.h5 [deleted file]
itest/eu/baltrad/beast/rules/composite/fixtures/seang_pvol_no05angle_20170203T010000Z.h5 [new file with mode: 0644]
itest/eu/baltrad/beast/rules/composite/fixtures/seang_pvol_no40angle_20170203T010000Z.h5 [new file with mode: 0644]
itest/eu/baltrad/beast/rules/composite/fixtures/sease_pvol_20170203T010000Z.h5 [new file with mode: 0644]
itest/eu/baltrad/beast/rules/composite/fixtures/sease_pvol_20170203T010500Z.h5 [new file with mode: 0644]
itest/eu/baltrad/beast/rules/composite/fixtures/sekir_pvol_20170203T010000Z.h5 [new file with mode: 0644]
itest/eu/baltrad/beast/rules/composite/fixtures/sekir_pvol_20170203T010500Z.h5 [new file with mode: 0644]
itest/eu/baltrad/beast/rules/composite/fixtures/sekkr_pvol_20170203T010000Z.h5 [new file with mode: 0644]
itest/eu/baltrad/beast/rules/composite/fixtures/sevil_pvol_20170203T010000Z.h5 [new file with mode: 0644]
itest/eu/baltrad/beast/rules/composite/fixtures/sevil_pvol_20170203T010500Z.h5 [new file with mode: 0644]
itest/eu/baltrad/beast/rules/composite/fixtures/sevil_pvol_no05angle_20170203T010500Z.h5 [new file with mode: 0644]
itest/eu/baltrad/beast/rules/volume/VolumeRuleITest.java
src/eu/baltrad/beast/db/filters/CompositingRuleFilter.java [new file with mode: 0644]
src/eu/baltrad/beast/router/RouteDefinition.java
src/eu/baltrad/beast/rules/composite/CompositeTimerData.java
src/eu/baltrad/beast/rules/composite/CompositingRule.java
src/eu/baltrad/beast/rules/composite/CompositingRuleManager.java
test/eu/baltrad/beast/db/filters/CompositingRuleFilterTest.java [new file with mode: 0644]
test/eu/baltrad/beast/rules/composite/CompositingRuleManagerTest.java
test/eu/baltrad/beast/rules/composite/CompositingRuleTest.java

index f3cb0e3..16242b4 100644 (file)
@@ -63,7 +63,9 @@
 
   <bean id="catalog" class="eu.baltrad.beast.db.Catalog" autowire="byType" />
 
-  <bean id="bltcompositemgr" class="eu.baltrad.beast.rules.composite.CompositingRuleManager" autowire="byType" />
+  <bean id="filterManager" class="eu.baltrad.beast.rules.RuleFilterManager" autowire="byType" />
+
+  <bean id="bltcompositemgr" class="eu.baltrad.beast.rules.composite.CompositingRuleManager" autowire="byType" />  
   
   <bean id="router" class="eu.baltrad.beast.router.impl.BltRouter" autowire="byType">
     <property name="ruleManagers">
index 8849d04..d26bc1a 100644 (file)
@@ -23,21 +23,20 @@ import java.io.FileInputStream;
 import java.util.ArrayList;
 import java.util.List;
 
-import junit.framework.TestCase;
-
 import org.springframework.context.support.AbstractApplicationContext;
 
 import eu.baltrad.bdb.db.FileEntry;
-import eu.baltrad.bdb.util.DateTime;
-
+import eu.baltrad.beast.db.AttributeFilter;
 import eu.baltrad.beast.db.Catalog;
-import eu.baltrad.beast.db.CatalogEntry;
+import eu.baltrad.beast.db.CombinedFilter;
+import eu.baltrad.beast.db.CombinedFilter.MatchType;
 import eu.baltrad.beast.itest.BeastDBTestHelper;
 import eu.baltrad.beast.message.IBltMessage;
 import eu.baltrad.beast.message.mo.BltDataMessage;
 import eu.baltrad.beast.message.mo.BltGenerateMessage;
 import eu.baltrad.beast.rules.timer.TimeoutManager;
 import eu.baltrad.beast.rules.util.IRuleUtilities;
+import junit.framework.TestCase;
 
 /**
  * @author Anders Henja
@@ -89,11 +88,6 @@ public class CompositingRuleITest extends TestCase {
     "fixtures/Z_SCAN_C_ESWI_20101016080500_sevil_000000.h5"
   };
   
-  private static String SCAN_SEHUD_CONFLICT_ELANGLE_1 = "fixtures/scan_sehud_0.5_20110126T184500Z.h5";  
-  private static String SCAN_SEHUD_CONFLICT_ELANGLE_2 = "fixtures/scan_sehud_0.5_20110126T184600Z.h5";
-  private static String SCAN_SEHUD_CONFLICT_ELANGLE_3 = "fixtures/scan_sehud_1.0_20110126T184500Z.h5";
-  private static String SCAN_SEHUD_CONFLICT_ELANGLE_4 = "fixtures/scan_sehud_1.0_20110126T184600Z.h5";
-  
   public CompositingRuleITest(String name) {
     super(name);
   }
@@ -241,47 +235,92 @@ public class CompositingRuleITest extends TestCase {
     assertNotNull(catalogAndHandle(classUnderTest, getFilePath("fixtures/Z_SCAN_C_ESWI_20101023182000_sease_000000.h5")));
   }
   
-  public void testFetchScanEntries() throws Exception {
+  public void testHandle_filterMalfuncScans() throws Exception {
+    AttributeFilter filter = new AttributeFilter();
+    filter.setAttribute("how/malfunc");
+    filter.setValueType(AttributeFilter.ValueType.STRING);
+    filter.setValue("True");
+    filter.setOperator(AttributeFilter.Operator.NE);
+    classUnderTest.setFilter(filter);
+    
     List<String> sources = new ArrayList<String>();
-    sources.add("sehud");
+    sources.add("seang");
+    sources.add("searl");
+    sources.add("sease");
 
     classUnderTest.setArea("baltrad_composite");
     classUnderTest.setInterval(5);
     classUnderTest.setSources(sources);
-    classUnderTest.setScanBased(true);    
+    classUnderTest.setTimeout(10000);
+    classUnderTest.setScanBased(true);
     
-    // 20110126T184500Z
-    FileEntry f2 = catalog.getCatalog().store(new FileInputStream(getFilePath(SCAN_SEHUD_CONFLICT_ELANGLE_2)));
-    Thread.sleep(2000); // We need to ensure that files are stored at different times to reproduce the problem
-    FileEntry f1 = catalog.getCatalog().store(new FileInputStream(getFilePath(SCAN_SEHUD_CONFLICT_ELANGLE_1)));
+    assertNull(catalogAndHandle(classUnderTest, getFilePath("fixtures/Z_SCAN_C_ESWI_20101023181000_seang_000000.h5")));
+    assertNull(catalogAndHandle(classUnderTest, getFilePath("fixtures/Z_SCAN_C_ESWI_20101023181000_searl_000000.h5")));
+    assertNull(catalogAndHandle(classUnderTest, getFilePath("fixtures/Z_SCAN_C_ESWI_20101023181000_sease_000000.h5")));
+    // next period
+    assertNull(catalogAndHandle(classUnderTest, getFilePath("fixtures/Z_SCAN_C_ESWI_20101023181500_seang_000000.h5")));
+    assertNull(catalogAndHandle(classUnderTest, getFilePath("fixtures/Z_SCAN_C_ESWI_20101023181500_searl_malfunc_000000.h5")));
+    // Since the scan above was filtered out, due to malfunc, no composite should be generated when adding sease below
+    assertNull(catalogAndHandle(classUnderTest, getFilePath("fixtures/Z_SCAN_C_ESWI_20101023181500_sease_000000.h5")));
+    // First when adding the none-malfunc scan for searl, a composite shall be generated
+    assertNotNull(catalogAndHandle(classUnderTest, getFilePath("fixtures/Z_SCAN_C_ESWI_20101023181500_searl_000000.h5")));
+
+    assertNull(catalogAndHandle(classUnderTest, getFilePath("fixtures/Z_SCAN_C_ESWI_20170202080000_searl_000000.h5")));
+    assertNull(catalogAndHandle(classUnderTest, getFilePath("fixtures/Z_SCAN_C_ESWI_20170202080000_sease_malfunc_000000.h5")));
+    assertNull(catalogAndHandle(classUnderTest, getFilePath("fixtures/Z_SCAN_C_ESWI_20170202080000_seang_000000.h5")));
+    // next period - since sease was filtered out last period, it is sufficient to receive searl and seang in the next to create a composite
+    assertNull(catalogAndHandle(classUnderTest, getFilePath("fixtures/Z_SCAN_C_ESWI_20170202080500_searl_000000.h5")));
+    assertNotNull(catalogAndHandle(classUnderTest, getFilePath("fixtures/Z_SCAN_C_ESWI_20170202080500_seang_000000.h5")));
     
-    List<CatalogEntry> entries = classUnderTest.fetchScanEntries(new DateTime(2011,1,26,18,45,0));
-    assertEquals(1, entries.size());
-    assertEquals(f1.getUuid().toString(), entries.get(0).getUuid());
-    assertEquals("20110126", entries.get(0).getFileEntry().getMetadata().getWhatDate().toIsoString());
-    assertEquals("184500", entries.get(0).getFileEntry().getMetadata().getWhatTime().toIsoString());
   }
-
-  public void testFetchScanEntries_bothDuplicateElAngleAndDates() throws Exception {
+  
+  public void testHandle_filterPvolAngles() throws Exception {
+    AttributeFilter attrFilter1 = new AttributeFilter();
+    attrFilter1.setAttribute("where/elangle");
+    attrFilter1.setValueType(AttributeFilter.ValueType.DOUBLE);
+    attrFilter1.setValue("0.7");
+    attrFilter1.setOperator(AttributeFilter.Operator.LT);
+    
+    AttributeFilter attrFilter2 = new AttributeFilter();
+    attrFilter2.setAttribute("where/elangle");
+    attrFilter2.setValueType(AttributeFilter.ValueType.DOUBLE);
+    attrFilter2.setValue("25.0");
+    attrFilter2.setOperator(AttributeFilter.Operator.GT);
+    
+    CombinedFilter combinedFilter = new CombinedFilter();
+    combinedFilter.addChildFilter(attrFilter1);
+    combinedFilter.addChildFilter(attrFilter2);
+    combinedFilter.setMatchType(MatchType.ALL);
+    
+    classUnderTest.setFilter(combinedFilter);
+    
     List<String> sources = new ArrayList<String>();
-    sources.add("sehud");
+    sources.add("sease");
+    sources.add("sekir");
+    sources.add("sevil");
+    sources.add("seang");
 
     classUnderTest.setArea("baltrad_composite");
     classUnderTest.setInterval(5);
     classUnderTest.setSources(sources);
-    classUnderTest.setScanBased(true);    
+    classUnderTest.setTimeout(10000);
+    classUnderTest.setScanBased(false);
+    
+    assertNull(catalogAndHandle(classUnderTest, getFilePath("fixtures/sease_pvol_20170203T010000Z.h5")));
+    assertNull(catalogAndHandle(classUnderTest, getFilePath("fixtures/sekkr_pvol_20170203T010000Z.h5"))); // not in sources list
+    assertNull(catalogAndHandle(classUnderTest, getFilePath("fixtures/sekir_pvol_20170203T010000Z.h5")));
+    assertNull(catalogAndHandle(classUnderTest, getFilePath("fixtures/seang_pvol_no05angle_20170203T010000Z.h5")));
+    assertNull(catalogAndHandle(classUnderTest, getFilePath("fixtures/seang_pvol_no40angle_20170203T010000Z.h5")));
+    assertNull(catalogAndHandle(classUnderTest, getFilePath("fixtures/sevil_pvol_20170203T010000Z.h5")));
     
-    // 20110126T184500Z
-    FileEntry f1 = catalog.getCatalog().store(new FileInputStream(getFilePath(SCAN_SEHUD_CONFLICT_ELANGLE_1)));
-    FileEntry f2 = catalog.getCatalog().store(new FileInputStream(getFilePath(SCAN_SEHUD_CONFLICT_ELANGLE_2)));
-    FileEntry f3 = catalog.getCatalog().store(new FileInputStream(getFilePath(SCAN_SEHUD_CONFLICT_ELANGLE_3)));
-    FileEntry f4 = catalog.getCatalog().store(new FileInputStream(getFilePath(SCAN_SEHUD_CONFLICT_ELANGLE_4)));
+    // since only three of the sources in the source-list were received with correct angles in the previous period, 
+    // it is enough to receive those three sources in the next
+    assertNull(catalogAndHandle(classUnderTest, getFilePath("fixtures/sease_pvol_20170203T010500Z.h5")));
+    assertNull(catalogAndHandle(classUnderTest, getFilePath("fixtures/sevil_pvol_no05angle_20170203T010500Z.h5")));
+    assertNull(catalogAndHandle(classUnderTest, getFilePath("fixtures/sekir_pvol_20170203T010500Z.h5")));
+    // no composite generated for volume above, since sevil was filtered out. Need to add sevil volume the pass filter below
+    assertNotNull(catalogAndHandle(classUnderTest, getFilePath("fixtures/sevil_pvol_20170203T010500Z.h5")));
     
-    List<CatalogEntry> entries = classUnderTest.fetchScanEntries(new DateTime(2011,1,26,18,45,0));
-    assertEquals(1, entries.size());
-    assertEquals(f1.getUuid().toString(), entries.get(0).getUuid());
-    assertEquals("20110126", entries.get(0).getFileEntry().getMetadata().getWhatDate().toIsoString());
-    assertEquals("184500", entries.get(0).getFileEntry().getMetadata().getWhatTime().toIsoString());
   }
   
   protected BltDataMessage createDataMessage(FileEntry f) {
diff --git a/itest/eu/baltrad/beast/rules/composite/fixtures/Z_SCAN_C_ESWI_20101023181500_searl_malfunc_000000.h5 b/itest/eu/baltrad/beast/rules/composite/fixtures/Z_SCAN_C_ESWI_20101023181500_searl_malfunc_000000.h5
new file mode 100644 (file)
index 0000000..27cc03f
Binary files /dev/null and b/itest/eu/baltrad/beast/rules/composite/fixtures/Z_SCAN_C_ESWI_20101023181500_searl_malfunc_000000.h5 differ
diff --git a/itest/eu/baltrad/beast/rules/composite/fixtures/Z_SCAN_C_ESWI_20170202080000_seang_000000.h5 b/itest/eu/baltrad/beast/rules/composite/fixtures/Z_SCAN_C_ESWI_20170202080000_seang_000000.h5
new file mode 100644 (file)
index 0000000..ab31e8b
Binary files /dev/null and b/itest/eu/baltrad/beast/rules/composite/fixtures/Z_SCAN_C_ESWI_20170202080000_seang_000000.h5 differ
diff --git a/itest/eu/baltrad/beast/rules/composite/fixtures/Z_SCAN_C_ESWI_20170202080000_searl_000000.h5 b/itest/eu/baltrad/beast/rules/composite/fixtures/Z_SCAN_C_ESWI_20170202080000_searl_000000.h5
new file mode 100644 (file)
index 0000000..ed7107f
Binary files /dev/null and b/itest/eu/baltrad/beast/rules/composite/fixtures/Z_SCAN_C_ESWI_20170202080000_searl_000000.h5 differ
diff --git a/itest/eu/baltrad/beast/rules/composite/fixtures/Z_SCAN_C_ESWI_20170202080000_sease_malfunc_000000.h5 b/itest/eu/baltrad/beast/rules/composite/fixtures/Z_SCAN_C_ESWI_20170202080000_sease_malfunc_000000.h5
new file mode 100644 (file)
index 0000000..d814e77
Binary files /dev/null and b/itest/eu/baltrad/beast/rules/composite/fixtures/Z_SCAN_C_ESWI_20170202080000_sease_malfunc_000000.h5 differ
diff --git a/itest/eu/baltrad/beast/rules/composite/fixtures/Z_SCAN_C_ESWI_20170202080500_seang_000000.h5 b/itest/eu/baltrad/beast/rules/composite/fixtures/Z_SCAN_C_ESWI_20170202080500_seang_000000.h5
new file mode 100644 (file)
index 0000000..6f26efd
Binary files /dev/null and b/itest/eu/baltrad/beast/rules/composite/fixtures/Z_SCAN_C_ESWI_20170202080500_seang_000000.h5 differ
diff --git a/itest/eu/baltrad/beast/rules/composite/fixtures/Z_SCAN_C_ESWI_20170202080500_searl_000000.h5 b/itest/eu/baltrad/beast/rules/composite/fixtures/Z_SCAN_C_ESWI_20170202080500_searl_000000.h5
new file mode 100644 (file)
index 0000000..fda38ce
Binary files /dev/null and b/itest/eu/baltrad/beast/rules/composite/fixtures/Z_SCAN_C_ESWI_20170202080500_searl_000000.h5 differ
diff --git a/itest/eu/baltrad/beast/rules/composite/fixtures/Z_SCAN_C_ESWI_20170202080500_sease_000000.h5 b/itest/eu/baltrad/beast/rules/composite/fixtures/Z_SCAN_C_ESWI_20170202080500_sease_000000.h5
new file mode 100644 (file)
index 0000000..d72053d
Binary files /dev/null and b/itest/eu/baltrad/beast/rules/composite/fixtures/Z_SCAN_C_ESWI_20170202080500_sease_000000.h5 differ
diff --git a/itest/eu/baltrad/beast/rules/composite/fixtures/scan_sehud_0.5_20110126T184500Z.h5 b/itest/eu/baltrad/beast/rules/composite/fixtures/scan_sehud_0.5_20110126T184500Z.h5
deleted file mode 100644 (file)
index d7b8b56..0000000
Binary files a/itest/eu/baltrad/beast/rules/composite/fixtures/scan_sehud_0.5_20110126T184500Z.h5 and /dev/null differ
diff --git a/itest/eu/baltrad/beast/rules/composite/fixtures/scan_sehud_0.5_20110126T184600Z.h5 b/itest/eu/baltrad/beast/rules/composite/fixtures/scan_sehud_0.5_20110126T184600Z.h5
deleted file mode 100644 (file)
index 38496ac..0000000
Binary files a/itest/eu/baltrad/beast/rules/composite/fixtures/scan_sehud_0.5_20110126T184600Z.h5 and /dev/null differ
diff --git a/itest/eu/baltrad/beast/rules/composite/fixtures/scan_sehud_1.0_20110126T184500Z.h5 b/itest/eu/baltrad/beast/rules/composite/fixtures/scan_sehud_1.0_20110126T184500Z.h5
deleted file mode 100644 (file)
index 4762a35..0000000
Binary files a/itest/eu/baltrad/beast/rules/composite/fixtures/scan_sehud_1.0_20110126T184500Z.h5 and /dev/null differ
diff --git a/itest/eu/baltrad/beast/rules/composite/fixtures/scan_sehud_1.0_20110126T184600Z.h5 b/itest/eu/baltrad/beast/rules/composite/fixtures/scan_sehud_1.0_20110126T184600Z.h5
deleted file mode 100644 (file)
index 922d5a5..0000000
Binary files a/itest/eu/baltrad/beast/rules/composite/fixtures/scan_sehud_1.0_20110126T184600Z.h5 and /dev/null differ
diff --git a/itest/eu/baltrad/beast/rules/composite/fixtures/seang_pvol_no05angle_20170203T010000Z.h5 b/itest/eu/baltrad/beast/rules/composite/fixtures/seang_pvol_no05angle_20170203T010000Z.h5
new file mode 100644 (file)
index 0000000..349da32
Binary files /dev/null and b/itest/eu/baltrad/beast/rules/composite/fixtures/seang_pvol_no05angle_20170203T010000Z.h5 differ
diff --git a/itest/eu/baltrad/beast/rules/composite/fixtures/seang_pvol_no40angle_20170203T010000Z.h5 b/itest/eu/baltrad/beast/rules/composite/fixtures/seang_pvol_no40angle_20170203T010000Z.h5
new file mode 100644 (file)
index 0000000..5290dfb
Binary files /dev/null and b/itest/eu/baltrad/beast/rules/composite/fixtures/seang_pvol_no40angle_20170203T010000Z.h5 differ
diff --git a/itest/eu/baltrad/beast/rules/composite/fixtures/sease_pvol_20170203T010000Z.h5 b/itest/eu/baltrad/beast/rules/composite/fixtures/sease_pvol_20170203T010000Z.h5
new file mode 100644 (file)
index 0000000..a280a45
Binary files /dev/null and b/itest/eu/baltrad/beast/rules/composite/fixtures/sease_pvol_20170203T010000Z.h5 differ
diff --git a/itest/eu/baltrad/beast/rules/composite/fixtures/sease_pvol_20170203T010500Z.h5 b/itest/eu/baltrad/beast/rules/composite/fixtures/sease_pvol_20170203T010500Z.h5
new file mode 100644 (file)
index 0000000..9af2566
Binary files /dev/null and b/itest/eu/baltrad/beast/rules/composite/fixtures/sease_pvol_20170203T010500Z.h5 differ
diff --git a/itest/eu/baltrad/beast/rules/composite/fixtures/sekir_pvol_20170203T010000Z.h5 b/itest/eu/baltrad/beast/rules/composite/fixtures/sekir_pvol_20170203T010000Z.h5
new file mode 100644 (file)
index 0000000..bbebaf2
Binary files /dev/null and b/itest/eu/baltrad/beast/rules/composite/fixtures/sekir_pvol_20170203T010000Z.h5 differ
diff --git a/itest/eu/baltrad/beast/rules/composite/fixtures/sekir_pvol_20170203T010500Z.h5 b/itest/eu/baltrad/beast/rules/composite/fixtures/sekir_pvol_20170203T010500Z.h5
new file mode 100644 (file)
index 0000000..0246a62
Binary files /dev/null and b/itest/eu/baltrad/beast/rules/composite/fixtures/sekir_pvol_20170203T010500Z.h5 differ
diff --git a/itest/eu/baltrad/beast/rules/composite/fixtures/sekkr_pvol_20170203T010000Z.h5 b/itest/eu/baltrad/beast/rules/composite/fixtures/sekkr_pvol_20170203T010000Z.h5
new file mode 100644 (file)
index 0000000..b7a66ca
Binary files /dev/null and b/itest/eu/baltrad/beast/rules/composite/fixtures/sekkr_pvol_20170203T010000Z.h5 differ
diff --git a/itest/eu/baltrad/beast/rules/composite/fixtures/sevil_pvol_20170203T010000Z.h5 b/itest/eu/baltrad/beast/rules/composite/fixtures/sevil_pvol_20170203T010000Z.h5
new file mode 100644 (file)
index 0000000..a267e6d
Binary files /dev/null and b/itest/eu/baltrad/beast/rules/composite/fixtures/sevil_pvol_20170203T010000Z.h5 differ
diff --git a/itest/eu/baltrad/beast/rules/composite/fixtures/sevil_pvol_20170203T010500Z.h5 b/itest/eu/baltrad/beast/rules/composite/fixtures/sevil_pvol_20170203T010500Z.h5
new file mode 100644 (file)
index 0000000..7ac267a
Binary files /dev/null and b/itest/eu/baltrad/beast/rules/composite/fixtures/sevil_pvol_20170203T010500Z.h5 differ
diff --git a/itest/eu/baltrad/beast/rules/composite/fixtures/sevil_pvol_no05angle_20170203T010500Z.h5 b/itest/eu/baltrad/beast/rules/composite/fixtures/sevil_pvol_no05angle_20170203T010500Z.h5
new file mode 100644 (file)
index 0000000..5ed7b66
Binary files /dev/null and b/itest/eu/baltrad/beast/rules/composite/fixtures/sevil_pvol_no05angle_20170203T010500Z.h5 differ
index 4ac0cc0..46e8232 100644 (file)
@@ -18,8 +18,6 @@ along with the Beast library library.  If not, see <http://www.gnu.org/licenses/
 ------------------------------------------------------------------------*/
 package eu.baltrad.beast.rules.volume;
 
-import static org.junit.Assert.assertEquals;
-
 import java.io.File;
 import java.io.FileInputStream;
 import java.util.ArrayList;
@@ -28,21 +26,18 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-import junit.framework.TestCase;
-
 import org.springframework.context.support.AbstractApplicationContext;
 
 import eu.baltrad.bdb.db.FileEntry;
-import eu.baltrad.bdb.expr.Expression;
 import eu.baltrad.beast.db.AttributeFilter;
 import eu.baltrad.beast.db.Catalog;
-import eu.baltrad.beast.db.IFilter;
 import eu.baltrad.beast.itest.BeastDBTestHelper;
 import eu.baltrad.beast.message.IBltMessage;
 import eu.baltrad.beast.message.mo.BltDataMessage;
 import eu.baltrad.beast.message.mo.BltGenerateMessage;
 import eu.baltrad.beast.rules.timer.TimeoutManager;
 import eu.baltrad.beast.rules.util.IRuleUtilities;
+import junit.framework.TestCase;
 
 /**
  * @author Anders Henja
diff --git a/src/eu/baltrad/beast/db/filters/CompositingRuleFilter.java b/src/eu/baltrad/beast/db/filters/CompositingRuleFilter.java
new file mode 100644 (file)
index 0000000..be792bd
--- /dev/null
@@ -0,0 +1,322 @@
+/* --------------------------------------------------------------------
+Copyright (C) 2009-2010 Swedish Meteorological and Hydrological Institute, SMHI,
+
+This file is part of the Beast library.
+
+Beast library is free software: you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Beast library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with the Beast library library.  If not, see <http://www.gnu.org/licenses/>.
+------------------------------------------------------------------------*/
+package eu.baltrad.beast.db.filters;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import eu.baltrad.bdb.db.FileEntry;
+import eu.baltrad.bdb.db.FileQuery;
+import eu.baltrad.bdb.expr.Expression;
+import eu.baltrad.bdb.expr.ExpressionFactory;
+import eu.baltrad.bdb.oh5.MetadataMatcher;
+import eu.baltrad.bdb.util.DateTime;
+
+import eu.baltrad.beast.db.ICatalogFilter;
+import eu.baltrad.beast.db.IFilter;
+
+/**
+ * A filter for collecting and matching files for a specific compositing 
+ * rule, within a time period. 
+ * 
+ * The filter can either be setup to be scan-based or volume-based, just 
+ * as the compositing rule. If scan-based, it will match files with "what/object" 
+ * set to "SCAN". Otherwise it will match files with "what/object" set to 
+ * "PVOL".
+ * 
+ * The filter can both be used to check if specific files match the filter 
+ * (by using method fileMatches(FileEntry)) and for getting a FileQuery 
+ * to use towards the database, to get all files matching the parameters of
+ * the filter.
+ * 
+ * When the filter is use to query the database, the returned files will be 
+ * sorted on time ("what/date" + "what/time"), with the oldest file first.
+ * 
+ * If the filter is set to be scan-based, the files will also be sorted on angle,  
+ * with the lowest angle ("where/elangle") first, when applied in a query for the 
+ * database, if there are several matches for the other parameters of the 
+ * filter.
+ * 
+ * The filter can also be provided with a list of sources. The filter will 
+ * then only match/return files that that have a source id matching any 
+ * of the sources in the list.
+ * 
+ * It is also possible to define a start - stop date/time interval. If so, 
+ * it will only fetch files within this time period, if used for querying the 
+ * database. You can also ignore specifying start and/or
+ * stop date/time but if you ignore both you probably want to use
+ * a different filter.
+ * 
+ * 
+ * @author Mats Vernersson
+ */
+public class CompositingRuleFilter implements ICatalogFilter {
+  /**
+   * The object type
+   */
+  private String objectType = null;
+  
+  /**
+   * The source node ids
+   */
+  private List<String> sources = null;
+  
+  /**
+   * The start date time
+   */
+  private DateTime startDateTime = null;
+  
+  /**
+   * The stop date time
+   */
+  private DateTime stopDateTime = null;
+  
+  /**
+   * The quantity to look for
+   */
+  private String quantity = null;
+  
+  /**
+   * Indicates if the filter is to be used for a scanBased compositing rule or not. If true, 
+   * filter will match objects with what/object=SCAN, otherwise what/object=PVOL
+   */
+  private boolean scanBased = false;
+  
+  /**
+   * Expression factory to use for generating expressions in the filter
+   */
+  private ExpressionFactory xprFactory = null;
+  
+  /**
+   * Matcher to use for comparing metadata in files against the filter settings
+   */
+  private MetadataMatcher matcher = null;
+  
+  /**
+   * Additional filter (in addition to the default filtering created for the compositing rule) 
+   * that will be applied to check in file matching.
+   * 
+   */
+  private IFilter additionalFileFilter = null;
+  
+  /**
+   * Constructor for the filter
+   * 
+   * @param scanBased             Indicates if the filter is to be used for a scanBased compositing rule or not.
+   * @param quantity              The quantity to look for in files
+   * @param sources               The source node ids
+   * @param startDateTime         A datetime object describing the start of the period in which to look for files
+   * @param stopDateTime          A datetime object describing the end of the period in which to look for files
+   * @param additionalFileFilter  Rule specific filter, to be applied when matching files. Can be set to null if 
+   *                              no additional filtering shall be applied.
+   */
+  public CompositingRuleFilter(boolean scanBased, String quantity, List<String> sources, DateTime startDateTime, DateTime stopDateTime, IFilter additionalFileFilter) {
+
+    this.scanBased = scanBased;
+    if (scanBased) {
+      this.objectType = "SCAN";      
+    } else {
+      this.objectType = "PVOL";
+    }
+    
+    this.quantity = quantity;
+    this.sources = sources;
+    this.startDateTime = startDateTime;
+    this.stopDateTime = stopDateTime;
+    
+    this.matcher = new MetadataMatcher();
+    this.xprFactory = new ExpressionFactory();
+    
+    this.additionalFileFilter = additionalFileFilter;
+  }
+  
+  /**
+   * @see eu.baltrad.beast.db.ICatalogFilter#apply(eu.baltrad.bdb.db.FileQuery)
+   */
+  @Override
+  public void apply(FileQuery query) {
+    Expression filterExpression = getFilterExpression(true);
+    query.setFilter(filterExpression);
+    
+    if (scanBased) {
+      query.appendOrderClause(xprFactory.asc(xprFactory.attribute("where/elangle")));
+    }
+    
+    query.appendOrderClause(xprFactory.asc(getDateTimeAttribute()));
+  }
+  
+  /**
+   * Checks if the metadata of a file matches the settings of the filter. 
+   * 
+   * @param file to check against the filter
+   * @return True if the file matches the filter. False otherwise.
+   */
+  public boolean fileMatches(FileEntry file) {
+    Expression filterExpression = getFilterExpression(false);
+    return matcher.match(file.getMetadata(), filterExpression);
+  }
+  
+  protected Expression getFilterExpression(boolean dbQuery) {
+    Expression datetimeAttr = getDateTimeAttribute();
+
+    List<Expression> filters = new ArrayList<Expression>();
+    filters.add(xprFactory.eq(xprFactory.attribute("what/object"), xprFactory.literal(objectType)));
+    
+    if (sources != null) {
+      List<Expression> sourceList = new ArrayList<Expression>();
+      for (String source : sources) {
+        sourceList.add(xprFactory.literal(source));
+      }
+      filters.add(xprFactory.in(getSourceAttribute(), xprFactory.list(sourceList)));
+    }
+
+    if (dbQuery) {
+      if (startDateTime != null) {
+        filters.add(xprFactory.ge(datetimeAttr, xprFactory.literal(startDateTime)));
+      }
+      if (stopDateTime != null) {
+        filters.add(xprFactory.lt(datetimeAttr, xprFactory.literal(stopDateTime)));
+      }
+    } else {    
+      if (additionalFileFilter != null) {
+        filters.add(additionalFileFilter.getExpression());
+      }
+    }
+    
+    if (quantity != null) {
+      filters.add(xprFactory.eq(xprFactory.attribute("what/quantity"), xprFactory.literal(quantity)));
+    }
+    
+    return xprFactory.and(filters);
+  }
+  
+  protected Expression getDateTimeAttribute() {
+    return xprFactory.combinedDateTime("what/date", "what/time");
+  }
+  
+  protected Expression getSourceAttribute() {
+    return xprFactory.attribute("_bdb/source_name");
+  }
+  
+  /**
+   * Sets the start date time
+   * @param dt the date time to set
+   */
+  public void setStartDateTime(DateTime dt) {
+    this.startDateTime = dt;
+  }
+  
+  /**
+   * Sets the stop date time
+   * @param dt the date time to set
+   */
+  public void setStopDateTime(DateTime dt) {
+    this.stopDateTime = dt;
+  }
+  
+  /**
+   * @return the start date
+   */
+  public DateTime getStartDateTime() {
+    return this.startDateTime;
+  }
+  
+  /**
+   * @return the stop date
+   */
+  public DateTime getStopDateTime() {
+    return this.stopDateTime;
+  }
+
+  /**
+   * Sets the object to search for
+   * @param object the object to set
+   */
+  public void setObject(String object) {
+    this.objectType = object;
+  }
+  
+  /**
+   * @return the object type
+   */
+  public String getObject() {
+    return this.objectType;
+  }
+  
+  /**
+   * Sets a list of source names that the filter will match against
+   * @param object the object to set
+   */
+  public void setSources(List<String> sources) {
+    this.sources = new ArrayList<String>(sources);
+  }
+  
+  /**
+   * @return the list of source names
+   */
+  public List<String> getSources() {
+    return this.sources;
+  }
+
+  /**
+   * @return the quantity
+   */
+  public String getQuantity() {
+    return quantity;
+  }
+
+  /**
+   * @param quantity the quantity to set
+   */
+  public void setQuantity(String quantity) {
+    this.quantity = quantity;
+  }
+
+  /**
+   * @return expression factory
+   */
+  public ExpressionFactory getXprFactory() {
+    return xprFactory;
+  }
+
+  /**
+   * Sets the expression factory to use
+   * 
+   * @param xprFactory
+   */
+  public void setXprFactory(ExpressionFactory xprFactory) {
+    this.xprFactory = xprFactory;
+  }
+
+  /**
+   * @return matcher used in the filter
+   */
+  public MetadataMatcher getMatcher() {
+    return matcher;
+  }
+
+  /**
+   * Sets the matcher to use for matching files against the filter.
+   * 
+   * @param matcher
+   */
+  public void setMatcher(MetadataMatcher matcher) {
+    this.matcher = matcher;
+  }
+}
index 506271b..5e589a2 100644 (file)
@@ -18,6 +18,7 @@ along with the Beast library library.  If not, see <http://www.gnu.org/licenses/
 ------------------------------------------------------------------------*/
 package eu.baltrad.beast.router;
 
+import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.List;
@@ -90,7 +91,12 @@ public class RouteDefinition {
     }
   }
   
-  public static class NameComparator extends RouteComparator {
+  public static class NameComparator extends RouteComparator implements Serializable {
+    /**
+     * 
+     */
+    private static final long serialVersionUID = 8047472457043886897L;
+
     public int doCompare(RouteDefinition route1, RouteDefinition route2) {
       if (route1.getName() == null || route2.getName() == null) {
         return 0;
@@ -100,7 +106,12 @@ public class RouteDefinition {
     }
   }
   
-  public static class DescriptionComparator extends RouteComparator {
+  public static class DescriptionComparator extends RouteComparator implements Serializable {
+    /**
+     * 
+     */
+    private static final long serialVersionUID = 9198396822068447710L;
+
     public int doCompare(RouteDefinition route1, RouteDefinition route2) {
       if (route1.getDescription() == null || route2.getDescription() == null) {
         return 0;
@@ -110,13 +121,23 @@ public class RouteDefinition {
     }
   }
   
-  public static class ActiveComparator extends RouteComparator {
+  public static class ActiveComparator extends RouteComparator implements Serializable {
+    /**
+     * 
+     */
+    private static final long serialVersionUID = 1158483498421166326L;
+
     public int doCompare(RouteDefinition route1, RouteDefinition route2) {
       return (route1.isActive() ==  route2.isActive() ? 0 : (route2.isActive() ? 1 : -1));
     }
   }
   
-  public static class TypeComparator extends RouteComparator {
+  public static class TypeComparator extends RouteComparator implements Serializable {
+    /**
+     * 
+     */
+    private static final long serialVersionUID = 4356175456332530439L;
+
     public int doCompare(RouteDefinition route1, RouteDefinition route2) {
       if (route1.getRuleType() == null || route2.getRuleType() == null) {
         return 0;
index 1f8fb02..13c3c82 100644 (file)
@@ -18,10 +18,13 @@ along with the Beast library library.  If not, see <http://www.gnu.org/licenses/
 ------------------------------------------------------------------------*/
 package eu.baltrad.beast.rules.composite;
 
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 import eu.baltrad.bdb.util.DateTime;
+import eu.baltrad.beast.db.CatalogEntry;
 
 /**
  * Used for keeping track on registered tasks in the timeout manager.
@@ -48,6 +51,8 @@ public class CompositeTimerData {
    */
   private Map<String, Double> prevAngles = new HashMap<String, Double>();
   
+  private List<String> previousSources = null;
+  
   /**
    * Constructor
    * @param dt the date time
@@ -67,9 +72,10 @@ public class CompositeTimerData {
    * @param dt the date time
    * @param scanBased if scan based composite or not
    */
-  public CompositeTimerData(int ruleid, DateTime dt, boolean scanBased) {
+  public CompositeTimerData(int ruleid, DateTime dt, boolean scanBased, List<String> sources) {
     this(ruleid, dt);
     this.scanBased = scanBased;
+    this.previousSources = sources;
   }
   
   /**
@@ -134,4 +140,26 @@ public class CompositeTimerData {
   public Map<String, Double> getPreviousAngles() {
     return prevAngles;
   }
+  
+  public void setPreviousEntries(Map<String,CatalogEntry> entries) {
+    ArrayList<String> sources = new ArrayList<String>(entries.keySet());
+    setPreviousSources(sources);
+    
+    if (isScanBased()) {
+      for (String source : sources) {
+        CatalogEntry entry = entries.get(source);
+        Double elangle = (Double)entry.getAttribute("/dataset1/where/elangle");
+        prevAngles.put(source, elangle);
+      }
+    }
+    
+  }
+  
+  public void setPreviousSources(List<String> sources) {
+    this.previousSources = sources;
+  }
+  
+  public List<String> getPreviousSources() {
+    return previousSources;
+  }
 }
index ae628f7..19daebf 100644 (file)
@@ -19,6 +19,7 @@ along with the Beast library library.  If not, see <http://www.gnu.org/licenses/
 package eu.baltrad.beast.rules.composite;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -30,15 +31,14 @@ import org.springframework.beans.factory.InitializingBean;
 import org.springframework.util.StringUtils;
 
 import eu.baltrad.bdb.db.FileEntry;
-import eu.baltrad.bdb.expr.ExpressionFactory;
-import eu.baltrad.bdb.oh5.MetadataMatcher;
+import eu.baltrad.bdb.oh5.Metadata;
 import eu.baltrad.bdb.util.Date;
 import eu.baltrad.bdb.util.DateTime;
 import eu.baltrad.bdb.util.Time;
 import eu.baltrad.beast.db.Catalog;
 import eu.baltrad.beast.db.CatalogEntry;
-import eu.baltrad.beast.db.filters.LowestAngleFilter;
-import eu.baltrad.beast.db.filters.TimeIntervalFilter;
+import eu.baltrad.beast.db.IFilter;
+import eu.baltrad.beast.db.filters.CompositingRuleFilter;
 import eu.baltrad.beast.message.IBltMessage;
 import eu.baltrad.beast.message.mo.BltDataMessage;
 import eu.baltrad.beast.message.mo.BltGenerateMessage;
@@ -232,16 +232,11 @@ public class CompositingRule implements IRule, ITimeoutRule, InitializingBean {
    * for generating timeout message
    */
   private List<String> recipients = new ArrayList<String>();
-
-  /**
-   * The metadata matcher
-   */
-  private MetadataMatcher matcher = null;
   
   /**
-   * The expression factory
+   * The filter used for matching files
    */
-  private ExpressionFactory xpr = null;
+  private IFilter filter = null;
   
   /**
    * The logger
@@ -252,8 +247,7 @@ public class CompositingRule implements IRule, ITimeoutRule, InitializingBean {
    * Default constructor,however, use manager for creation
    */
   protected CompositingRule() {
-    matcher = new MetadataMatcher();
-    xpr = new ExpressionFactory();
+    //
   }
   
   /**
@@ -416,133 +410,73 @@ public class CompositingRule implements IRule, ITimeoutRule, InitializingBean {
       IBltMessage generatedMessage = null;
       if (message instanceof BltDataMessage) {
         FileEntry file = ((BltDataMessage)message).getFileEntry();
-        String object = file.getMetadata().getWhatObject();
-        
-        boolean handleScan = false;
-        boolean handlePvol = false;
-        if (object != null && object.equals("SCAN") && isScanBased()) {
-          handleScan = true;
-        } else if (object != null && object.equals("PVOL") && !isScanBased()) {
-          handlePvol = true;
-        }
-        
-        if (handleScan || handlePvol) {
+        CompositingRuleFilter ruleFilter = createFilter(getNominalTimeFromFile(file));
+        if (ruleFilter.fileMatches(file)) {
           logger.info("ENTER: execute ruleId: " + getRuleId() + ", thread: " + Thread.currentThread().getName() + 
               ", file: " + file.getUuid());
           
-          if (handleScan) {
-            generatedMessage = handleCompositeFromScans(message);
-          } else if (handlePvol) {
-            generatedMessage = handleCompositeFromVolume(message);
-          }
+          generatedMessage = createComposite(message, ruleFilter);
           
           logger.info("EXIT: execute ruleId: " + getRuleId() + ", thread: " + Thread.currentThread().getName() + 
-              ", file: " + file.getUuid());          
+              ", file: " + file.getUuid()); 
         }
-        
       }
       return generatedMessage;
     } finally {
       logger.debug("EXIT: handle(IBltMessage)");
     }
   }
-
-  /**
-   * Handles generation of a composite from a number of scans
-   * @param message the @ref eu.baltrad.beast.message.mo.BltDataMessage containing a file scan 
-   * @return a message or null if criterias not have been met.
-   */
-  protected IBltMessage handleCompositeFromScans(IBltMessage message) {
-    logger.debug("ENTER: handleCompositeFromScans(IBltMessage)");
-
+  
+  protected IBltMessage createComposite(IBltMessage message, CompositingRuleFilter ruleFilter) {
     IBltMessage result = null;
-    Map<String, Double> prevAngles = null;
+
     CompositeTimerData data = createTimerData(message);
-    if (data != null) {
-      TimeoutTask tt = timeoutManager.getRegisteredTask(data);
-      if (tt == null) {
-        DateTime prevDateTime = ruleUtil.createPrevNominalTime(data.getDateTime(), interval);
-        prevAngles = ruleUtil.fetchLowestSourceElevationAngle(prevDateTime, data.getDateTime(), sources, quantity);
-        data.setPreviousAngles(prevAngles);
-      } else {
-        data = (CompositeTimerData)tt.getData();
-      }
+    if (data == null) {
+      return null;
+    }
     
-      result = createCompositeScanMessage(data);
-      if (result != null) {
-        ruleUtil.trigger(ruleid, data.getDateTime());
-        if (tt != null) {
-          timeoutManager.unregister(tt.getId());
-        }
-      } else {
-        if (tt == null && timeout > 0) {
-          timeoutManager.register(this, ruleUtil.getTimeoutTime(data.getDateTime(), nominalTimeout, timeout*1000), data);
-        }
+    TimeoutTask timeoutTask = timeoutManager.getRegisteredTask(data);
+    if (timeoutTask == null) {
+      Map<String, CatalogEntry> previousEntries = fetchPreviousEntriesMap(ruleFilter);
+      if (previousEntries.size() > 0) {
+        data.setPreviousEntries(previousEntries);        
       }
+    } else {
+      data = (CompositeTimerData)timeoutTask.getData();
     }
-    logger.debug("EXIT: handleCompositeFromScans(IBltMessage)");
-    return result;
-  }
-  
-  /**
-   * Creates a composite scan message if criterias are met.
-   * @param data the composite timer data
-   * @return a message if criterias are met, otherwise null
-   */
-  protected IBltMessage createCompositeScanMessage(CompositeTimerData data) {
-    logger.debug("ENTER: createCompositeScanMessage(CompositeTimerData)");
-    try {
-      DateTime nextTime = ruleUtil.createNextNominalTime(data.getDateTime(), interval);
-      Map<String, Double> currAngles = ruleUtil.fetchLowestSourceElevationAngle(data.getDateTime(), nextTime, sources, quantity);
-      Map<String, Double> prevAngles = data.getPreviousAngles();
     
-      for (String src : sources) {
-        Double pelangle = prevAngles.get(src);
-        Double elangle = currAngles.get(src);
-        if (pelangle != null && elangle != null) {
-          if (pelangle.compareTo(elangle) < 0) {
-            return null;
-          }
-        } else {
-          return null;
+    Map<String,CatalogEntry> currentEntries = fetchEntriesMap(ruleFilter);
+    
+    boolean allSourcesPresent = true;
+    for (String src : data.getPreviousSources()) {
+      if (!currentEntries.containsKey(src)) {
+        allSourcesPresent = false;
+        break;
+      } else if (isScanBased()) {
+        // for scans, we check also that the lowest scan is received
+        CatalogEntry currentEntry = currentEntries.get(src);
+        Double currentElangle = (Double)currentEntry.getAttribute("/dataset1/where/elangle");
+        Double previousElangle = data.getPreviousAngles().get(src);
+        
+        if (previousElangle == null || previousElangle.compareTo(currentElangle) < 0) {
+          allSourcesPresent = false;
+          break;
         }
       }
-      List<CatalogEntry> entries = fetchScanEntries(data.getDateTime());
-      return createMessage(data.getDateTime(), entries);
-    } finally {
-      logger.debug("EXIT: createCompositeScanMessage(CompositeTimerData)");
     }
-  }
-
-  /**
-   * Determines if a composite should be generated from a number
-   * of volumes or not.
-   * @param message the @ref eu.baltrad.beast.message.mo.BltDataMessage containing a file volume
-   * @return the message or null if criterias not have been met.
-   */
-  protected IBltMessage handleCompositeFromVolume(IBltMessage message) {
-    logger.debug("ENTER: handleCompositeFromVolume(IBltMessage)");
-
-    IBltMessage result = null;
-    CompositeTimerData data = createTimerData(message);
-    if (data != null) {
-      List<CatalogEntry> entries = fetchEntries(data.getDateTime());
-      TimeoutTask tt = timeoutManager.getRegisteredTask(data);
-      if (areCriteriasMet(entries)) {
-        result = createMessage(data.getDateTime(), entries);
-        ruleUtil.trigger(ruleid, data.getDateTime());
-        if (tt != null) {
-          timeoutManager.unregister(tt.getId());
-        }
-      } else {
-        if (tt == null) {
-          if (timeout > 0) {
-            timeoutManager.register(this, ruleUtil.getTimeoutTime(data.getDateTime(), nominalTimeout, timeout*1000), data);
-          }
-        }
+    
+    if (allSourcesPresent) {
+      result = createMessage(data.getDateTime(), currentEntries);
+      ruleUtil.trigger(ruleid, data.getDateTime());
+      if (timeoutTask != null) {
+        timeoutManager.unregister(timeoutTask.getId());
+      }
+    } else {
+      if (timeoutTask == null && timeout > 0) {
+        timeoutManager.register(this, ruleUtil.getTimeoutTime(data.getDateTime(), nominalTimeout, timeout*1000), data);
       }
     }
-    logger.debug("EXIT: handleCompositeFromVolume(IBltMessage)");
+
     return result;
   }
   
@@ -555,13 +489,9 @@ public class CompositingRule implements IRule, ITimeoutRule, InitializingBean {
     IBltMessage result = null;
     CompositeTimerData ctd = (CompositeTimerData)data;
     if (ctd != null) {
-      List<CatalogEntry> entries = null;
-      
-      if (ctd.isScanBased()) {
-        entries = fetchScanEntries(ctd.getDateTime());
-      } else {
-        entries = fetchEntries(ctd.getDateTime());
-      }
+      CompositingRuleFilter ruleFilter = createFilter(ctd.getDateTime());
+      Map<String, CatalogEntry> entries = fetchEntriesMap(ruleFilter);
+
       if (!ruleUtil.isTriggered(ruleid, ctd.getDateTime())) {
         IBltMessage msgtosend = createMessage(ctd.getDateTime(), entries);
         BltMultiRoutedMessage mrmsg = new BltMultiRoutedMessage();
@@ -584,59 +514,39 @@ public class CompositingRule implements IRule, ITimeoutRule, InitializingBean {
     CompositeTimerData result = null;
     if (message instanceof BltDataMessage) {
       FileEntry file = ((BltDataMessage)message).getFileEntry();
-      String object = file.getMetadata().getWhatObject();
-      Time t = file.getMetadata().getWhatTime();
-      Date d = file.getMetadata().getWhatDate();
-      DateTime nominalTime = ruleUtil.createNominalTime(d, t, interval);
-      if (quantity != null && !quantity.equals("")) {
-        if (!matcher.match(file.getMetadata(), xpr.eq(xpr.attribute("what/quantity"), xpr.literal(quantity)))) {
-          return null;
-        }
-      }
+      DateTime nominalTime = getNominalTimeFromFile(file);
+
       if (!ruleUtil.isTriggered(ruleid, nominalTime)) {
-        if (!isScanBased() && object.equals("PVOL")) {
-          result = new CompositeTimerData(ruleid, nominalTime);
-        } else if (isScanBased() && object.equals("SCAN")) {
-          result = new CompositeTimerData(ruleid, nominalTime, true);
-        }
+        result = new CompositeTimerData(ruleid, nominalTime, isScanBased(), sources);
       }
     }
     
     return result;
   }
   
-  /**
-   * Verifies if the criterias has been met so that we can create
-   * the message.
-   * @param entries a list of catalog entries
-   * @return true if the criterias has been met.
-   */
-  protected boolean areCriteriasMet(List<CatalogEntry> entries) {
-    List<String> es = ruleUtil.getSourcesFromEntries(entries);
-    for (String s : sources) {
-      if (!es.contains(s)) {
-        return false;
-      }
-    }
-    return true;
+  protected DateTime getNominalTimeFromFile(FileEntry file) {
+    Metadata metaData = file.getMetadata();
+    Time t = metaData.getWhatTime();
+    Date d = metaData.getWhatDate();
+    return ruleUtil.createNominalTime(d, t, interval);
   }
 
   /**
    * Creates a message if all nessecary entries are there
    * @param nominalDT the nominal time
-   * @param entries the list of entries
+   * @param entriesMap the list of entries
    * @return a message if criterias are fullfilled, otherwise null
    */
-  protected IBltMessage createMessage(DateTime nominalDT, List<CatalogEntry> entries) {
+  protected IBltMessage createMessage(DateTime nominalDT, Map<String,CatalogEntry> entriesMap) {
     BltGenerateMessage result = new BltGenerateMessage();
     Date date = nominalDT.getDate();
     Time time = nominalDT.getTime();
     
+    List<CatalogEntry> entries = new ArrayList<CatalogEntry>(entriesMap.values());
+    
     result.setAlgorithm("eu.baltrad.beast.GenerateComposite");
-    entries = ruleUtil.getEntriesByClosestTime(nominalDT, entries);
-    entries = ruleUtil.getEntriesBySources(sources, entries);
     List<String> uuids = ruleUtil.getUuidStringsFromEntries(entries);
-    List<String> usedSources = ruleUtil.getSourcesFromEntries(entries);
+    List<String> usedSources = new ArrayList<String>(entriesMap.keySet());
     ruleUtil.reportRadarSourceUsage(sources, usedSources);
     
     result.setFiles(uuids.toArray(new String[0]));
@@ -692,72 +602,46 @@ public class CompositingRule implements IRule, ITimeoutRule, InitializingBean {
 
     return result;
   }
-  
-  /**
-   * Fetches the entries that are from the nominal time up until 
-   * the stop time defined by the interval.
-   * @param nominalTime the nominal time
-   * @return a list of entries
-   */
-  protected List<CatalogEntry> fetchEntries(DateTime nominalTime) {
-    TimeIntervalFilter filter = createFilter(nominalTime);
-    List<CatalogEntry> entries = catalog.fetch(filter);
-    return entries;
-  }
 
   /**
    * Returns a filter
    * @param nominalDT the nominal time
    * @return a TimeIntervalFilter for polar volumes  
    */
-  protected TimeIntervalFilter createFilter(DateTime nominalDT) {
-    TimeIntervalFilter filter = new TimeIntervalFilter();
-    filter.setObject("PVOL");
-    if (quantity != null && !quantity.equals("")) {
-      filter.setQuantity(quantity);
-    }
+  protected CompositingRuleFilter createFilter(DateTime nominalDT) {
     DateTime stopDT = ruleUtil.createNextNominalTime(nominalDT, interval);
-    filter.setStartDateTime(nominalDT);
-    filter.setStopDateTime(stopDT);
-    return filter;
+    
+    CompositingRuleFilter ruleFilter = 
+        new CompositingRuleFilter(isScanBased(), quantity, sources, nominalDT, stopDT, filter);
+    
+    return ruleFilter;
   }
   
-  /**
-   * Fetches lowest sweep scan for sources between nomialDT and
-   * nominalDT + interval
-   * @param nominalDT the nominal time
-   * @return list of entries
-   */
-  protected List<CatalogEntry> fetchScanEntries(DateTime nominalDT) {
-    List<CatalogEntry> result = new ArrayList<CatalogEntry>();
-    LowestAngleFilter filter = createScanFilter(nominalDT);
-    for (String src : sources) {
-      filter.setSource(src);
-      List<CatalogEntry> entries = catalog.fetch(filter);
-      result.addAll(entries);
-    }
-    return result;
-  }
-
-  protected LowestAngleFilter createScanFilter(DateTime nominalDT) {
-    LowestAngleFilter filter = new LowestAngleFilter();
-    if (quantity != null && !quantity.equals("")) {
-      filter.setQuantity(quantity);
-    }
+  protected Map<String,CatalogEntry> fetchPreviousEntriesMap(CompositingRuleFilter ruleFilter) {
+    DateTime previousStartDT = ruleUtil.createPrevNominalTime(ruleFilter.getStartDateTime(), interval);
+    CompositingRuleFilter previousFilter = createFilter(previousStartDT);
     
-    filter.setStart(nominalDT);
-    DateTime stopDT = ruleUtil.createNextNominalTime(nominalDT, interval);
-    filter.setStop(stopDT);
-    filter.setClosestToStartDate(true);
-    return filter;
+    return fetchEntriesMap(previousFilter);
   }
-
-  protected List<String> getUuidsFromEntries(List<CatalogEntry> entries) {
-    List<String> uuids = new ArrayList<String>(entries.size());
-    for (CatalogEntry e: entries) {
-      uuids.add(e.getUuid());
+  
+  protected Map<String,CatalogEntry> fetchEntriesMap(CompositingRuleFilter ruleFilter) {
+    Map<String,CatalogEntry> result = new HashMap<String, CatalogEntry>();
+    
+    List<CatalogEntry> entries = catalog.fetch(ruleFilter);
+    
+    List<String> remainingSources = new ArrayList<String>(sources);
+    
+    for (CatalogEntry entry : entries) {
+      String src = entry.getSource();
+      if (remainingSources.contains(src)) {
+        if (ruleFilter.fileMatches(entry.getFileEntry())) {
+          result.put(src, entry);
+          remainingSources.remove(src);          
+        }
+      }
     }
-    return uuids;
+
+    return result;
   }
   
   /**
@@ -941,14 +825,6 @@ public class CompositingRule implements IRule, ITimeoutRule, InitializingBean {
     this.quantity = quantity;
   }
 
-  /**
-   * For test purspose
-   * @param matcher the matcher
-   */
-  protected void setMetadataMatcher(MetadataMatcher matcher) {
-    this.matcher = matcher;
-  }
-
   public boolean isNominalTimeout() {
     return nominalTimeout;
   }
@@ -973,4 +849,12 @@ public class CompositingRule implements IRule, ITimeoutRule, InitializingBean {
   public void setQualityControlMode(int qualityControlMode) {
     this.qualityControlMode = qualityControlMode;
   }
+  
+  public IFilter getFilter() {
+    return filter;
+  }
+
+  public void setFilter(IFilter filter) {
+    this.filter = filter;
+  }
 }
index 26d00ae..de9ee70 100644 (file)
@@ -20,14 +20,18 @@ package eu.baltrad.beast.rules.composite;
 
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import org.springframework.jdbc.core.JdbcOperations;
 import org.springframework.jdbc.core.RowMapper;
 
 import eu.baltrad.beast.db.Catalog;
+import eu.baltrad.beast.db.IFilter;
 import eu.baltrad.beast.rules.IRule;
 import eu.baltrad.beast.rules.IRuleManager;
+import eu.baltrad.beast.rules.RuleFilterManager;
 import eu.baltrad.beast.rules.timer.TimeoutManager;
 import eu.baltrad.beast.rules.util.IRuleUtilities;
 
@@ -57,6 +61,11 @@ public class CompositingRuleManager implements IRuleManager {
   private TimeoutManager timeoutManager = null;
   
   /**
+   * filter manager
+   */
+  private RuleFilterManager filterManager;
+  
+  /**
    * @param template the jdbc template to set
    */
   public void setJdbcTemplate(JdbcOperations template) {
@@ -91,6 +100,7 @@ public class CompositingRuleManager implements IRuleManager {
   public void delete(int ruleId) {
     storeSources(ruleId, null);
     storeDetectors(ruleId, null);
+    storeFilter(ruleId, null);
     template.update("delete from beast_composite_rules where rule_id=?",
         new Object[]{ruleId});
   }
@@ -100,10 +110,12 @@ public class CompositingRuleManager implements IRuleManager {
    */
   @Override
   public IRule load(int ruleId) {
-    return template.queryForObject(
+    CompositingRule rule = template.queryForObject(
         "select * from beast_composite_rules where rule_id=?",
         getCompsiteRuleMapper(),
         new Object[]{ruleId});
+    rule.setFilter(loadFilter(ruleId));
+    return rule;
   }
 
   /**
@@ -134,6 +146,7 @@ public class CompositingRuleManager implements IRuleManager {
         " values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", new Object[]{ruleId, area, interval, timeout, byscan, selection_method, method, prodpar, applygra, ZR_A, ZR_b, ignoreMalfunc, ctfilter, qitotalField, quantity, nominal_timeout, qualityControlMode});
     storeSources(ruleId, crule.getSources());
     storeDetectors(ruleId, crule.getDetectors());
+    storeFilter(ruleId, crule.getFilter());
     crule.setRuleId(ruleId);
   }
 
@@ -148,6 +161,7 @@ public class CompositingRuleManager implements IRuleManager {
         new Object[]{crule.getArea(), crule.getInterval(), crule.getTimeout(), crule.isScanBased(), crule.getSelectionMethod(), crule.getMethod(), crule.getProdpar(), crule.isApplyGRA(), crule.getZR_A(), crule.getZR_b(), crule.isIgnoreMalfunc(), crule.isCtFilter(), crule.getQitotalField(), crule.getQuantity(), crule.isNominalTimeout(), crule.getQualityControlMode(), ruleId});
     storeSources(ruleId, crule.getSources());
     storeDetectors(ruleId, crule.getDetectors());
+    storeFilter(ruleId, crule.getFilter());
     crule.setRuleId(ruleId);
   }
   
@@ -195,6 +209,35 @@ public class CompositingRuleManager implements IRuleManager {
       }
     }    
   }
+  
+  /**
+   * Stores the associated filter
+   * @param rule_id the rule_id
+   * @param filter the filter to store
+   */
+  protected void storeFilter(int rule_id, IFilter filter) {
+    if (filter != null) {
+      Map<String, IFilter> filters = new HashMap<String, IFilter>();
+      filters.put("match", filter);
+      filterManager.storeFilters(rule_id, filters);
+    } else {
+      filterManager.deleteFilters(rule_id);
+    }
+  }
+  
+  /**
+   * Loads the filter for the rule
+   * @param rule_id the rule
+   * @return the filter if any otherwise null
+   */
+  protected IFilter loadFilter(int rule_id) {
+    IFilter result = null;
+    Map<String, IFilter> filters = filterManager.loadFilters(rule_id);
+    if (filters.containsKey("match")) {
+      result = filters.get("match");
+    }
+    return result;
+  }
  
   /**
    * Returns a list of sources connected to the rule_id
@@ -276,4 +319,19 @@ public class CompositingRuleManager implements IRuleManager {
     result.afterPropertiesSet();
     return result;
   }
+  
+  /**
+   * @return the filter manager
+   */
+  public RuleFilterManager getFilterManager() {
+    return filterManager;
+  }
+
+  /**
+   * @param filterManager the filter manager
+   */
+  public void setFilterManager(RuleFilterManager filterManager) {
+    this.filterManager = filterManager;
+  }
+  
 }
diff --git a/test/eu/baltrad/beast/db/filters/CompositingRuleFilterTest.java b/test/eu/baltrad/beast/db/filters/CompositingRuleFilterTest.java
new file mode 100644 (file)
index 0000000..521cf40
--- /dev/null
@@ -0,0 +1,262 @@
+/* --------------------------------------------------------------------
+Copyright (C) 2009-2010 Swedish Meteorological and Hydrological Institute, SMHI,
+
+This file is part of the Beast library.
+
+Beast library is free software: you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Beast library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with the Beast library library.  If not, see <http://www.gnu.org/licenses/>.
+------------------------------------------------------------------------*/
+package eu.baltrad.beast.db.filters;
+
+import static org.easymock.EasyMock.expect;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+
+import org.easymock.Capture;
+import org.easymock.EasyMock;
+import org.easymock.EasyMockSupport;
+import org.junit.Before;
+import org.junit.Test;
+
+import eu.baltrad.bdb.db.FileEntry;
+import eu.baltrad.bdb.db.FileQuery;
+import eu.baltrad.bdb.expr.Expression;
+import eu.baltrad.bdb.expr.ExpressionFactory;
+import eu.baltrad.bdb.expr.StringExpression;
+import eu.baltrad.bdb.oh5.Metadata;
+import eu.baltrad.bdb.oh5.MetadataMatcher;
+import eu.baltrad.bdb.util.DateTime;
+import eu.baltrad.beast.db.IFilter;
+
+
+/**
+ * @author Mats Vernersson, SMHI
+ */
+public class CompositingRuleFilterTest extends EasyMockSupport{
+  
+  private String quantity;
+  private ArrayList<String> sources;
+  private DateTime startDateTime;
+  private DateTime stopDateTime;
+  
+  private static interface ICompositingRuleFilterMethods {
+    public Expression getFilterExpression(boolean dbQuery);
+  };
+  
+  @Before
+  public void setUp() throws Exception {
+    quantity = "DBZH";
+    sources = new ArrayList<String>();
+    sources.add("source1");
+    startDateTime = new DateTime(2016, 4, 15, 10, 15, 0);
+    stopDateTime = new DateTime(2016, 4, 15, 10, 30, 0);
+  }
+  
+  private CompositingRuleFilter createDefaultFilter(boolean isScanBased) {
+    return new CompositingRuleFilter(isScanBased, quantity, sources, startDateTime, stopDateTime, null);
+  }
+
+  @Test
+  public void testConstructorScanBased() {
+    testConstructor(true);
+  }
+  
+  @Test
+  public void testConstructorVolBased() {
+    testConstructor(false);
+  }
+  
+  @Test
+  public void testApplyScanBased() {
+    testApply(true);   
+  }
+  
+  @Test
+  public void testApplyPvolBased() {
+    testApply(false);   
+  }
+  
+  @Test
+  public void testScanFileMatches() {
+    testFileMatches(true, true, false);
+  }
+  
+  @Test
+  public void testScanFileNotMatches() {
+    testFileMatches(true, false, false);
+  }
+  
+  @Test
+  public void testScanFileMatchAdditionalFilter() {
+    testFileMatches(true, true, true);
+  }
+  
+  @Test
+  public void testPvolFileMatches() {
+    testFileMatches(false, true, false);
+  }
+  
+  @Test
+  public void testPvolFileNotMatches() {
+    testFileMatches(false, false, false);
+  }
+  
+  @Test
+  public void testPvolFileMatchAdditionalFilter() {
+    testFileMatches(false, true, true);
+  }
+  
+  private void testConstructor(boolean isScanBased) {
+    CompositingRuleFilter filter = createDefaultFilter(isScanBased);
+    
+    String objectType = "PVOL";
+    if (isScanBased) {
+      objectType = "SCAN";
+    }
+    
+    assertEquals(objectType, filter.getObject());
+    assertEquals(quantity, filter.getQuantity());
+    assertEquals(sources, filter.getSources());
+    assertEquals(startDateTime, filter.getStartDateTime());
+    assertEquals(stopDateTime, filter.getStopDateTime());
+  }
+  
+  private void testApply(boolean isScanBased) {
+
+    final ICompositingRuleFilterMethods methods = createMock(ICompositingRuleFilterMethods.class);
+    
+    FileQuery query = createMock(FileQuery.class);
+    Expression filterExpression = createMock(Expression.class);
+    
+    CompositingRuleFilter classUnderTest = new CompositingRuleFilter(isScanBased, quantity, sources, startDateTime, startDateTime, null) {
+      protected Expression getFilterExpression(boolean dbQuery) {
+        return methods.getFilterExpression(dbQuery);
+      }
+    };
+
+    expect(methods.getFilterExpression(true)).andReturn(filterExpression);
+    
+    Capture<Expression> capturedElangleOrderXpr = new Capture<Expression>();
+    if (isScanBased) {
+      query.appendOrderClause(EasyMock.capture(capturedElangleOrderXpr));      
+    }
+    
+    Capture<Expression> capturedDateOrderXpr = new Capture<Expression>();
+    query.appendOrderClause(EasyMock.capture(capturedDateOrderXpr));
+    
+    query.setFilter(filterExpression);
+
+    replayAll();
+    
+    classUnderTest.apply(query);
+    
+    verifyAll();
+    
+    ExpressionFactory xprFactory = new ExpressionFactory();
+    if (isScanBased) {
+      assertEquals(xprFactory.asc(xprFactory.attribute("where/elangle")), capturedElangleOrderXpr.getValue());      
+    }
+    assertEquals(xprFactory.asc(xprFactory.combinedDateTime("what/date", "what/time")), capturedDateOrderXpr.getValue());
+
+  }
+  
+  private void testFileMatches(boolean isScanBased, boolean fileMatching, boolean useAdditionalFilter) {
+    FileEntry file = createMock(FileEntry.class);
+    Metadata metadata = createMock(Metadata.class);
+    MetadataMatcher matcher = createMock(MetadataMatcher.class);
+    
+    IFilter additionalFilter = null;
+    Expression additionalFilterExpression = null;
+    if (useAdditionalFilter) {
+      additionalFilter = createMock(IFilter.class);
+      additionalFilterExpression = new StringExpression("AdditionalFilterTest");
+    }
+
+    CompositingRuleFilter classUnderTest = new CompositingRuleFilter(isScanBased, quantity, sources, startDateTime, startDateTime, additionalFilter);
+    classUnderTest.setMatcher(matcher);
+
+    expect(file.getMetadata()).andReturn(metadata);
+    
+    Capture<Expression> capturedExpression = new Capture<Expression>();
+    Capture<Metadata> capturedMeta = new Capture<Metadata>();
+    expect(matcher.match(EasyMock.capture(capturedMeta), EasyMock.capture(capturedExpression))).andReturn(fileMatching);
+
+    if (useAdditionalFilter) {
+      expect(additionalFilter.getExpression()).andReturn(additionalFilterExpression);            
+    }
+    
+    replayAll();
+    
+    boolean result = classUnderTest.fileMatches(file);
+    
+    verifyAll();
+    
+    assertEquals(result, fileMatching);
+    assertEquals(capturedMeta.getValue(), metadata);
+    Expression resultExpression = capturedExpression.getValue();
+    assertEquals(resultExpression.getType(), Expression.Type.LIST);
+    assertEquals(resultExpression.get(0).toString(), "and");
+    assertEquals(resultExpression.get(1).getType(), Expression.Type.LIST);
+    assertEquals(resultExpression.get(2).getType(), Expression.Type.LIST);
+    
+    String objectType = "PVOL";
+    if (isScanBased) {
+      objectType = "SCAN";
+    }
+
+    String[] whatObjectStrings = {"what/object", "string", objectType};
+    assertTrue(expressionContainStrings(resultExpression, whatObjectStrings));
+    String[] whatQuantityStrings = {"what/quantity", "string", "DBZH"};
+    assertTrue(expressionContainStrings(resultExpression, whatQuantityStrings));
+    String[] sourceStrings = {"_bdb/source_name", "string", "source1"};
+    assertTrue(expressionContainStrings(resultExpression, sourceStrings));
+    
+    if (useAdditionalFilter) {
+      String[] additionalFilterStrings = {"AdditionalFilterTest"};
+      assertTrue(expressionContainStrings(resultExpression, additionalFilterStrings));
+    }
+
+  }
+  
+  private boolean expressionContainStrings(Expression expressionList, String[] stringsToFind) {
+    int noOfConsequtiveStringsFound = searchExpressionForStrings(expressionList, stringsToFind, 0);
+    return noOfConsequtiveStringsFound == stringsToFind.length;
+  }
+  
+  private int searchExpressionForStrings(Expression expressionList, String[] stringsToFind, int currentStringIndex) {
+    for (Expression expr : expressionList) {
+      if (expr.getType() == Expression.Type.LIST) {
+        currentStringIndex = searchExpressionForStrings(expr, stringsToFind, currentStringIndex);
+      } else if (currentStringIndex > 0) {
+        if (expr.toString().equals(stringsToFind[currentStringIndex])) {
+          currentStringIndex++;
+        } else {
+          return 0;
+        }
+      } else {
+        if (expr.toString().equals(stringsToFind[currentStringIndex])) {
+          currentStringIndex++;
+        }
+      }
+      
+      if (currentStringIndex == stringsToFind.length) {
+        return currentStringIndex;
+      }
+      
+    }
+    return currentStringIndex;
+  }
+
+}
index 83d8fd0..a6e32fe 100644 (file)
@@ -25,6 +25,7 @@ import static org.junit.Assert.assertSame;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 
 import org.easymock.EasyMockSupport;
@@ -35,6 +36,8 @@ import org.springframework.jdbc.core.JdbcOperations;
 import org.springframework.jdbc.core.RowMapper;
 
 import eu.baltrad.beast.db.Catalog;
+import eu.baltrad.beast.db.IFilter;
+import eu.baltrad.beast.rules.RuleFilterManager;
 import eu.baltrad.beast.rules.timer.TimeoutManager;
 import eu.baltrad.beast.rules.util.RuleUtilities;
 
@@ -51,13 +54,16 @@ public class CompositingRuleManagerTest extends EasyMockSupport {
   };
 
   private CompositingRuleManager classUnderTest = null;
+  private RuleFilterManager filterManager = null;
   private JdbcOperations jdbc = null;
   
   @Before
   public void setUp() throws Exception {
     jdbc = createMock(JdbcOperations.class);
+    filterManager = createMock(RuleFilterManager.class);
     classUnderTest = new CompositingRuleManager();
     classUnderTest.setJdbcTemplate(jdbc);
+    classUnderTest.setFilterManager(filterManager);
   }
 
   @After
@@ -79,12 +85,14 @@ public class CompositingRuleManagerTest extends EasyMockSupport {
       }
     };
     classUnderTest.setJdbcTemplate(jdbc);
+    classUnderTest.setFilterManager(filterManager);
     
     methods.storeSources(13, null);
     methods.storeDetectors(13, null);
     
     expect(jdbc.update("delete from beast_composite_rules where rule_id=?",
         new Object[]{13})).andReturn(0);
+    filterManager.deleteFilters(13);
     
     replayAll();
     
@@ -96,6 +104,9 @@ public class CompositingRuleManagerTest extends EasyMockSupport {
   @Test
   public void testLoad() throws Exception {
     CompositingRule rule = new CompositingRule();
+    HashMap<String, IFilter> filters = new HashMap<String, IFilter>();
+    IFilter filter = createMock(IFilter.class);
+    filters.put("match", filter);
     final RowMapper<CompositingRule> mapper = new RowMapper<CompositingRule>() {
       public CompositingRule mapRow(ResultSet arg0, int arg1) throws SQLException {
         return null;
@@ -107,10 +118,12 @@ public class CompositingRuleManagerTest extends EasyMockSupport {
       }
     };
     classUnderTest.setJdbcTemplate(jdbc);
+    classUnderTest.setFilterManager(filterManager);
     
     expect(jdbc.queryForObject("select * from beast_composite_rules where rule_id=?",
         mapper,
         new Object[]{13})).andReturn(rule);
+    expect(filterManager.loadFilters(13)).andReturn(filters);
     
     replayAll();
     
@@ -154,6 +167,7 @@ public class CompositingRuleManagerTest extends EasyMockSupport {
     
     methods.storeSources(13, sources);
     methods.storeDetectors(13, detectors);
+    filterManager.deleteFilters(13);
     
     classUnderTest = new CompositingRuleManager() {
       protected void storeSources(int rule_id, List<String> sources) {
@@ -164,6 +178,7 @@ public class CompositingRuleManagerTest extends EasyMockSupport {
       }
     };
     classUnderTest.setJdbcTemplate(jdbc);
+    classUnderTest.setFilterManager(filterManager);
     
     replayAll();
     
@@ -205,6 +220,7 @@ public class CompositingRuleManagerTest extends EasyMockSupport {
     
     methods.storeSources(13, sources);
     methods.storeDetectors(13, detectors);
+    filterManager.deleteFilters(13);
     
     classUnderTest = new CompositingRuleManager() {
       protected void storeSources(int rule_id, List<String> sources) {
@@ -215,6 +231,7 @@ public class CompositingRuleManagerTest extends EasyMockSupport {
       }
     };
     classUnderTest.setJdbcTemplate(jdbc);
+    classUnderTest.setFilterManager(filterManager);
     
     replayAll();
     
index f4a3d4f..0e601d8 100644 (file)
@@ -28,9 +28,11 @@ import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.UUID;
 
 import org.easymock.EasyMockSupport;
 import org.junit.After;
@@ -39,23 +41,20 @@ import org.junit.Test;
 import org.springframework.beans.factory.BeanInitializationException;
 
 import eu.baltrad.bdb.db.FileEntry;
-import eu.baltrad.bdb.expr.Expression;
-import eu.baltrad.bdb.expr.ExpressionFactory;
 import eu.baltrad.bdb.oh5.Metadata;
-import eu.baltrad.bdb.oh5.MetadataMatcher;
 import eu.baltrad.bdb.util.Date;
 import eu.baltrad.bdb.util.DateTime;
 import eu.baltrad.bdb.util.Time;
 import eu.baltrad.beast.db.Catalog;
 import eu.baltrad.beast.db.CatalogEntry;
-import eu.baltrad.beast.db.filters.LowestAngleFilter;
-import eu.baltrad.beast.db.filters.TimeIntervalFilter;
+import eu.baltrad.beast.db.filters.CompositingRuleFilter;
 import eu.baltrad.beast.message.IBltMessage;
 import eu.baltrad.beast.message.mo.BltDataMessage;
 import eu.baltrad.beast.message.mo.BltGenerateMessage;
 import eu.baltrad.beast.router.IMultiRoutedMessage;
 import eu.baltrad.beast.rules.timer.ITimeoutRule;
 import eu.baltrad.beast.rules.timer.TimeoutManager;
+import eu.baltrad.beast.rules.timer.TimeoutTask;
 import eu.baltrad.beast.rules.util.IRuleUtilities;
 
 /**
@@ -65,20 +64,16 @@ public class CompositingRuleTest extends EasyMockSupport {
   private Catalog catalog = null;
   private IRuleUtilities ruleUtil = null;
   private TimeoutManager timeoutManager = null;
-  private MetadataMatcher matcher = null;
-  private ExpressionFactory xpr = null;
   
   private CompositingRule classUnderTest = null;
   
   private static interface ICompositingMethods {
     public CompositeTimerData createTimerData(IBltMessage message);
-    public List<CatalogEntry> fetchEntries(DateTime nominalTime);
-    public TimeIntervalFilter createFilter(DateTime nominalTime);
-    public List<CatalogEntry> fetchScanEntries(DateTime nominalTime);
-    public LowestAngleFilter createScanFilter(DateTime nominalTime);
-    public IBltMessage createMessage(DateTime nominalTime, List<CatalogEntry> entries);
-    public boolean areCriteriasMet(List<CatalogEntry> entries);
-    public IBltMessage createCompositeScanMessage(CompositeTimerData data);
+    public Map<String, CatalogEntry> fetchEntriesMap(CompositingRuleFilter filter);
+    public CompositingRuleFilter createFilter(DateTime nominalTime);
+    public IBltMessage createMessage(DateTime nominalTime, Map<String, CatalogEntry> entries);
+    public IBltMessage createComposite(IBltMessage message, CompositingRuleFilter ruleFilter);
+    public DateTime getNominalTimeFromFile(FileEntry file);
   };
 
   @Before
@@ -86,14 +81,11 @@ public class CompositingRuleTest extends EasyMockSupport {
     catalog = createMock(Catalog.class);
     timeoutManager = createMock(TimeoutManager.class);
     ruleUtil = createMock(IRuleUtilities.class);
-    matcher = createMock(MetadataMatcher.class);
-    xpr = new ExpressionFactory();
     classUnderTest = new CompositingRule();
     classUnderTest.setRuleId(10);
     classUnderTest.setCatalog(catalog);
     classUnderTest.setTimeoutManager(timeoutManager);
     classUnderTest.setRuleUtilities(ruleUtil);
-    classUnderTest.setMetadataMatcher(matcher);
   }
 
   @After
@@ -162,10 +154,13 @@ public class CompositingRuleTest extends EasyMockSupport {
     CompositeTimerData ctd = new CompositeTimerData(15, dt);
     IBltMessage resultMessage = new IBltMessage() {
     };
-    List<CatalogEntry> entries = new ArrayList<CatalogEntry>();
+    Map<String,CatalogEntry> entries = new HashMap<String, CatalogEntry>();
     List<String> recipients = new ArrayList<String>();
     
-    expect(methods.fetchEntries(dt)).andReturn(entries);
+    CompositingRuleFilter filter = createMock(CompositingRuleFilter.class);
+    
+    expect(methods.createFilter(dt)).andReturn(filter);
+    expect(methods.fetchEntriesMap(filter)).andReturn(entries);
     expect(ruleUtil.isTriggered(25, dt)).andReturn(false);
     expect(methods.createMessage(dt, entries)).andReturn(resultMessage);
     ruleUtil.trigger(25, dt);
@@ -173,12 +168,15 @@ public class CompositingRuleTest extends EasyMockSupport {
     // continuing with mocking after classUnderTest declaration
     
     classUnderTest = new CompositingRule() {
-      protected List<CatalogEntry> fetchEntries(DateTime nominalTime) {
-        return methods.fetchEntries(nominalTime);
+      protected Map<String, CatalogEntry> fetchEntriesMap(CompositingRuleFilter filter) {
+        return methods.fetchEntriesMap(filter);
       }
-      protected IBltMessage createMessage(DateTime nominalTime, List<CatalogEntry> entries) {
+      protected IBltMessage createMessage(DateTime nominalTime, Map<String, CatalogEntry> entries) {
         return methods.createMessage(nominalTime, entries);
       }
+      protected CompositingRuleFilter createFilter(DateTime nominalTime) {
+        return methods.createFilter(nominalTime);
+      }
     };
     classUnderTest.setRecipients(recipients);
     classUnderTest.setRuleUtilities(ruleUtil);
@@ -198,21 +196,28 @@ public class CompositingRuleTest extends EasyMockSupport {
     final ICompositingMethods methods = createMock(ICompositingMethods.class);
     DateTime dt = new DateTime(2010, 4, 15, 10, 15, 0);
     CompositeTimerData ctd = new CompositeTimerData(15, dt);
-    List<CatalogEntry> entries = new ArrayList<CatalogEntry>();
+    Map<String,CatalogEntry> entries = new HashMap<String,CatalogEntry>();
     List<String> recipients = new ArrayList<String>();
     
-    expect(methods.fetchEntries(dt)).andReturn(entries);
+    CompositingRuleFilter filter = createMock(CompositingRuleFilter.class);
+    
+    expect(methods.createFilter(dt)).andReturn(filter);
+    
+    expect(methods.fetchEntriesMap(filter)).andReturn(entries);
     expect(ruleUtil.isTriggered(25, dt)).andReturn(true);
     
     // continuing with mocking after classUnderTest declaration
     
     classUnderTest = new CompositingRule() {
-      protected List<CatalogEntry> fetchEntries(DateTime nominalTime) {
-        return methods.fetchEntries(nominalTime);
+      protected Map<String,CatalogEntry> fetchEntriesMap(CompositingRuleFilter filter) {
+        return methods.fetchEntriesMap(filter);
       }
-      protected IBltMessage createMessage(DateTime nominalTime, List<CatalogEntry> entries) {
+      protected IBltMessage createMessage(DateTime nominalTime, Map<String,CatalogEntry> entries) {
         return methods.createMessage(nominalTime, entries);
       }
+      protected CompositingRuleFilter createFilter(DateTime nominalTime) {
+        return methods.createFilter(nominalTime);
+      }
     };
     classUnderTest.setRecipients(recipients);
     classUnderTest.setRuleUtilities(ruleUtil);
@@ -283,9 +288,6 @@ public class CompositingRuleTest extends EasyMockSupport {
     Date date = new Date(2010, 1, 1);
     Time time = new Time(10, 0, 0);
     DateTime nominalTime = new DateTime(date, time);
-    Expression e1 = createMock(Expression.class);
-    Expression e2 = createMock(Expression.class);
-    Expression e3 = createMock(Expression.class);
     
     FileEntry file = createMock(FileEntry.class);
     Metadata metadata = createMock(Metadata.class);
@@ -293,12 +295,10 @@ public class CompositingRuleTest extends EasyMockSupport {
     BltDataMessage dataMessage = new BltDataMessage();
     dataMessage.setFileEntry(file);
 
-    expect(file.getMetadata()).andReturn(metadata).times(4);
-    expect(metadata.getWhatObject()).andReturn("PVOL");
+    expect(file.getMetadata()).andReturn(metadata).times(1);
     expect(metadata.getWhatDate()).andReturn(date);
     expect(metadata.getWhatTime()).andReturn(time);
     expect(ruleUtil.createNominalTime(date, time, 10)).andReturn(nominalTime);
-    expect(matcher.match(metadata, xpr.eq(xpr.attribute("what/quantity"), xpr.literal("VRAD")))).andReturn(true);
     
     expect(ruleUtil.isTriggered(25, nominalTime)).andReturn(false);
     
@@ -313,41 +313,6 @@ public class CompositingRuleTest extends EasyMockSupport {
     assertSame(nominalTime, result.getDateTime());
     assertEquals(25, result.getRuleId());
   }
-
-  @Test
-  public void testCreateTimerData_noQuantity() throws Exception {
-    Date date = new Date(2010, 1, 1);
-    Time time = new Time(10, 0, 0);
-    DateTime nominalTime = new DateTime(date, time);
-    Expression e1 = createMock(Expression.class);
-    Expression e2 = createMock(Expression.class);
-    Expression e3 = createMock(Expression.class);
-    
-    FileEntry file = createMock(FileEntry.class);
-    Metadata metadata = createMock(Metadata.class);
-
-    BltDataMessage dataMessage = new BltDataMessage();
-    dataMessage.setFileEntry(file);
-
-    expect(file.getMetadata()).andReturn(metadata).times(3);
-    expect(metadata.getWhatObject()).andReturn("PVOL");
-    expect(metadata.getWhatDate()).andReturn(date);
-    expect(metadata.getWhatTime()).andReturn(time);
-    expect(ruleUtil.createNominalTime(date, time, 10)).andReturn(nominalTime);
-    
-    expect(ruleUtil.isTriggered(25, nominalTime)).andReturn(false);
-    
-    classUnderTest.setRuleId(25);
-    classUnderTest.setInterval(10);
-    classUnderTest.setQuantity(null);
-    replayAll();
-    
-    CompositeTimerData result = classUnderTest.createTimerData(dataMessage);
-    
-    verifyAll();
-    assertSame(nominalTime, result.getDateTime());
-    assertEquals(25, result.getRuleId());
-  }
   
   @Test
   public void testCreateTimerData_alreadyTriggered() throws Exception {
@@ -361,12 +326,10 @@ public class CompositingRuleTest extends EasyMockSupport {
     BltDataMessage dataMessage = new BltDataMessage();
     dataMessage.setFileEntry(file);
     
-    expect(file.getMetadata()).andReturn(metadata).times(4);
-    expect(metadata.getWhatObject()).andReturn("PVOL");
+    expect(file.getMetadata()).andReturn(metadata).times(1);
     expect(metadata.getWhatDate()).andReturn(date);
     expect(metadata.getWhatTime()).andReturn(time);
     expect(ruleUtil.createNominalTime(date, time, 10)).andReturn(nominalTime);
-    expect(matcher.match(metadata, xpr.eq(xpr.attribute("what/quantity"), xpr.literal("DBZH")))).andReturn(true);
     expect(ruleUtil.isTriggered(25, nominalTime)).andReturn(true);
     
     classUnderTest.setRuleId(25);
@@ -382,35 +345,6 @@ public class CompositingRuleTest extends EasyMockSupport {
   }
 
   @Test
-  public void testCreateTimerData_notVolume() throws Exception {
-    FileEntry file = createMock(FileEntry.class);
-    Metadata metadata = createMock(Metadata.class);
-    Date d = new Date(2010, 1, 1);
-    Time t = new Time(10, 1, 15);
-    DateTime dt = new DateTime();
-    
-    BltDataMessage dataMessage = new BltDataMessage();
-    dataMessage.setFileEntry(file);
-
-    expect(file.getMetadata()).andReturn(metadata).times(4);
-    expect(metadata.getWhatObject()).andReturn("IMAGE");
-    expect(metadata.getWhatDate()).andReturn(d);
-    expect(metadata.getWhatTime()).andReturn(t);
-    expect(ruleUtil.createNominalTime(d, t, 10)).andReturn(dt);
-    expect(matcher.match(metadata, xpr.eq(xpr.attribute("what/quantity"), xpr.literal("DBZH")))).andReturn(true);
-    expect(ruleUtil.isTriggered(25, dt)).andReturn(false);
-    classUnderTest.setRuleId(25);
-    classUnderTest.setInterval(10);
-    
-    replayAll();
-    
-    CompositeTimerData result = classUnderTest.createTimerData(dataMessage);
-    
-    verifyAll();
-    assertEquals(null, result);
-  }
-
-  @Test
   public void testCreateTimerData_notBltDataMessage() throws Exception {
     IBltMessage dataMessage = new IBltMessage() {};
     
@@ -448,62 +382,6 @@ public class CompositingRuleTest extends EasyMockSupport {
     expect(entry.getUuid()).andReturn("uuid-"+source);
     return entry;
   }
-
-  @Test
-  public void testAreCriteriasMet() {
-    List<String> sources = new ArrayList<String>();
-    sources.add("searl");
-    sources.add("sekkr");
-    sources.add("seosu");
-    List<CatalogEntry> entries = new ArrayList<CatalogEntry>();
-    entries.add(createCatalogEntry("sekkr"));
-    entries.add(createCatalogEntry("seosu"));
-    entries.add(createCatalogEntry("selul"));
-    entries.add(createCatalogEntry("searl"));
-    List<String> entrySources = new ArrayList<String>();
-    entrySources.add("sekkr");
-    entrySources.add("seosu");
-    entrySources.add("selul");
-    entrySources.add("searl");
-    
-    classUnderTest.setSources(sources);
-    expect(ruleUtil.getSourcesFromEntries(entries)).andReturn(entrySources);
-    
-    replayAll();
-    
-    boolean result = classUnderTest.areCriteriasMet(entries);
-    
-    verifyAll();
-    
-    assertEquals(true, result);
-  }
-
-  @Test
-  public void testAreCriteriasMet_false() {
-    List<String> sources = new ArrayList<String>();
-    sources.add("searl");
-    sources.add("sekkr");
-    sources.add("seosu");
-    List<CatalogEntry> entries = new ArrayList<CatalogEntry>();
-    entries.add(createCatalogEntry("sekkr"));
-    entries.add(createCatalogEntry("selul"));
-    entries.add(createCatalogEntry("searl"));
-    List<String> entrySources = new ArrayList<String>();
-    entrySources.add("sekkr");
-    entrySources.add("selul");
-    entrySources.add("searl");
-    
-    expect(ruleUtil.getSourcesFromEntries(entries)).andReturn(entrySources);
-    
-    classUnderTest.setSources(sources);
-    
-    replayAll();
-    
-    boolean result = classUnderTest.areCriteriasMet(entries);
-    
-    verifyAll();
-    assertEquals(false, result);
-  }
   
   protected boolean arrayContains(String[] arr, String value) {
     for (String x : arr) {
@@ -525,9 +403,8 @@ public class CompositingRuleTest extends EasyMockSupport {
     detectors.add("nisse");
     
     // actual entries don't matter, just make the list of different size to distinguish
-    List<CatalogEntry> entries = new ArrayList<CatalogEntry>();
-    List<CatalogEntry> entriesByTime = new ArrayList<CatalogEntry>();
-    List<CatalogEntry> entriesBySources = new ArrayList<CatalogEntry>();
+    Map<String, CatalogEntry> entries = new HashMap<String, CatalogEntry>();
+    List<CatalogEntry> entriesBySources = new ArrayList<CatalogEntry>(entries.values());
     
     List<String> fileEntries = new ArrayList<String>();
     fileEntries.add("uuid-1");
@@ -544,10 +421,7 @@ public class CompositingRuleTest extends EasyMockSupport {
     sources.add("searl");
     classUnderTest.setSources(sources);
     
-    expect(ruleUtil.getEntriesByClosestTime(nominalTime, entries)).andReturn(entriesByTime);
-    expect(ruleUtil.getEntriesBySources(sources, entriesByTime)).andReturn(entriesBySources);
     expect(ruleUtil.getUuidStringsFromEntries(entriesBySources)).andReturn(fileEntries);
-    expect(ruleUtil.getSourcesFromEntries(entriesBySources)).andReturn(usedSources);
     ruleUtil.reportRadarSourceUsage(sources, usedSources);
     
     classUnderTest.setSelectionMethod(CompositingRule.SelectionMethod_HEIGHT_ABOVE_SEALEVEL);
@@ -596,9 +470,8 @@ public class CompositingRuleTest extends EasyMockSupport {
     detectors.add("nisse");
     
     // actual entries don't matter, just make the list of different size to distinguish
-    List<CatalogEntry> entries = new ArrayList<CatalogEntry>();
-    List<CatalogEntry> entriesByTime = new ArrayList<CatalogEntry>();
-    List<CatalogEntry> entriesBySources = new ArrayList<CatalogEntry>();
+    Map<String, CatalogEntry> entries = new HashMap<String, CatalogEntry>();
+    List<CatalogEntry> entriesBySources = new ArrayList<CatalogEntry>(entries.values());
     
     List<String> fileEntries = new ArrayList<String>();
     fileEntries.add("uuid-1");
@@ -615,10 +488,7 @@ public class CompositingRuleTest extends EasyMockSupport {
     sources.add("searl");
     classUnderTest.setSources(sources);
     
-    expect(ruleUtil.getEntriesByClosestTime(nominalTime, entries)).andReturn(entriesByTime);
-    expect(ruleUtil.getEntriesBySources(sources, entriesByTime)).andReturn(entriesBySources);
     expect(ruleUtil.getUuidStringsFromEntries(entriesBySources)).andReturn(fileEntries);
-    expect(ruleUtil.getSourcesFromEntries(entriesBySources)).andReturn(usedSources);
     ruleUtil.reportRadarSourceUsage(sources, usedSources);
     
     classUnderTest.setSelectionMethod(CompositingRule.SelectionMethod_HEIGHT_ABOVE_SEALEVEL);
@@ -673,9 +543,8 @@ public class CompositingRuleTest extends EasyMockSupport {
     detectors.add("nisse");
     
     // actual entries don't matter, just make the list of different size to distinguish
-    List<CatalogEntry> entries = new ArrayList<CatalogEntry>();
-    List<CatalogEntry> entriesByTime = new ArrayList<CatalogEntry>();
-    List<CatalogEntry> entriesBySources = new ArrayList<CatalogEntry>();
+    Map<String, CatalogEntry> entries = new HashMap<String, CatalogEntry>();
+    List<CatalogEntry> entriesBySources = new ArrayList<CatalogEntry>(entries.values());
     
     List<String> fileEntries = new ArrayList<String>();
     fileEntries.add("uuid-1");
@@ -692,10 +561,7 @@ public class CompositingRuleTest extends EasyMockSupport {
     sources.add("searl");
     classUnderTest.setSources(sources);
     
-    expect(ruleUtil.getEntriesByClosestTime(nominalTime, entries)).andReturn(entriesByTime);
-    expect(ruleUtil.getEntriesBySources(sources, entriesByTime)).andReturn(entriesBySources);
     expect(ruleUtil.getUuidStringsFromEntries(entriesBySources)).andReturn(fileEntries);
-    expect(ruleUtil.getSourcesFromEntries(entriesBySources)).andReturn(usedSources);
     ruleUtil.reportRadarSourceUsage(sources, usedSources);
     
     classUnderTest.setSelectionMethod(CompositingRule.SelectionMethod_HEIGHT_ABOVE_SEALEVEL);
@@ -753,9 +619,8 @@ public class CompositingRuleTest extends EasyMockSupport {
     detectors.add("nisse");
     
     // actual entries don't matter, just make the list of different size to distinguish
-    List<CatalogEntry> entries = new ArrayList<CatalogEntry>();
-    List<CatalogEntry> entriesByTime = new ArrayList<CatalogEntry>();
-    List<CatalogEntry> entriesBySources = new ArrayList<CatalogEntry>();
+    Map<String, CatalogEntry> entries = new HashMap<String, CatalogEntry>();
+    List<CatalogEntry> entriesBySources = new ArrayList<CatalogEntry>(entries.values());
     
     List<String> fileEntries = new ArrayList<String>();
     fileEntries.add("uuid-1");
@@ -772,10 +637,7 @@ public class CompositingRuleTest extends EasyMockSupport {
     sources.add("searl");
     classUnderTest.setSources(sources);
     
-    expect(ruleUtil.getEntriesByClosestTime(nominalTime, entries)).andReturn(entriesByTime);
-    expect(ruleUtil.getEntriesBySources(sources, entriesByTime)).andReturn(entriesBySources);
     expect(ruleUtil.getUuidStringsFromEntries(entriesBySources)).andReturn(fileEntries);
-    expect(ruleUtil.getSourcesFromEntries(entriesBySources)).andReturn(usedSources);
     ruleUtil.reportRadarSourceUsage(sources, usedSources);
     
     classUnderTest.setSelectionMethod(CompositingRule.SelectionMethod_HEIGHT_ABOVE_SEALEVEL);
@@ -834,9 +696,8 @@ public class CompositingRuleTest extends EasyMockSupport {
     detectors.add("nisse");
     
     // actual entries don't matter, just make the list of different size to distinguish
-    List<CatalogEntry> entries = new ArrayList<CatalogEntry>();
-    List<CatalogEntry> entriesByTime = new ArrayList<CatalogEntry>();
-    List<CatalogEntry> entriesBySources = new ArrayList<CatalogEntry>();
+    Map<String, CatalogEntry> entries = new HashMap<String, CatalogEntry>();
+    List<CatalogEntry> entriesBySources = new ArrayList<CatalogEntry>(entries.values());
     
     List<String> fileEntries = new ArrayList<String>();
     fileEntries.add("uuid-1");
@@ -853,10 +714,7 @@ public class CompositingRuleTest extends EasyMockSupport {
     sources.add("searl");
     classUnderTest.setSources(sources);
     
-    expect(ruleUtil.getEntriesByClosestTime(nominalTime, entries)).andReturn(entriesByTime);
-    expect(ruleUtil.getEntriesBySources(sources, entriesByTime)).andReturn(entriesBySources);
     expect(ruleUtil.getUuidStringsFromEntries(entriesBySources)).andReturn(fileEntries);
-    expect(ruleUtil.getSourcesFromEntries(entriesBySources)).andReturn(usedSources);
     ruleUtil.reportRadarSourceUsage(sources, usedSources);
     
     classUnderTest.setSelectionMethod(CompositingRule.SelectionMethod_HEIGHT_ABOVE_SEALEVEL);
@@ -917,9 +775,8 @@ public class CompositingRuleTest extends EasyMockSupport {
     detectors.add("nisse");
     
     // actual entries don't matter, just make the list of different size to distinguish
-    List<CatalogEntry> entries = new ArrayList<CatalogEntry>();
-    List<CatalogEntry> entriesByTime = new ArrayList<CatalogEntry>();
-    List<CatalogEntry> entriesBySources = new ArrayList<CatalogEntry>();
+    Map<String, CatalogEntry> entries = new HashMap<String, CatalogEntry>();
+    List<CatalogEntry> entriesBySources = new ArrayList<CatalogEntry>(entries.values());
     
     List<String> fileEntries = new ArrayList<String>();
     fileEntries.add("uuid-1");
@@ -936,10 +793,7 @@ public class CompositingRuleTest extends EasyMockSupport {
     sources.add("searl");
     classUnderTest.setSources(sources);
     
-    expect(ruleUtil.getEntriesByClosestTime(nominalTime, entries)).andReturn(entriesByTime);
-    expect(ruleUtil.getEntriesBySources(sources, entriesByTime)).andReturn(entriesBySources);
     expect(ruleUtil.getUuidStringsFromEntries(entriesBySources)).andReturn(fileEntries);
-    expect(ruleUtil.getSourcesFromEntries(entriesBySources)).andReturn(usedSources);
     ruleUtil.reportRadarSourceUsage(sources, usedSources);
     
     classUnderTest.setSelectionMethod(CompositingRule.SelectionMethod_HEIGHT_ABOVE_SEALEVEL);
@@ -989,28 +843,132 @@ public class CompositingRuleTest extends EasyMockSupport {
   }
   
   @Test
-  public void testFetchEntries() throws Exception {
-    final ICompositingMethods methods = createMock(ICompositingMethods.class);
-    DateTime dt = new DateTime(2010,1,1,10,0,0);
-    TimeIntervalFilter filter = new TimeIntervalFilter();
+  public void testFetchEntries_noEntries() throws Exception {
+    CompositingRuleFilter filter = createMock(CompositingRuleFilter.class);
     List<CatalogEntry> entries = new ArrayList<CatalogEntry>();
     
-    expect(methods.createFilter(dt)).andReturn(filter);
     expect(catalog.fetch(filter)).andReturn(entries);
     
-    classUnderTest = new CompositingRule() {
-      protected TimeIntervalFilter createFilter(DateTime nt) {
-        return methods.createFilter(nt);
+    classUnderTest.setCatalog(catalog);
+    
+    replayAll();
+    
+    Map<String, CatalogEntry> result = classUnderTest.fetchEntriesMap(filter);
+    
+    verifyAll();
+    
+    assertTrue(result.size() == 0);
+  }
+  
+  @Test
+  public void testFetchEntries_oneEntryPerSource() throws Exception {
+    List<String> sources = new ArrayList<String>(Arrays.asList("source1", "source2", "last_source"));
+    CompositingRuleFilter filter = createMock(CompositingRuleFilter.class);
+    List<CatalogEntry> entries = new ArrayList<CatalogEntry>();
+    
+    classUnderTest.setSources(sources);
+    
+    for (String source : sources) {
+      CatalogEntry entry = createMock(CatalogEntry.class);
+      FileEntry fileEntry = createMock(FileEntry.class);
+      
+      expect(entry.getSource()).andReturn(source);
+      expect(entry.getFileEntry()).andReturn(fileEntry);
+      expect(filter.fileMatches(fileEntry)).andReturn(true);
+      
+      entries.add(entry);
+    }
+    
+    expect(catalog.fetch(filter)).andReturn(entries);
+    
+    classUnderTest.setCatalog(catalog);
+    
+    replayAll();
+    
+    Map<String, CatalogEntry> result = classUnderTest.fetchEntriesMap(filter);
+    
+    verifyAll();
+    
+    assertTrue(result.size() == sources.size());
+  }
+  
+  @Test
+  public void testFetchEntries_twoEntriesPerSource() throws Exception {
+    List<String> sources = new ArrayList<String>(Arrays.asList("source1", "source2", "last_source"));
+    CompositingRuleFilter filter = createMock(CompositingRuleFilter.class);
+    List<CatalogEntry> entries = new ArrayList<CatalogEntry>();
+    
+    classUnderTest.setSources(sources);
+    
+    for (String source : sources) {
+      CatalogEntry entry1 = createMock(CatalogEntry.class);
+      FileEntry fileEntry = createMock(FileEntry.class);
+      
+      expect(entry1.getSource()).andReturn(source);
+      expect(entry1.getFileEntry()).andReturn(fileEntry);
+      expect(filter.fileMatches(fileEntry)).andReturn(true);
+      
+      entries.add(entry1);
+      
+      // add one more entry for the same source. this entry shall not be returned however, 
+      // since it is expecetd that filter has ordered the results in correct order and this 
+      // is located after the previous one for the same source
+      CatalogEntry entry2 = createMock(CatalogEntry.class);
+      expect(entry2.getSource()).andReturn(source);
+      entries.add(entry2);
+    }
+    
+    expect(catalog.fetch(filter)).andReturn(entries);
+    
+    classUnderTest.setCatalog(catalog);
+    
+    replayAll();
+    
+    Map<String, CatalogEntry> result = classUnderTest.fetchEntriesMap(filter);
+    
+    verifyAll();
+    
+    assertTrue(result.size() == sources.size());
+  }
+  
+  @Test
+  public void testFetchEntries_oneEntryDoesNotMatchFilter() throws Exception {
+    List<String> sources = new ArrayList<String>(Arrays.asList("source1", "source2", "last_source"));
+    CompositingRuleFilter filter = createMock(CompositingRuleFilter.class);
+    List<CatalogEntry> entries = new ArrayList<CatalogEntry>();
+    
+    classUnderTest.setSources(sources);
+    
+    for (String source : sources) {
+      boolean fileMatches = true;
+      if (source.equals("source2")) {
+        fileMatches = false;
       }
-    };
+      
+      CatalogEntry entry = createMock(CatalogEntry.class);
+      FileEntry fileEntry = createMock(FileEntry.class);
+      
+      expect(entry.getSource()).andReturn(source);
+      expect(entry.getFileEntry()).andReturn(fileEntry);
+      expect(filter.fileMatches(fileEntry)).andReturn(fileMatches);
+      
+      entries.add(entry);
+    }
+    
+    expect(catalog.fetch(filter)).andReturn(entries);
+    
     classUnderTest.setCatalog(catalog);
     
     replayAll();
     
-    List<CatalogEntry> result = classUnderTest.fetchEntries(dt);
+    Map<String, CatalogEntry> result = classUnderTest.fetchEntriesMap(filter);
     
     verifyAll();
-    assertSame(entries, result);
+    
+    assertTrue(result.size() == (sources.size() - 1));
+    assertTrue(result.keySet().contains("source1"));
+    assertTrue(result.keySet().contains("last_source"));
+    assertTrue(!result.keySet().contains("source2"));
   }
 
   @Test
@@ -1027,7 +985,7 @@ public class CompositingRuleTest extends EasyMockSupport {
     replayAll();
     
     classUnderTest.setInterval(10);
-    TimeIntervalFilter result = classUnderTest.createFilter(startDT);
+    CompositingRuleFilter result = classUnderTest.createFilter(startDT);
     
     verifyAll();
     assertNotNull(result);
@@ -1035,315 +993,201 @@ public class CompositingRuleTest extends EasyMockSupport {
     assertSame(stopDT, result.getStopDateTime());
     assertEquals("PVOL", result.getObject());
   }
-
+  
+  @Test
+  public void testHandle_fileMatches() throws Exception {
+    testHandle(true); 
+  }
+  
   @Test
-  public void handleCompositeFromScans() throws Exception {
+  public void testHandle_fileDoesNotMatch() throws Exception {
+    testHandle(false); 
+  }
+  
+  private void testHandle(boolean fileMatches) {
     final ICompositingMethods methods = createMock(ICompositingMethods.class);
-    DateTime dt = new DateTime(2016, 1, 2, 3, 30);
-    DateTime prevDt = new DateTime(2016, 1, 2, 3, 15);
-    Map<String, Double> angles = new HashMap<String, Double>();
-    List<String> sources = new ArrayList<String>();
+
     FileEntry fileEntry = createMock(FileEntry.class);
+    DateTime dateTime = new DateTime(2017, 02, 01, 15, 10, 0);
+    CompositingRuleFilter filter = createMock(CompositingRuleFilter.class);
+    
     BltDataMessage msg = new BltDataMessage();
+    BltDataMessage genMsg = new BltDataMessage();
     msg.setFileEntry(fileEntry);
-    CompositeTimerData timerData = new CompositeTimerData(1, dt);
-    
+
     classUnderTest = new CompositingRule() {
       @Override
-      protected CompositeTimerData createTimerData(IBltMessage message) {
-        return methods.createTimerData(message);
+      public IBltMessage createComposite(IBltMessage message, CompositingRuleFilter ruleFilter) {
+        return methods.createComposite(message, ruleFilter);
       }
-      @Override
-      protected IBltMessage createCompositeScanMessage(CompositeTimerData data) {
-        return methods.createCompositeScanMessage(data);
+      
+      protected CompositingRuleFilter createFilter(DateTime nominalTime) {
+        return methods.createFilter(nominalTime);
+      }
+      
+      protected DateTime getNominalTimeFromFile(FileEntry file) {
+        return methods.getNominalTimeFromFile(file);
       }
     };
-    classUnderTest.setRuleUtilities(ruleUtil);
-    classUnderTest.setTimeoutManager(timeoutManager);
-    classUnderTest.setQuantity("DBZH");
-    classUnderTest.setInterval(15);
-    classUnderTest.setSources(sources);
-    classUnderTest.setTimeout(2);
+
+    expect(methods.getNominalTimeFromFile(fileEntry)).andReturn(dateTime);
+    expect(methods.createFilter(dateTime)).andReturn(filter);
+    expect(filter.fileMatches(fileEntry)).andReturn(fileMatches);
     
-    expect(methods.createTimerData(msg)).andReturn(timerData);
-    expect(timeoutManager.getRegisteredTask(timerData)).andReturn(null);
-    expect(ruleUtil.createPrevNominalTime(dt, 15)).andReturn(prevDt);
-    expect(ruleUtil.fetchLowestSourceElevationAngle(prevDt, dt, sources, "DBZH")).andReturn(angles);
-    expect(methods.createCompositeScanMessage(timerData)).andReturn(null);
-    expect(ruleUtil.getTimeoutTime(dt, false, 2000)).andReturn(10L);
-    expect(timeoutManager.register(classUnderTest, 10L, timerData)).andReturn(123L);
+    if (fileMatches) {
+      expect(methods.createComposite(msg, filter)).andReturn(genMsg);
+      expect(fileEntry.getUuid()).andReturn(new UUID(100, 100)).anyTimes();      
+    }
     
     replayAll();
-
-    IBltMessage result = classUnderTest.handleCompositeFromScans(msg);
+  
+    IBltMessage result = classUnderTest.handle(msg);
     
     verifyAll();
-    assertNull(result);
-    assertSame(angles, timerData.getPreviousAngles());
+    if (fileMatches) {
+      assertSame(result, genMsg);      
+    } else {
+      assertNull(result);
+    }
   }
   
-  protected boolean compareDT(DateTime o1, DateTime o2) {
-    return o1.equals(o2);
+  @Test
+  public void testCreateComposite_pvol() throws Exception {
+    testCreateComposite(false, false, false, false); 
   }
   
   @Test
-  public void testCreateCompositeScanMessage() throws Exception {
-    final ICompositingMethods methods = createMock(ICompositingMethods.class);
-    BltGenerateMessage createdMessage = new BltGenerateMessage();
-    
-    DateTime pdt = new DateTime();
-    DateTime dt = new DateTime();
-    DateTime ndt = new DateTime();
-    
-    Map<String, Double> prevAngles = new HashMap<String, Double>();
-    Map<String, Double> currAngles = new HashMap<String, Double>();
-    List<String> sources = new ArrayList<String>();
-    List<CatalogEntry> currEntries = new ArrayList<CatalogEntry>();
-
-    CatalogEntry e1 = createCatalogEntry("sekkr", pdt, 0.1);
-    currEntries.add(e1);
-    CatalogEntry e2 = createCatalogEntry("searl", pdt, 0.5);
-    currEntries.add(e2);
-
-    sources.add("sekkr");
-    sources.add("searl");
-    
-    prevAngles.put("sekkr", 0.1);
-    prevAngles.put("searl", 0.5);
-
-    currAngles.put("sekkr", 0.1);
-    currAngles.put("searl", 0.5);
-    
-    CompositeTimerData data = new CompositeTimerData(1, dt);
-    data.setPreviousAngles(prevAngles);
-    expect(ruleUtil.createNextNominalTime(dt, 10)).andReturn(ndt);
-    expect(ruleUtil.fetchLowestSourceElevationAngle(dt, ndt, sources, "DBZH")).andReturn(currAngles);
-
-    expect(methods.createMessage(dt, currEntries)).andReturn(createdMessage);
-    expect(methods.fetchScanEntries(dt)).andReturn(currEntries);
-
-    replayAll();
-    
-    classUnderTest = new CompositingRule() {
-      protected IBltMessage createMessage(DateTime nominalDT, List<CatalogEntry> entries) {
-        return methods.createMessage(nominalDT, entries);
-      }
-
-      protected List<CatalogEntry> fetchScanEntries(DateTime nominalTime) {
-        return methods.fetchScanEntries(nominalTime);
-      }
-    };
-    classUnderTest.setCatalog(catalog);
-    classUnderTest.setTimeoutManager(timeoutManager);
-    classUnderTest.setRuleUtilities(ruleUtil);
-    classUnderTest.setSources(sources);
-    classUnderTest.setInterval(10);
-    
-    BltGenerateMessage msg = (BltGenerateMessage)classUnderTest.createCompositeScanMessage(data);
-    
-    verifyAll();
-    assertSame(createdMessage, msg);
+  public void testCreateComposite_pvol_sourceMissing() throws Exception {
+    testCreateComposite(false, true, false, false); 
   }
   
   @Test
-  public void testCreateCompositeScanMessage_withOtherQuantity() throws Exception {
-    final ICompositingMethods methods = createMock(ICompositingMethods.class);
-    BltGenerateMessage createdMessage = new BltGenerateMessage();
-    
-    DateTime pdt = new DateTime();
-    DateTime dt = new DateTime();
-    DateTime ndt = new DateTime();
-    
-    Map<String, Double> prevAngles = new HashMap<String, Double>();
-    Map<String, Double> currAngles = new HashMap<String, Double>();
-    List<String> sources = new ArrayList<String>();
-    List<CatalogEntry> currEntries = new ArrayList<CatalogEntry>();
-
-    CatalogEntry e1 = createCatalogEntry("sekkr", pdt, 0.1);
-    currEntries.add(e1);
-    CatalogEntry e2 = createCatalogEntry("searl", pdt, 0.5);
-    currEntries.add(e2);
-
-    sources.add("sekkr");
-    sources.add("searl");
-    
-    prevAngles.put("sekkr", 0.1);
-    prevAngles.put("searl", 0.5);
-
-    currAngles.put("sekkr", 0.1);
-    currAngles.put("searl", 0.5);
-    
-    CompositeTimerData data = new CompositeTimerData(1, dt);
-    data.setPreviousAngles(prevAngles);
-    expect(ruleUtil.createNextNominalTime(dt, 10)).andReturn(ndt);
-    expect(ruleUtil.fetchLowestSourceElevationAngle(dt, ndt, sources, "VRAD")).andReturn(currAngles);
-
-    expect(methods.createMessage(dt, currEntries)).andReturn(createdMessage);
-    expect(methods.fetchScanEntries(dt)).andReturn(currEntries);
-
-    replayAll();
-    
-    classUnderTest = new CompositingRule() {
-      protected IBltMessage createMessage(DateTime nominalDT, List<CatalogEntry> entries) {
-        return methods.createMessage(nominalDT, entries);
-      }
-
-      protected List<CatalogEntry> fetchScanEntries(DateTime nominalTime) {
-        return methods.fetchScanEntries(nominalTime);
-      }
-    };
-    classUnderTest.setCatalog(catalog);
-    classUnderTest.setTimeoutManager(timeoutManager);
-    classUnderTest.setRuleUtilities(ruleUtil);
-    classUnderTest.setSources(sources);
-    classUnderTest.setInterval(10);
-    classUnderTest.setQuantity("VRAD");
-    
-    BltGenerateMessage msg = (BltGenerateMessage)classUnderTest.createCompositeScanMessage(data);
-    
-    verifyAll();
-    assertSame(createdMessage, msg);
+  public void testCreateComposite_scan() throws Exception {
+    testCreateComposite(true, false, false, false); 
+  }
+  
+  @Test
+  public void testCreateComposite_scan_sourceMissing() throws Exception {
+    testCreateComposite(true, true, false, false); 
   }
   
   @Test
-  public void testCreateCompositeScanMessage_missingSources() throws Exception {
+  public void testCreateComposite_scan_angleMissing() throws Exception {
+    testCreateComposite(true, false, true, false); 
+  }
+  
+  @Test
+  public void testCreateComposite_scan_noPreviousAngles() throws Exception {
+    testCreateComposite(true, false, false, true); 
+  }
+  
+  private void testCreateComposite(boolean scanBased, boolean sourceMissing, boolean scanAngleMissing, boolean noPreviousAngles) {
     final ICompositingMethods methods = createMock(ICompositingMethods.class);
+
+    int ruleId = 567;
+    FileEntry fileEntry = createMock(FileEntry.class);
+    DateTime dateTime = new DateTime(2017, 02, 01, 15, 10, 0);
+    CompositingRuleFilter filter = createMock(CompositingRuleFilter.class);
     
-    DateTime dt = new DateTime();
-    DateTime ndt = new DateTime();
-    Map<String, Double> prevAngles = new HashMap<String, Double>();
-    Map<String, Double> currAngles = new HashMap<String, Double>();
-    List<String> sources = new ArrayList<String>();
-    sources.add("sekkr");
-    sources.add("searl");
+    CompositeTimerData timerData = createMock(CompositeTimerData.class);
+    TimeoutTask timeoutTask = createMock(TimeoutTask.class);
     
-    prevAngles.put("sekkr", 0.1);
-    prevAngles.put("searl", 0.5);
-
-    currAngles.put("sekkr", 0.1);
+    Map<String, CatalogEntry> currentEntries = new HashMap<String, CatalogEntry>();
     
-    CompositeTimerData data = new CompositeTimerData(1, dt);
-    data.setPreviousAngles(prevAngles);
-    expect(ruleUtil.createNextNominalTime(dt, 10)).andReturn(ndt);
-    expect(ruleUtil.fetchLowestSourceElevationAngle(dt, ndt, sources, "DBZH")).andReturn(currAngles);
+    List<String> previousSources = new ArrayList<String>(Arrays.asList("source1", "source2", "last_source"));
+    Map<String,Double> previousAngles = new HashMap<String,Double>();
     
-    replayAll();
+    for (String source : previousSources) {
+      
+      Double previousAngle = new Double(0.5);
+      Double currentAngle = new Double(previousAngle);
+      if (scanAngleMissing && source.equals("source2")) {
+        currentAngle = previousAngle + new Double(0.2);
+      }
+      
+      if (!noPreviousAngles) {
+        previousAngles.put(source, previousAngle);        
+      }
+      
+      if (!(sourceMissing && source.equals("last_source"))) {
+        CatalogEntry catalogEntry = createMock(CatalogEntry.class);
+        
+        if (scanBased && !(scanAngleMissing && source.equals("last_source")) && !(noPreviousAngles && !source.equals("source1"))) {
+          expect(catalogEntry.getAttribute("/dataset1/where/elangle")).andReturn(currentAngle);
+        }
+        
+        currentEntries.put(source, catalogEntry);        
+      }
+    }
     
+    BltDataMessage msg = new BltDataMessage();
+    BltDataMessage genMsg = new BltDataMessage();
+    msg.setFileEntry(fileEntry);
+
     classUnderTest = new CompositingRule() {
-      protected IBltMessage createMessage(DateTime nominalDT, List<CatalogEntry> entries) {
-        return methods.createMessage(nominalDT, entries);
+      @Override
+      protected CompositeTimerData createTimerData(IBltMessage message) {
+        return methods.createTimerData(message);
+      }
+      
+      protected CompositingRuleFilter createFilter(DateTime nominalTime) {
+        return methods.createFilter(nominalTime);
+      }
+      
+      protected Map<String, CatalogEntry> fetchEntriesMap(CompositingRuleFilter filter) {
+        return methods.fetchEntriesMap(filter);
+      }
+      
+      protected IBltMessage createMessage(DateTime nominalTime, Map<String, CatalogEntry> entries) {
+        return methods.createMessage(nominalTime, entries);
       }
     };
-    classUnderTest.setCatalog(catalog);
-    classUnderTest.setTimeoutManager(timeoutManager);
-    classUnderTest.setRuleUtilities(ruleUtil);
-    classUnderTest.setSources(sources);
-    classUnderTest.setInterval(10);
     
-    BltGenerateMessage msg = (BltGenerateMessage)classUnderTest.createCompositeScanMessage(data);
-    
-    verifyAll();
-    assertNull(msg);
-  }
+    long ttId = 987;
 
-  @Test
-  public void testCreateCompositeScanMessage_toHighElevation() throws Exception {
-    final ICompositingMethods methods = createMock(ICompositingMethods.class);
-    
-    DateTime dt = new DateTime();
-    DateTime ndt = new DateTime();
-    Map<String, Double> prevAngles = new HashMap<String, Double>();
-    Map<String, Double> currAngles = new HashMap<String, Double>();
-    List<String> sources = new ArrayList<String>();
-    sources.add("sekkr");
-    
-    prevAngles.put("sekkr", 0.1);
-
-    currAngles.put("sekkr", 0.2);
+    expect(methods.createTimerData(msg)).andReturn(timerData);
+    expect(timeoutManager.getRegisteredTask(timerData)).andReturn(timeoutTask);
+    expect(timeoutTask.getData()).andReturn(timerData);
+    expect(methods.fetchEntriesMap(filter)).andReturn(currentEntries);
+    expect(timerData.getPreviousSources()).andReturn(previousSources);
+    
+    if (scanBased) {
+      int noOfCalls = currentEntries.size();
+      if (noPreviousAngles) {
+        noOfCalls = 1;
+      } else if (scanAngleMissing) {
+        noOfCalls = currentEntries.size() - 1;
+      }
+      expect(timerData.getPreviousAngles()).andReturn(previousAngles).times(noOfCalls);
+    }
     
-    CompositeTimerData data = new CompositeTimerData(1, dt);
-    data.setPreviousAngles(prevAngles);
-    expect(ruleUtil.createNextNominalTime(dt, 10)).andReturn(ndt);
-    expect(ruleUtil.fetchLowestSourceElevationAngle(dt, ndt, sources, "DBZH")).andReturn(currAngles);
+    if (!sourceMissing && !scanAngleMissing && !noPreviousAngles) {    
+      expect(timerData.getDateTime()).andReturn(dateTime).times(2);
+      expect(methods.createMessage(dateTime, currentEntries)).andReturn(genMsg);
+      
+      ruleUtil.trigger(ruleId, dateTime);
+      
+      expect(timeoutTask.getId()).andReturn(ttId);
+      
+      timeoutManager.unregister(ttId);
+    }
     
     replayAll();
     
-    classUnderTest = new CompositingRule() {
-      protected IBltMessage createMessage(DateTime nominalDT, List<CatalogEntry> entries) {
-        return methods.createMessage(nominalDT, entries);
-      }
-    };
-    classUnderTest.setCatalog(catalog);
+    classUnderTest.setScanBased(scanBased);
     classUnderTest.setTimeoutManager(timeoutManager);
     classUnderTest.setRuleUtilities(ruleUtil);
-    classUnderTest.setSources(sources);
-    classUnderTest.setInterval(10);
-    
-    BltGenerateMessage msg = (BltGenerateMessage)classUnderTest.createCompositeScanMessage(data);
+    classUnderTest.setRuleId(ruleId);
+  
+    IBltMessage result = classUnderTest.createComposite(msg, filter);
     
     verifyAll();
-    assertNull(msg);
-  }
-
-  @Test
-  public void testFetchScanEntries() throws Exception {
-    final ICompositingMethods methods = createMock(ICompositingMethods.class);
-    LowestAngleFilter filter = createMock(LowestAngleFilter.class);
-    DateTime dt = new DateTime(2010,1,1,10,0,0);
-    CatalogEntry e1 = new CatalogEntry();
-    CatalogEntry e2 = new CatalogEntry();
-    List<CatalogEntry> entries1 = new ArrayList<CatalogEntry>();
-    List<CatalogEntry> entries2 = new ArrayList<CatalogEntry>();
-    List<String> sources = new ArrayList<String>();
 
-    entries1.add(e1);
-    entries2.add(e2);
-    sources.add("seang");
-    sources.add("sekkr");
-
-    expect(methods.createScanFilter(dt)).andReturn(filter);
-    filter.setSource("seang");
-    expect(catalog.fetch(filter)).andReturn(entries1);
-    filter.setSource("sekkr");
-    expect(catalog.fetch(filter)).andReturn(entries2);
-
-    classUnderTest = new CompositingRule() {
-      protected LowestAngleFilter createScanFilter(DateTime nt) {
-        return methods.createScanFilter(nt);
-      }
-    };
-    classUnderTest.setCatalog(catalog);
-    classUnderTest.setSources(sources);
-
-    replayAll();
-
-    List<CatalogEntry> result = classUnderTest.fetchScanEntries(dt);
-
-    verifyAll();
-    
-    assertTrue(result.contains(e1));
-    assertTrue(result.contains(e2));
-  }
-
-  @Test
-  public void testCreateScanFilter() throws Exception {
-    Date startDate = new Date(2010,1,1);
-    Time startTime = new Time(1,2,0);
-    Date stopDate = new Date(2010,1,1);
-    Time stopTime = new Time(1,3,0);
-    final DateTime startDT = new DateTime(startDate, startTime);
-    final DateTime stopDT = new DateTime(stopDate, stopTime);
-
-    expect(ruleUtil.createNextNominalTime(startDT, 10)).andReturn(stopDT);
-    
-    replayAll();
-    classUnderTest.setInterval(10);
-    LowestAngleFilter result = classUnderTest.createScanFilter(startDT);
-    
-    verifyAll();
-    assertNotNull(result);
-    assertSame(startDT, result.getStart());
-    assertSame(stopDT, result.getStop());
+    if (sourceMissing || scanAngleMissing || noPreviousAngles) {
+      assertNull(result);
+    } else {      
+      assertSame(result, genMsg);      
+    }
   }
   
   @Test
@@ -1382,8 +1226,4 @@ public class CompositingRuleTest extends EasyMockSupport {
     assertNotSame(detectors, classUnderTest.getDetectors());
   }
   
-  private CatalogEntry createCatalogEntry(String src, DateTime dt, double elangle) {
-    CatalogEntry entry = createMock(CatalogEntry.class);
-    return entry;
-  }
 }