diff --git a/homework/bill_gc.log b/homework/bill_gc.log new file mode 100644 index 0000000..e5e453e --- /dev/null +++ b/homework/bill_gc.log @@ -0,0 +1,16 @@ +Java HotSpot(TM) 64-Bit Server VM (25.20-b23) for windows-amd64 JRE (1.8.0_20-b26), built on Jul 30 2014 13:51:23 by "java_re" with MS VC++ 10.0 (VS2010) +Memory: 4k page, physical 8271460k(3665084k free), swap 10237540k(4273076k free) +CommandLine flags: -XX:InitialHeapSize=132343360 -XX:MaxHeapSize=2117493760 -XX:+PrintGC -XX:+PrintGCTimeStamps -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC +3.961: [GC (Allocation Failure) 23808K->21424K(125952K), 0.0080784 secs] +4.551: [GC (Allocation Failure) 53438K->52064K(125952K), 0.0110631 secs] +5.875: [GC (Allocation Failure) 83678K->82752K(125952K), 0.0129618 secs] +5.888: [Full GC (Ergonomics) 82752K->82716K(201216K), 0.0267797 secs] +6.612: [GC (Allocation Failure) 114034K->123740K(221184K), 0.0133295 secs] +6.625: [Full GC (Ergonomics) 123740K->113436K(273920K), 0.0038620 secs] +8.864: [GC (Allocation Failure) 165577K->205660K(282624K), 0.0266288 secs] +8.890: [Full GC (Ergonomics) 205660K->164636K(384512K), 0.0206266 secs] +10.402: [GC (Allocation Failure) 216917K->215868K(418304K), 0.0119434 secs] +14.423: [GC (Allocation Failure) 309699K->308060K(418304K), 0.0319572 secs] +14.455: [Full GC (Ergonomics) 308060K->307997K(593920K), 0.0288107 secs] +16.737: [GC (Allocation Failure) 401938K->410461K(655872K), 0.0324336 secs] +16.770: [Full GC (Ergonomics) 410461K->400157K(776192K), 0.0112225 secs] diff --git a/homework/bill_jvm32_gc.log b/homework/bill_jvm32_gc.log new file mode 100644 index 0000000..5590840 --- /dev/null +++ b/homework/bill_jvm32_gc.log @@ -0,0 +1,53 @@ +Java HotSpot(TM) Client VM (25.51-b03) for windows-x86 JRE (1.8.0_51-b16), built on Jun 8 2015 18:00:23 by "java_re" with MS VC++ 10.0 (VS2010) +Memory: 4k page, physical 8271460k(4177156k free), swap 10237540k(4761452k free) +CommandLine flags: -XX:ErrorFile=C:\Users\Bill\java_error_in_IDEA_%p.log -XX:InitialHeapSize=16777216 -XX:MaxHeapSize=1073741824 -XX:+PrintGC -XX:+PrintGCTimeStamps -XX:-UseLargePagesIndividualAllocation +0.322: [GC (Allocation Failure) 4416K->1040K(15872K), 0.0043299 secs] +0.398: [GC (Allocation Failure) 5456K->3128K(15872K), 0.0035010 secs] +0.480: [GC (Allocation Failure) 7544K->4146K(15872K), 0.0033180 secs] +0.508: [GC (Allocation Failure) 8562K->4319K(15872K), 0.0028429 secs] +0.515: [GC (Allocation Failure) 8726K->4362K(15872K), 0.0014477 secs] +0.546: [GC (Allocation Failure) 8778K->5109K(15872K), 0.0023394 secs] +0.571: [GC (Allocation Failure) 9525K->5040K(15872K), 0.0018788 secs] +0.619: [GC (Allocation Failure) 9456K->6239K(15872K), 0.0045714 secs] +0.639: [GC (Allocation Failure) 10655K->9123K(15872K), 0.0026407 secs] +0.653: [GC (Allocation Failure) 13539K->12218K(16640K), 0.0025906 secs] +0.656: [Full GC (Allocation Failure) 12218K->12218K(16640K), 0.0091288 secs] +0.694: [GC (Allocation Failure) 19579K->16967K(28348K), 0.0058126 secs] +0.722: [GC (Allocation Failure) 24834K->22086K(30052K), 0.0046649 secs] +0.727: [Full GC (Allocation Failure) 22086K->22086K(30052K), 0.0112068 secs] +0.778: [GC (Allocation Failure) 35490K->30043K(51368K), 0.0098116 secs] +0.830: [GC (Allocation Failure) 44315K->34207K(51368K), 0.0127499 secs] +0.882: [GC (Allocation Failure) 48479K->38497K(52776K), 0.0119572 secs] +0.894: [Full GC (Allocation Failure) 38497K->38497K(52776K), 0.0277656 secs] +1.057: [GC (Allocation Failure) 61409K->43770K(88932K), 0.0232897 secs] +1.567: [GC (Allocation Failure) 68410K->49161K(88932K), 0.0228224 secs] +1.774: [GC (Allocation Failure) 73801K->54209K(88932K), 0.0144856 secs] +2.660: [Full GC (Metadata GC Threshold) 69102K->42564K(88932K), 0.0449064 secs] +3.355: [GC (Allocation Failure) 71044K->47328K(102944K), 0.0108748 secs] +3.904: [GC (Allocation Failure) 75808K->52938K(102944K), 0.0210788 secs] +4.027: [GC (Allocation Failure) 81418K->56317K(102944K), 0.0125392 secs] +4.355: [GC (Allocation Failure) 84797K->57277K(102944K), 0.0092810 secs] +4.707: [GC (Allocation Failure) 85757K->60982K(102944K), 0.0120037 secs] +5.090: [Full GC (Metadata GC Threshold) 75873K->58456K(102944K), 0.0899523 secs] +6.323: [GC (Allocation Failure) 97496K->67513K(141332K), 0.0300465 secs] +7.357: [GC (Allocation Failure) 102999K->78400K(141332K), 0.0350410 secs] +7.672: [Full GC (Metadata GC Threshold) 87567K->65335K(141332K), 0.1162865 secs] +9.394: [GC (Allocation Failure) 108983K->70979K(157984K), 0.0274511 secs] +10.300: [GC (Allocation Failure) 114627K->74486K(157984K), 0.0302167 secs] +10.983: [GC (Allocation Failure) 118134K->76286K(157984K), 0.0271287 secs] +11.780: [GC (Allocation Failure) 119934K->79470K(157984K), 0.0233156 secs] +13.111: [GC (Allocation Failure) 123118K->81341K(157984K), 0.0249988 secs] +13.268: [Full GC (Metadata GC Threshold) 85218K->66155K(157984K), 0.2353434 secs] +14.551: [GC (Allocation Failure) 110379K->69711K(159988K), 0.0204552 secs] +15.486: [GC (Allocation Failure) 113935K->71958K(159988K), 0.0192550 secs] +16.629: [GC (Allocation Failure) 116182K->75935K(159988K), 0.0278797 secs] +17.686: [GC (Allocation Failure) 120159K->83147K(159988K), 0.0294754 secs] +18.268: [GC (Allocation Failure) 127371K->94111K(159988K), 0.0380868 secs] +18.725: [GC (Allocation Failure) 138335K->105327K(159988K), 0.0443552 secs] +23.391: [GC (Allocation Failure) 149551K->113632K(159988K), 0.0503248 secs] +57.356: [GC (Allocation Failure) 157856K->118215K(163324K), 0.0372537 secs] +57.394: [Full GC (Allocation Failure) 118215K->78406K(163324K), 0.2369982 secs] +92.345: [GC (Allocation Failure) 130821K->81545K(189624K), 0.0168981 secs] +117.588: [GC (Allocation Failure) 133961K->82092K(189624K), 0.0214815 secs] +139.777: [GC (Allocation Failure) 134508K->82945K(189624K), 0.0225465 secs] +219.740: [GC (Allocation Failure) 135361K->82582K(189624K), 0.0140721 secs] diff --git a/homework/heap_dump_compare.png b/homework/heap_dump_compare.png new file mode 100644 index 0000000..5d43d19 Binary files /dev/null and b/homework/heap_dump_compare.png differ diff --git a/homework/jvmgc_2.png b/homework/jvmgc_2.png new file mode 100644 index 0000000..aa759c4 Binary files /dev/null and b/homework/jvmgc_2.png differ diff --git a/homework/jvmgc_3.png b/homework/jvmgc_3.png new file mode 100644 index 0000000..38820b9 Binary files /dev/null and b/homework/jvmgc_3.png differ diff --git a/homework/jvmgc_4.png b/homework/jvmgc_4.png new file mode 100644 index 0000000..06017d8 Binary files /dev/null and b/homework/jvmgc_4.png differ diff --git a/homework/settings.xml b/homework/settings.xml new file mode 100644 index 0000000..70af453 --- /dev/null +++ b/homework/settings.xml @@ -0,0 +1,12 @@ + + + + CN + OSChina Central + http://maven.oschina.net/content/groups/public/ + central + + + /home/bill/.m2/repository + diff --git a/pom.xml b/pom.xml index 904b2df..decdf8d 100644 --- a/pom.xml +++ b/pom.xml @@ -17,18 +17,37 @@ 1.8 + + org.springframework.boot + spring-boot-maven-plugin + + + + org.springframework.boot + spring-boot-starter-parent + 1.3.5.RELEASE + + + + UTF-8 + 1.8 + + - com.fasterxml.jackson.core - jackson-annotations - 2.4.5 + org.springframework.boot + spring-boot-starter-web - com.fasterxml.jackson.core - jackson-databind - 2.4.5 + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-test + test org.apache.commons @@ -36,22 +55,35 @@ 1.2 - junit - junit - 4.12 + org.dbunit + dbunit + 2.5.1 test + + + junit + junit + + - com.google.guava - guava - 13.0.1 + com.github.springtestdbunit + spring-test-dbunit + 1.2.1 + test com.google.guava guava 13.0.1 - + + com.h2database + h2 + runtime + + + - \ No newline at end of file + diff --git a/src/main/java/com/epam/data/EnrichRoadAccident.java b/src/main/java/com/epam/data/EnrichRoadAccident.java new file mode 100644 index 0000000..0aaba9f --- /dev/null +++ b/src/main/java/com/epam/data/EnrichRoadAccident.java @@ -0,0 +1,37 @@ +package com.epam.data; + +import java.time.LocalTime; + +/** + * Created by bill on 16-5-2. + */ +public class EnrichRoadAccident extends RoadAccident { + private String ForceContact; + private TimeOfDay timeOfDay; + + public EnrichRoadAccident(RoadAccidentBuilder builder) { + super(builder); + } + +/* + public EnrichRoadAccident(RoadAccident roadAccident) { + super(builder); + } +*/ + + public String getForceContact() { + return ForceContact; + } + + public void setForceContact(String forceContact) { + ForceContact = forceContact; + } + + public TimeOfDay getTimeOfDay() { + return timeOfDay; + } + + public void setTimeOfDay(LocalTime time) { + this.timeOfDay = TimeOfDay.getTimeOfDay(time); + } +} diff --git a/src/main/java/com/epam/data/RoadAccident.java b/src/main/java/com/epam/data/RoadAccident.java index 9548fa5..6441ed2 100644 --- a/src/main/java/com/epam/data/RoadAccident.java +++ b/src/main/java/com/epam/data/RoadAccident.java @@ -146,4 +146,35 @@ public String getRoadSurfaceConditions() { public void setRoadSurfaceConditions(String roadSurfaceConditions) { this.roadSurfaceConditions = roadSurfaceConditions; } + + // Patch for home work 3, should extend RoadAccident class + private String ForceContact = null; + private TimeOfDay timeOfDay; + + public String getForceContact() { + return ForceContact; + } + + public void setForceContact(String forceContact) { + ForceContact = forceContact; + } + + public TimeOfDay getTimeOfDay() { + return timeOfDay; + } + + public void setTimeOfDay(LocalTime time) { + this.timeOfDay = TimeOfDay.getTimeOfDay(time); + } + + public String toCSV() { + return String.join(",", + accidentId, + policeForce, + ForceContact, + time.toString(), + timeOfDay.toString() + ); + } + } diff --git a/src/main/java/com/epam/data/TimeOfDay.java b/src/main/java/com/epam/data/TimeOfDay.java new file mode 100644 index 0000000..4b0558f --- /dev/null +++ b/src/main/java/com/epam/data/TimeOfDay.java @@ -0,0 +1,50 @@ +package com.epam.data; + +import java.time.LocalTime; + +/** + * Created by bill on 16-5-2. + * + */ + +public enum TimeOfDay { + MORNING, AFTERNOON, EVENING, NIGHT; + + public static TimeOfDay getTimeOfDay(LocalTime time) { + int hour = time.getHour(); + switch (hour/6) { + case 0: + return TimeOfDay.NIGHT; + case 1: + return TimeOfDay.MORNING; + case 2: + return TimeOfDay.AFTERNOON; + case 3: + default: + return TimeOfDay.EVENING; + } + } + + public int getCategory() { + if(this.equals(TimeOfDay.MORNING) | this.equals(TimeOfDay.AFTERNOON)) { + return 1; + } else { + return 0; + } + } +} + +/* + MORNING - 6 am to 12 pm + AFTERNOON - 12 pm to 6 pm + EVENING - 6 pm to 12 am + NIGHT - 12 am to 6 am + MORNING, + AFTERNOON(AFTERNOON"), + EVENING("EVENING"), + NIGHT("NIGHT"); + MORNING, + AFTERNOON, + EVENING, + NIGHT; +*/ diff --git a/src/main/java/com/epam/dataservice/AccidentBatchLoader.java b/src/main/java/com/epam/dataservice/AccidentBatchLoader.java index 016a2b1..b39854c 100644 --- a/src/main/java/com/epam/dataservice/AccidentBatchLoader.java +++ b/src/main/java/com/epam/dataservice/AccidentBatchLoader.java @@ -1,6 +1,7 @@ package com.epam.dataservice; +import com.epam.data.EnrichRoadAccident; import com.epam.data.RoadAccident; import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVParser; @@ -13,9 +14,12 @@ import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; +import java.util.concurrent.atomic.AtomicInteger; public class AccidentBatchLoader implements Callable { + private static AtomicInteger serialNo = new AtomicInteger(); + private String taskName = this.getClass().getSimpleName() + serialNo.incrementAndGet(); private Integer batchSize; private BlockingQueue> dataQueue; private String dataFileName; @@ -42,11 +46,13 @@ public Integer call() throws Exception { dataCount = dataCount + roadAccidentBatch.size(); if(roadAccidentBatch.isEmpty()){ isDataLoadFinished = true; + System.out.println(taskName + " Finished"); }else{ ++batchCount; System.out.println(" Completed reading " + dataCount + " in " + batchCount + " batches for " + dataFileName); +// System.out.println(taskName + " Trying to put, Queue.remainingCapacity()="+dataQueue.remainingCapacity()); + dataQueue.put(roadAccidentBatch); } - dataQueue.put(roadAccidentBatch); } //dataQueue.put(roadAccidentBatch); //Epmty batch can be used as identifier for end of record production return dataCount; @@ -61,7 +67,7 @@ private List getNextBatch(Iterator recordIterator){ List roadAccidentBatch = new ArrayList(); int recordCount = 0; RoadAccident roadAccidentItem = null; - while(recordCount <= batchSize && recordIterator.hasNext() ){ + while(recordCount < batchSize && recordIterator.hasNext() ){ roadAccidentItem = roadAccidentParser.parseRecord(recordIterator.next()); if(roadAccidentItem != null){ roadAccidentBatch.add(roadAccidentItem); diff --git a/src/main/java/com/epam/dataservice/AccidentBatchProcessor.java b/src/main/java/com/epam/dataservice/AccidentBatchProcessor.java new file mode 100644 index 0000000..c29ed5c --- /dev/null +++ b/src/main/java/com/epam/dataservice/AccidentBatchProcessor.java @@ -0,0 +1,97 @@ +package com.epam.dataservice; + +import com.epam.data.RoadAccident; + +import java.util.List; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Created by bill on 16-5-1. + */ +public class AccidentBatchProcessor implements Runnable { + private static AtomicInteger serialNo = new AtomicInteger(); +// For record Enrichment + private static PoliceForceService policeForceService = new PoliceForceService(); + private static ExecutorService enrichExecutor = Executors.newFixedThreadPool(HomeWork3.MAX_ENRICH_THREADS); + private CompletionService enrichCompletionService = new ExecutorCompletionService(enrichExecutor); + + private String taskName = getClass().getSimpleName() + serialNo.incrementAndGet(); + private BlockingQueue> readQueue; + private List> writeQueues; + + public AccidentBatchProcessor(BlockingQueue> readQueue, List> writeQueues){ + this.readQueue = readQueue; + this.writeQueues = writeQueues; + } + + @Override + public void run() { + int dataCounter = 0; + List consumedData = null; + Boolean done = false; + try { + while (!done) { + consumedData = readQueue.take(); + if (consumedData != null ) { + if(!consumedData.isEmpty()) { + dataCounter += consumedData.size(); + System.out.println(" Consumed " + dataCounter + " records from " + taskName); + enrichRoadAccident(consumedData); + } else { + done = true; + System.out.println(taskName + " Get empty data," + " Quit"); + } + } else { + System.out.println(taskName + " Abort"); + break; + } + } +// System.out.println(taskName + " Finished"); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + + public void enrichRoadAccident(List consumedData) { + for (RoadAccident record : consumedData) { + enrichCompletionService.submit(new enrichRecord(record)); + } + for (int i = 0; i < consumedData.size(); i++) { + try { + RoadAccident record = enrichCompletionService.take().get(); + int index = record.getTimeOfDay().getCategory(); + writeQueues.get(index).put(record); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } + } + } + + private class enrichRecord implements Callable { + private RoadAccident record; + public enrichRecord(RoadAccident record) { + this.record = record; + } + + @Override + public RoadAccident call() { + String contactNo = null; + try { + contactNo = policeForceService.getContactNo(record.getPoliceForce()); + } catch (RuntimeException e) { + System.out.println("Catch policeForceService.getContactNo exception"); + e.printStackTrace(); + contactNo = null; + } + record.setForceContact(contactNo); + record.setTimeOfDay(record.getTime()); +// System.out.println(taskName + " policeForceService=" + contactNo); + return record; + } + } + +} diff --git a/src/main/java/com/epam/dataservice/AccidentBatchWriter.java b/src/main/java/com/epam/dataservice/AccidentBatchWriter.java new file mode 100644 index 0000000..74ca964 --- /dev/null +++ b/src/main/java/com/epam/dataservice/AccidentBatchWriter.java @@ -0,0 +1,50 @@ +package com.epam.dataservice; + +import com.epam.data.RoadAccident; + +import java.io.FileNotFoundException; +import java.io.PrintWriter; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Created by bill on 16-5-2. + */ +public class AccidentBatchWriter implements Runnable { + private static AtomicInteger serialNo = new AtomicInteger(); + private String taskName = this.getClass().getSimpleName() + serialNo.incrementAndGet(); + private BlockingQueue dataQueue; + private String dataFileName; + + public AccidentBatchWriter(BlockingQueue dataQueue, String dataFileName) { + this.dataQueue = dataQueue; + this.dataFileName = "src/main/resources/" + dataFileName; + } + + @Override + public void run() { + Boolean done = false; + int dataCounter = 0; + + System.out.println(taskName + " Write to file: " + dataFileName); + try (PrintWriter csv = new PrintWriter(dataFileName)) { + // CSV Header + while (!done) { + RoadAccident record = dataQueue.take(); + if(record.getAccidentId() != null) { + // write to file + csv.println(record.toCSV()); + dataCounter++; + } else { + done = true; + System.out.println(taskName + " Quit, Total write: " + dataCounter); + } + } + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + + } +} diff --git a/src/main/java/com/epam/dataservice/HomeWork3.java b/src/main/java/com/epam/dataservice/HomeWork3.java new file mode 100644 index 0000000..9c89b43 --- /dev/null +++ b/src/main/java/com/epam/dataservice/HomeWork3.java @@ -0,0 +1,154 @@ +package com.epam.dataservice; + +import com.epam.data.RoadAccident; +import com.epam.data.RoadAccidentBuilder; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Created by bill on 16-5-1. + */ +public class HomeWork3 { + public final int MAX_READ_THREADS = 2; + public final int MAX_PROC_THREADS = 4; + public static final int MAX_ENRICH_THREADS = 100; + public final int MAX_WRITE_THREADS = 2; + public static final int MAX_BATCH_SIZE = 40000; + public static final String outputFileNames[] = {"NighttimeAccidents.csv", "DaytimeAccidents.csv"}; + + private LinkedList fileList; + private BlockingQueue> readQueue; + private ExecutorService readExecutor; + private List> readTaskList = new ArrayList<>(); + + private ExecutorService procExecutor; + + private List> writeQueues = new ArrayList<>(MAX_WRITE_THREADS); + private ExecutorService writeExecutor; + + public HomeWork3(LinkedList fileList) { + this.fileList = fileList; + } + + public void startTask() { + readQueue = new ArrayBlockingQueue>(MAX_PROC_THREADS); + for (int i = 0; i < MAX_WRITE_THREADS; i++) { + writeQueues.add( new ArrayBlockingQueue(MAX_BATCH_SIZE/2) ); + } + + readExecutor = Executors.newFixedThreadPool(MAX_READ_THREADS); + for (String file : fileList ) { + FutureTask readTask = new FutureTask( + new AccidentBatchLoader(MAX_BATCH_SIZE,readQueue,file)); + readTaskList.add(readTask); + readExecutor.submit(readTask); + } + + procExecutor = Executors.newFixedThreadPool(MAX_PROC_THREADS); + for (int i = 0; i < MAX_PROC_THREADS; i++) { + procExecutor.submit(new AccidentBatchProcessor(readQueue, writeQueues)); + } + + writeExecutor = Executors.newFixedThreadPool(MAX_WRITE_THREADS); + for (int i = 0; i < MAX_WRITE_THREADS; i++) { + writeExecutor.submit(new AccidentBatchWriter(writeQueues.get(i),outputFileNames[i])); + } + System.out.println("All task submitted"); + } + + public void run() { + startTask(); + + try { + readQueueFinish(); + endDatLoading(procExecutor); + for (int i = 0; i < MAX_WRITE_THREADS; i++) { + System.out.println("Send quit message to writeQueue, count: " + Integer.toString(i+1)); + writeQueues.get(i).put( new RoadAccidentBuilder(null).build() ); + } + endDatLoading(writeExecutor); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void readQueueFinish() throws Exception { + Boolean taskStatus; + while(!readTaskList.isEmpty()) { + Iterator> iter = readTaskList.iterator(); + while (iter.hasNext()) { + if ( iter.next().isDone() ) { + iter.remove(); + } + } + TimeUnit.SECONDS.sleep(1); + } + + readExecutor.submit(new Runnable() { + @Override + public void run() { + List emptyAccidentList = new ArrayList(1); + for (int i = 0; i < MAX_PROC_THREADS; i++) { + System.out.println("Send quit message to readQueue, count: " + Integer.toString(i+1)); + try { + readQueue.put(emptyAccidentList); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + }); + + readExecutor.shutdown(); + try { + taskStatus = readExecutor.awaitTermination(5, TimeUnit.MINUTES); + if (taskStatus) { + System.out.println("Data loading finished"); + } else { + System.out.println("Data loading task timeout"); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + + } + + public static void main(String[] args) throws Exception{ + } + + private static class ReadTerminator implements Runnable { + public static List emptyAccidentList = new ArrayList(1); + private BlockingQueue> dataQueue; + + public ReadTerminator(BlockingQueue> dataQueue) { + this.dataQueue = dataQueue; + } + + @Override + public void run() { + try { + System.out.println("Send quit message"); + dataQueue.put(emptyAccidentList); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + private static void endDatLoading(ExecutorService executor){ + executor.shutdown(); + try { + if (executor.awaitTermination(10, TimeUnit.SECONDS)) { + System.out.println("All task finished for " + executor.getClass().getSimpleName()); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } +// System.out.println("Data loading finished"); + } + +} diff --git a/src/main/java/com/epam/dataservice/PoliceForceService.java b/src/main/java/com/epam/dataservice/PoliceForceService.java index d871bd4..d6b581f 100644 --- a/src/main/java/com/epam/dataservice/PoliceForceService.java +++ b/src/main/java/com/epam/dataservice/PoliceForceService.java @@ -18,8 +18,8 @@ public class PoliceForceService{ private final String PHONE_PREFIX = "13163862"; private Map forceMap = new HashMap<>(); private AtomicInteger executionCount = new AtomicInteger(0); - private final int HALT_AT_EXECUTION = 10; - private final long HALT_FOR = 1000 * 2; + private final int HALT_AT_EXECUTION = 20000; + private final long HALT_FOR = 100 * 2; public PoliceForceService(){ init(); @@ -41,15 +41,16 @@ public void init(){ } public String getContactNo(String policeForceName){ - if(executionCount.get() == HALT_AT_EXECUTION){ + if(executionCount.incrementAndGet() % HALT_AT_EXECUTION == 0){ try { + System.out.println("Oops:"+executionCount.get()); Thread.sleep(HALT_FOR); } catch (InterruptedException e) { throw new RuntimeException(e); } } Integer idx = forceMap.get(policeForceName); - executionCount.incrementAndGet(); +// executionCount.incrementAndGet(); return idx == null ? PHONE_PREFIX : PHONE_PREFIX + idx.toString() ; } diff --git a/src/main/java/com/epam/dataservice/RoadAccidentParser.java b/src/main/java/com/epam/dataservice/RoadAccidentParser.java index c78509c..39dfa8f 100644 --- a/src/main/java/com/epam/dataservice/RoadAccidentParser.java +++ b/src/main/java/com/epam/dataservice/RoadAccidentParser.java @@ -1,5 +1,6 @@ package com.epam.dataservice; +import com.epam.data.EnrichRoadAccident; import com.epam.data.RoadAccident; import com.epam.data.RoadAccidentBuilder; import org.apache.commons.csv.CSVRecord; diff --git a/src/main/java/com/epam/processor/DataProcessor.java b/src/main/java/com/epam/processor/DataProcessor.java index d2f37d7..e52aca5 100644 --- a/src/main/java/com/epam/processor/DataProcessor.java +++ b/src/main/java/com/epam/processor/DataProcessor.java @@ -1,11 +1,12 @@ package com.epam.processor; import com.epam.data.RoadAccident; -import com.google.common.collect.Multimap; +import com.google.common.base.*; +import com.google.common.collect.*; -import java.util.Collection; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.Optional; +import java.util.stream.Collectors; /** * This is to be completed by mentees @@ -18,7 +19,12 @@ public DataProcessor(List roadAccidentList){ this.roadAccidentList = roadAccidentList; } - + public static boolean inRect(RoadAccident roadAccident, float minLongitude, float maxLongitude, float minLatitude, float maxLatitude) { + float longitude = roadAccident.getLongitude(); + float latitude = roadAccident.getLatitude(); + return ( (longitude>=minLongitude && longitude<=maxLongitude) + && (latitude>=minLatitude && latitude<=maxLatitude) ); + } // First try to solve task using java 7 style for processing collections /** @@ -27,6 +33,12 @@ public DataProcessor(List roadAccidentList){ * @return */ public RoadAccident getAccidentByIndex7(String index){ + Iterator iter = roadAccidentList.iterator(); + while(iter.hasNext()) { + RoadAccident rec = iter.next(); + if (rec.getAccidentId().equals(index)) + return rec; + } return null; } @@ -40,7 +52,13 @@ public RoadAccident getAccidentByIndex7(String index){ * @return */ public Collection getAccidentsByLocation7(float minLongitude, float maxLongitude, float minLatitude, float maxLatitude){ - return null; + List result = new ArrayList<>(); + for (RoadAccident rec : roadAccidentList ) { + if ( inRect(rec, minLongitude, maxLongitude, minLatitude, maxLatitude) ) { + result.add(rec); + } + } + return result; } /** @@ -51,7 +69,16 @@ public Collection getAccidentsByLocation7(float minLongitude, floa * @return */ public Map getCountByRoadSurfaceCondition7(){ - return null; + Map result = new HashMap<>(); + Multiset roadSurfaceCondition = HashMultiset.create(); + for (RoadAccident rec : roadAccidentList ) { + roadSurfaceCondition.add(rec.getRoadSurfaceConditions()); + } + for (String key : roadSurfaceCondition.elementSet() ) { +// System.out.println(key+" count:"+roadSurfaceCondition.count(key)); + result.put(key, Long.valueOf(roadSurfaceCondition.count(key))); + } + return result; } /** @@ -59,8 +86,43 @@ public Map getCountByRoadSurfaceCondition7(){ * as example if there were 10 accidence in rain, 5 in snow, 6 in sunny and 1 in foggy, then your result list should contain {rain, sunny, snow} - top three in decreasing order * @return */ + public List getTopThreeWeatherCondition6(){ + + Multiset weatherConditions = HashMultiset.create(); + for (RoadAccident rec : roadAccidentList ) { + weatherConditions.add(rec.getWeatherConditions()); + } + ImmutableSortedMap.Builder builder = new ImmutableSortedMap + .Builder(Ordering.natural().reverse()); + for (String key : weatherConditions.elementSet() ) { + String countStr = String.format("%08d %s", weatherConditions.count(key), key); + System.out.println(countStr); + builder.put(countStr, key); + } + ImmutableSortedMap sortedConditions = builder.build(); + List result = sortedConditions.values().asList(); +// System.out.println(result); +/* + // If no duplicate count value for WeatherCondition, we can use BiMap + // Another Solution + Multimap sortedConditions = TreeMultimap.create(Ordering.natural().reverse(), Ordering.natural()); + for (String key : weatherConditions.elementSet() ) { + sortedConditions.put(weatherConditions.count(key), key); + } + List result = ImmutableList.copyOf(sortedConditions.values()); +*/ + return result.subList(0,3); + } + public List getTopThreeWeatherCondition7(){ - return null; + Multiset weatherConditions = HashMultiset.create(); + for (RoadAccident rec : roadAccidentList ) { + weatherConditions.add(rec.getWeatherConditions()); + } + List> topKList = Ordering + .from(Comparator.>comparingInt(e -> e.getCount())) + .greatestOf(weatherConditions.entrySet(),3); + return Lists.transform(topKList, e -> e.getElement()); } /** @@ -71,7 +133,11 @@ public List getTopThreeWeatherCondition7(){ * @return */ public Multimap getAccidentIdsGroupedByAuthority7(){ - return null; + ImmutableMultimap.Builder builder = new ImmutableMultimap.Builder(); + for (RoadAccident rec : roadAccidentList ) { + builder.put(rec.getDistrictAuthority(), rec.getAccidentId()); + } + return builder.build(); } @@ -80,7 +146,10 @@ public Multimap getAccidentIdsGroupedByAuthority7(){ public RoadAccident getAccidentByIndex(String index){ - return null; + return roadAccidentList.stream() + .filter(r -> r.getAccidentId().equals(index)) + .findFirst().orElse(null); + } @@ -93,7 +162,9 @@ public RoadAccident getAccidentByIndex(String index){ * @return */ public Collection getAccidentsByLocation(float minLongitude, float maxLongitude, float minLatitude, float maxLatitude){ - return null; + return roadAccidentList.stream() + .filter(r -> inRect(r, minLongitude, maxLongitude, minLatitude, maxLatitude) ) + .collect(Collectors.toList()); } /** @@ -101,7 +172,14 @@ public Collection getAccidentsByLocation(float minLongitude, float * @return */ public List getTopThreeWeatherCondition(){ - return null; + Map weatherCondition = roadAccidentList.stream() + .collect(Collectors.groupingBy(RoadAccident::getWeatherConditions, + Collectors.counting())); + return weatherCondition.entrySet().stream() + .sorted(Map.Entry.comparingByValue().reversed()) + .limit(3) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); } /** @@ -109,7 +187,11 @@ public List getTopThreeWeatherCondition(){ * @return */ public Map getCountByRoadSurfaceCondition(){ - return null; + Map roadSurfaceCondition = roadAccidentList.stream() + .collect(Collectors.groupingBy(RoadAccident::getRoadSurfaceConditions, + Collectors.counting())); + + return roadSurfaceCondition; } /** @@ -117,7 +199,11 @@ public Map getCountByRoadSurfaceCondition(){ * @return */ public Map> getAccidentIdsGroupedByAuthority(){ - return null; + Map> authority = roadAccidentList.stream() + .collect(Collectors.groupingBy(RoadAccident::getDistrictAuthority, + Collectors.mapping(RoadAccident::getAccidentId, Collectors.toList()))); + + return authority; } } diff --git a/src/main/java/com/epam/springboot/AccidentsRestApplication.java b/src/main/java/com/epam/springboot/AccidentsRestApplication.java new file mode 100644 index 0000000..307dc51 --- /dev/null +++ b/src/main/java/com/epam/springboot/AccidentsRestApplication.java @@ -0,0 +1,45 @@ +package com.epam.springboot; + +import com.epam.springboot.modal.Accidents; +import com.epam.springboot.modal.RoadConditions; +import com.epam.springboot.repository.AccidentRepository; +import com.epam.springboot.repository.AccidentService; +import com.epam.springboot.repository.AccidentServiceImpl; +import com.epam.springboot.repository.RoadConditionRepository; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ApplicationContext; + +import java.util.List; + +/** + * Created by bill on 16-5-22. + */ + +@SpringBootApplication +public class AccidentsRestApplication { + public static ApplicationContext getContext() { + return context; + } + + private static ApplicationContext context; + public static void main(String[] args) { + SpringApplication springApplication = new SpringApplication(); + context = SpringApplication.run(AccidentsRestApplication.class, args); + initDB(context); + } + + private static void initDB(ApplicationContext ctx){ +// AccidentRepository accidentRepository= ctx.getBean(AccidentRepository.class); +// RoadConditionRepository roadConditionRepository= context.getBean(RoadConditionRepository.class); +// roadConditionRepository.save(new RoadConditions(12,"Test Sample")); +// AccidentServiceImpl accidentService = (AccidentServiceImpl)context.getBean("accidentService"); +// AccidentServiceImpl accidentServiceImpl = context.getBean(AccidentServiceImpl.class); +// accidentRepository.save(new Accidents("200901BS70001")); +// accidentRepository.save(new Accidents("200901BS70002",3)); +// List accidentsList = accidentRepository.findAll(); +// System.out.println(accidentsList); + } +// @Bean + +} diff --git a/src/main/java/com/epam/springboot/controller/AccidentController.java b/src/main/java/com/epam/springboot/controller/AccidentController.java new file mode 100644 index 0000000..b1f3efa --- /dev/null +++ b/src/main/java/com/epam/springboot/controller/AccidentController.java @@ -0,0 +1,73 @@ +package com.epam.springboot.controller; + +import com.epam.springboot.modal.Accidents; +import com.epam.springboot.repository.AccidentRepository; +import com.epam.springboot.repository.AccidentService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.orm.ObjectRetrievalFailureException; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * Created by bill on 16-5-22. + */ +@RestController +//@RequestMapping(value = "/api/") +public class AccidentController { + private static org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(AccidentController.class); + @Autowired + AccidentRepository repository; + @Autowired + AccidentService accidentService; + +/* + @ExceptionHandler + void handleIllegalArgumentException(IllegalArgumentException e, + HttpServletResponse response) throws IOException { + response.sendError(HttpStatus.BAD_REQUEST.value()); + } +*/ + + @RequestMapping(method= RequestMethod.GET, value= "/accidents", headers="Accept=application/json") + public @ResponseBody + List accidents() { + return repository.findAll(); + } + + @RequestMapping(method= RequestMethod.GET, value= "/accidents/{id}", headers="Accept=application/json") + public @ResponseBody + Accidents accidents(@PathVariable String id){ + Accidents accidents= repository.findOne(id); + return accidents; + } + + @RequestMapping(method= RequestMethod.POST, value= "/accidents", headers="Accept=application/json") + public @ResponseBody String save(@RequestBody Accidents accidents){ + logger.info("creating Accidents: "+ accidents.toString()); + accidentService.createAccident(accidents); + return accidents.getId(); + } + + @RequestMapping(method= RequestMethod.PUT, value= "/accidents/{id}", headers="Accept=application/json") + public @ResponseBody String update(@RequestBody Accidents accidents, @PathVariable String id){ + logger.info("Update Accidents with: " + accidents); + if(!accidents.getId().equals(id)) { + logger.error("Expect " + id + " , but get " + accidents.getId()); + throw new IllegalArgumentException("Expect " + id + " , but get " + accidents.getId()); + } + if (repository.findOne(id) == null) { + throw new ObjectRetrievalFailureException(repository.getClass(), accidents); + } + repository.save(accidents); + return accidents.getId(); + } + + @RequestMapping(method= RequestMethod.DELETE, value= "/accidents/{id}", headers="Accept=application/json") + public @ResponseBody void delete(@PathVariable String id){ + logger.info("Delete Accidents with ID=" + id); + logger.info("[Before] Count=" + repository.count()); + repository.delete(id); + logger.info("[After] Count=" + repository.count()); + } +} diff --git a/src/main/java/com/epam/springboot/controller/HelloController.java b/src/main/java/com/epam/springboot/controller/HelloController.java new file mode 100644 index 0000000..eb9a4e0 --- /dev/null +++ b/src/main/java/com/epam/springboot/controller/HelloController.java @@ -0,0 +1,17 @@ +package com.epam.springboot.controller; + +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * Created by bill on 16-5-22. + */ +@RestController +public class HelloController { + + @RequestMapping(value = "hello/{name}") + public String hello(@PathVariable String name){ + return "hello, "+ name; + } +} diff --git a/src/main/java/com/epam/springboot/controller/RestControllerAdvice.java b/src/main/java/com/epam/springboot/controller/RestControllerAdvice.java new file mode 100644 index 0000000..3d925ea --- /dev/null +++ b/src/main/java/com/epam/springboot/controller/RestControllerAdvice.java @@ -0,0 +1,42 @@ +package com.epam.springboot.controller; + +import org.springframework.http.HttpStatus; +import org.springframework.orm.ObjectRetrievalFailureException; +import org.springframework.web.bind.annotation.*; + +/** + * Created by bill on 16-6-5. + */ +@ControllerAdvice(annotations = RestController.class) +public class RestControllerAdvice { + @ExceptionHandler(ObjectRetrievalFailureException.class) + @ResponseStatus(HttpStatus.NOT_FOUND) + @ResponseBody + public String handle(ObjectRetrievalFailureException e) { + return e.getMessage() + "\n"; + } + + @ExceptionHandler(IllegalArgumentException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ResponseBody + public String handle(IllegalArgumentException e) { + return e.getMessage() + "\n"; + } + + @ExceptionHandler(value=RuntimeException.class) + @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR) + @ResponseBody + public String handle(RuntimeException e) { + return "INTERNAL_SERVER_ERROR_RUNTIME"; + } + + @ExceptionHandler(value=Exception.class) + @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR) + @ResponseBody + public String handle(Exception e) { + //log it + return "INTERNAL_SERVER_ERROR_ALL_EXCEPTION"; + + } + +} diff --git a/src/main/java/com/epam/springboot/controller/WeatherController.java b/src/main/java/com/epam/springboot/controller/WeatherController.java new file mode 100644 index 0000000..d843eb3 --- /dev/null +++ b/src/main/java/com/epam/springboot/controller/WeatherController.java @@ -0,0 +1,94 @@ +package com.epam.springboot.controller; + +import com.epam.springboot.modal.WeatherConditions; +import com.epam.springboot.repository.WeatherConditionRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; + +/** + * Created by bill on 16-5-29. + */ +@RestController +@RequestMapping(value = "/weather") +public class WeatherController { + private static org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(WeatherController.class); + @Autowired + private WeatherConditionRepository weatherConditionRepository; + + @ExceptionHandler + void handleIllegalArgumentException(IllegalArgumentException e, + HttpServletResponse response) throws IOException { + response.sendError(HttpStatus.BAD_REQUEST.value()); + } + + @RequestMapping(method= RequestMethod.GET, value= "/init", headers="Accept=application/json") + public @ResponseBody String init() { + weatherConditionRepository.saveAndFlush(new WeatherConditions(1,"First")); + weatherConditionRepository.saveAndFlush(new WeatherConditions(2,"Second")); + weatherConditionRepository.saveAndFlush(new WeatherConditions(3,"Third")); + return "Init 2 records"; + } + + @RequestMapping(method= RequestMethod.GET, value= "/test", headers="Accept=application/json") + public @ResponseBody + WeatherConditions testWeatherConditions() { + return new WeatherConditions(20,"Test20"); + } + + @RequestMapping(method= RequestMethod.GET, headers="Accept=application/json") + public @ResponseBody + List getWeatherConditions() { + List result = weatherConditionRepository.findAll(); + logger.info(result); + return result; + } + + @RequestMapping(method= RequestMethod.GET, value= "/{id}", headers="Accept=application/json") + public @ResponseBody + WeatherConditions getWeatherCondition(@PathVariable Integer id){ + WeatherConditions weatherConditions = new WeatherConditions(20,"Test20"); + logger.warn("Autowired weatherConditionRepository is: " + weatherConditionRepository); + if (weatherConditionRepository != null) { + weatherConditions = weatherConditionRepository.findOne(id); + } + logger.info(weatherConditions); + return weatherConditions; + } + + @RequestMapping(method= RequestMethod.POST, headers="Accept=application/json") + public @ResponseBody + WeatherConditions create (@RequestBody WeatherConditions weatherConditions) { + logger.info("Create " + weatherConditions); + return weatherConditionRepository.save(weatherConditions); + } + + @RequestMapping(method= RequestMethod.PUT, value= "/{id}", headers="Accept=application/json") + public @ResponseBody + WeatherConditions put (@PathVariable Integer id, @RequestBody WeatherConditions weatherConditions) { + WeatherConditions target = weatherConditionRepository.findOne(id); + if ((target != null) && (weatherConditions.getCode().equals(target.getCode()))) { + logger.info("Put: Update " + target + " With " + weatherConditions); + target.setLabel(weatherConditions.getLabel()); + return weatherConditionRepository.save(target); + } else { + logger.error("Try to update id=" + id + " With " + weatherConditions ); + throw new IllegalArgumentException(); +// return null; + } + } + + @RequestMapping(method= RequestMethod.DELETE, value= "/{id}", headers="Accept=application/json") + public @ResponseBody + WeatherConditions delete (@PathVariable Integer id) { + WeatherConditions weatherConditions = weatherConditionRepository.findOne(id); + weatherConditionRepository.delete(id); + logger.info("Delete " + weatherConditions); + return weatherConditions; + } + +} diff --git a/src/main/java/com/epam/springboot/modal/Accidents.java b/src/main/java/com/epam/springboot/modal/Accidents.java new file mode 100644 index 0000000..94a989e --- /dev/null +++ b/src/main/java/com/epam/springboot/modal/Accidents.java @@ -0,0 +1,224 @@ +package com.epam.springboot.modal; + +import javax.persistence.*; +import java.io.Serializable; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.Date; + +/** + * Created by bill on 16-5-22. + */ +@Entity +@Table(name="Accidents") +public class Accidents { + + @Id + @Column(name="Accident_Index") + private String id; + + @ManyToOne(cascade = CascadeType.PERSIST,fetch = FetchType.EAGER) + @JoinColumn(name = "Weather_Conditions") // Column name in table Accidents + private WeatherConditions weatherConditions; + + @ManyToOne(cascade = CascadeType.PERSIST,fetch = FetchType.EAGER) + @JoinColumn(name = "Road_Surface_Conditions") // Column name in table Accidents + private RoadConditions roadSurfaceConditions; + + @Column(name="Date") + @Temporal(TemporalType.DATE) + private java.util.Date date; + + @Column(name="Time") + private String time; + + private double longitude = 0.0; + private double latitude = 0.0; + @Column(name="Police_Force") + private Integer policeForce = 1; + @Column(name="Accident_Severity") + private Integer severity = 1; + @Column(name="Number_of_Vehicles") + private Integer numberOfVehicles = 0; + @Column(name="Number_of_Casualties") + private Integer numberOfCasualties = 0; + @Column(name="Day_of_Week") + private Integer dayOfWeek; + @Column(name="Local_Authority") + private Integer districtAuthority = 1; + @Column(name="Light_Conditions") + private Integer lightConditions = -1; + + protected Accidents() { + } + + public Accidents(String id) { + this.id = id; + } + + public Accidents(String id, WeatherConditions weatherConditions, RoadConditions roadSurfaceConditions, Date date) { + this.id = id; + this.weatherConditions = weatherConditions; + this.roadSurfaceConditions = roadSurfaceConditions; + this.date = date; + } + + public Accidents(String id, WeatherConditions weatherConditions, RoadConditions roadSurfaceConditions, Date date, String time, double longitude, double latitude, Integer policeForce, Integer severity, Integer numberOfVehicles, Integer numberOfCasualties, Integer dayOfWeek, Integer districtAuthority, Integer lightConditions) { + this.id = id; + this.weatherConditions = weatherConditions; + this.roadSurfaceConditions = roadSurfaceConditions; + this.date = date; + this.time = time; + this.longitude = longitude; + this.latitude = latitude; + this.policeForce = policeForce; + this.severity = severity; + this.numberOfVehicles = numberOfVehicles; + this.numberOfCasualties = numberOfCasualties; + this.dayOfWeek = dayOfWeek; + this.districtAuthority = districtAuthority; + this.lightConditions = lightConditions; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public void setWeatherConditions(WeatherConditions weatherConditions) { + this.weatherConditions = weatherConditions; + } + public WeatherConditions getWeatherConditions() { + return weatherConditions; + } + + public void setRoadSurfaceConditions(RoadConditions roadSurfaceConditions) { + this.roadSurfaceConditions = roadSurfaceConditions; + } + + public RoadConditions getRoadSurfaceConditions() { + return roadSurfaceConditions; + } + + public Date getDate() { + return date; + } + + public void setDate(Date date) { + this.date = date; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } + + public double getLongitude() { + return longitude; + } + + public void setLongitude(double longitude) { + this.longitude = longitude; + } + + public double getLatitude() { + return latitude; + } + + public void setLatitude(double latitude) { + this.latitude = latitude; + } + + public Integer getPoliceForce() { + return policeForce; + } + + public void setPoliceForce(Integer policeForce) { + this.policeForce = policeForce; + } + + public Integer getSeverity() { + return severity; + } + + public void setSeverity(Integer severity) { + this.severity = severity; + } + + public Integer getNumberOfVehicles() { + return numberOfVehicles; + } + + public void setNumberOfVehicles(Integer numberOfVehicles) { + this.numberOfVehicles = numberOfVehicles; + } + + public Integer getNumberOfCasualties() { + return numberOfCasualties; + } + + public void setNumberOfCasualties(Integer numberOfCasualties) { + this.numberOfCasualties = numberOfCasualties; + } + + public Integer getDayOfWeek() { + return dayOfWeek; + } + + public void setDayOfWeek(Integer dayOfWeek) { + this.dayOfWeek = dayOfWeek; + } + + public Integer getDistrictAuthority() { + return districtAuthority; + } + + public void setDistrictAuthority(Integer districtAuthority) { + this.districtAuthority = districtAuthority; + } + + public Integer getLightConditions() { + return lightConditions; + } + + public void setLightConditions(Integer lightConditions) { + this.lightConditions = lightConditions; + } + + @Transient + // use getLocalTime will cause DBUnit fail + public LocalTime convertLocalTime() { + LocalTime localTime = LocalTime.parse(time, DateTimeFormatter.ofPattern("H:mm")); + return localTime; + } + + + @Override + public String toString() { + return "Accidents{" + + "id='" + id + '\'' + + ", longitude=" + longitude + + ", latitude=" + latitude + + ", policeForce=" + policeForce + + ", severity=" + severity + + ", numberOfVehicles=" + numberOfVehicles + + ", numberOfCasualties=" + numberOfCasualties + + ", date=" + date + + ", dayOfWeek=" + dayOfWeek + + ", time=" + time + + ", districtAuthority=" + districtAuthority + + ", lightConditions=" + lightConditions + + ", weatherConditions=" + weatherConditions + + ", roadSurfaceConditions=" + roadSurfaceConditions + + '}' + "\n"; + } + +} diff --git a/src/main/java/com/epam/springboot/modal/RoadConditions.java b/src/main/java/com/epam/springboot/modal/RoadConditions.java new file mode 100644 index 0000000..23a4731 --- /dev/null +++ b/src/main/java/com/epam/springboot/modal/RoadConditions.java @@ -0,0 +1,60 @@ +package com.epam.springboot.modal; + +import com.epam.springboot.modal.Accidents; +import javax.persistence.*; +import java.io.Serializable; +import java.util.Set; + +/** + * Created by Bill on 2016/5/25. + */ +@Entity +@Table(name="road_surface") +public class RoadConditions implements Serializable { + private static final long serialVersionUID = 1L; + + @Id + @Column(name="code") + private Integer code; + @Column(name="label") + private String label; + +// @OneToMany(mappedBy = "roadSurfaceConditions", cascade = CascadeType.REMOVE, fetch = FetchType.LAZY) +// private Set accidents; + + protected RoadConditions() {} + + public RoadConditions(Integer code) { + this.code = code; + this.label = "Invalid"; + } + + public RoadConditions(Integer code, String label) { + this.code = code; + this.label = label; + } + + public Integer getCode() { + return code; + } + + public void setCode(Integer code) { + this.code = code; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + @Override + public String toString() { + return "RoadConditions{" + + "code=" + code + + ", label='" + label + '\'' + + '}'; + } +} diff --git a/src/main/java/com/epam/springboot/modal/WeatherConditions.java b/src/main/java/com/epam/springboot/modal/WeatherConditions.java new file mode 100644 index 0000000..4568e95 --- /dev/null +++ b/src/main/java/com/epam/springboot/modal/WeatherConditions.java @@ -0,0 +1,52 @@ +package com.epam.springboot.modal; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * Created by bill on 16-5-28. + */ +@Entity +@Table(name="weather_conditions") +public class WeatherConditions { + + @Id + @Column(name="code") + private Integer code; + @Column(name="label") + private String label; + + protected WeatherConditions() { + } + + public WeatherConditions(Integer code, String label) { + this.code = code; + this.label = label; + } + + public WeatherConditions(Integer code) { + this.code = code; + } + + public Integer getCode() { + return code; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + @Override + public String toString() { + return "WeatherConditions{" + + "code=" + code + + ", label='" + label + '\'' + + '}'; + } +} diff --git a/src/main/java/com/epam/springboot/repository/AccidentRepository.java b/src/main/java/com/epam/springboot/repository/AccidentRepository.java new file mode 100644 index 0000000..ec934a6 --- /dev/null +++ b/src/main/java/com/epam/springboot/repository/AccidentRepository.java @@ -0,0 +1,39 @@ +package com.epam.springboot.repository; + +import com.epam.data.RoadAccident; +import com.epam.springboot.modal.Accidents; +import com.epam.springboot.modal.RoadConditions; +import com.epam.springboot.modal.WeatherConditions; +import org.springframework.cglib.core.Local; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.time.LocalDate; +import java.util.Date; +import java.util.List; + +/** + * Created by bill on 16-5-22. + */ +@Repository +public interface AccidentRepository extends JpaRepository { + + List findByRoadSurfaceConditions(RoadConditions roadCondition); + + Integer countByRoadSurfaceConditions(RoadConditions roadCondition); + + List findByDate(Date date); + List findByDateBetween(Date date1, Date date2); + + List findByRoadSurfaceConditionsAndDateBetween(RoadConditions roadCondition, Date date1, Date date2); + Integer countByWeatherConditionsAndDateBetween(WeatherConditions weatherConditions, Date date1, Date date2); + + //(clearAutomatically = true) + @Modifying + @Query("UPDATE Accidents r SET r.time = :timeOfDay WHERE r.id = :accidentId") // Use java entity name + int updateTime(@Param("accidentId") String id, @Param("timeOfDay") String timeOfDay); + +} diff --git a/src/main/java/com/epam/springboot/repository/AccidentService.java b/src/main/java/com/epam/springboot/repository/AccidentService.java new file mode 100644 index 0000000..d6f9108 --- /dev/null +++ b/src/main/java/com/epam/springboot/repository/AccidentService.java @@ -0,0 +1,16 @@ +package com.epam.springboot.repository; + +import com.epam.springboot.modal.Accidents; + +import javax.persistence.criteria.CriteriaBuilder; +import java.util.Map; + +/** + * Created by bill on 16-5-28. + */ +public interface AccidentService { + Map getAccidentCountGroupByRoadCondition(); + Map getAccidentCountGroupByWeatherConditionAndYear(String year); + int updateAccidentTimeByDate(String strDate); + Accidents createAccident(Accidents accidents); +} diff --git a/src/main/java/com/epam/springboot/repository/AccidentServiceImpl.java b/src/main/java/com/epam/springboot/repository/AccidentServiceImpl.java new file mode 100644 index 0000000..3f1b97f --- /dev/null +++ b/src/main/java/com/epam/springboot/repository/AccidentServiceImpl.java @@ -0,0 +1,100 @@ +package com.epam.springboot.repository; + +import com.epam.data.TimeOfDay; +import com.epam.springboot.modal.Accidents; +import com.epam.springboot.modal.RoadConditions; +import com.epam.springboot.modal.WeatherConditions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalTime; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Created by bill on 16-5-28. + */ +@Component("accidentService") +@Transactional +public class AccidentServiceImpl implements AccidentService { + + public static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + + private final AccidentRepository repository; + private final RoadConditionRepository roadConditionRepository; + private final WeatherConditionRepository weatherConditionRepository; + + @Autowired + public AccidentServiceImpl(AccidentRepository repository, + RoadConditionRepository roadConditionRepository, + WeatherConditionRepository weatherConditionRepository) { + this.repository = repository; + this.roadConditionRepository = roadConditionRepository; + this.weatherConditionRepository = weatherConditionRepository; + } + + @Override + public Map getAccidentCountGroupByRoadCondition() { + Map result = new HashMap<>(); + List roadConditionsList = roadConditionRepository.findAll(); + + for (RoadConditions roadCondition : roadConditionsList ) { + result.put(roadCondition.getLabel(), + repository.countByRoadSurfaceConditions(roadCondition)); + } + return result; + } + + @Override + public Map getAccidentCountGroupByWeatherConditionAndYear(String year) { + Map result = new HashMap<>(); + try { + Date date1 = dateFormat.parse(year + "-01-01"); + Date date2 = dateFormat.parse(year + "-12-31"); + List weatherConditionsList = weatherConditionRepository.findAll(); + + for (WeatherConditions weatherCondition : weatherConditionsList ) { + result.put(weatherCondition.getLabel(), + repository.countByWeatherConditionsAndDateBetween(weatherCondition, date1, date2)); + } + } catch (ParseException e) { + e.printStackTrace(); + } + + return result; + } + + @Override + public int updateAccidentTimeByDate(String strDate) { + try { + Date date = dateFormat.parse(strDate); + List accidentsList = repository.findByDate(date); + for (Accidents accidents : accidentsList) { + String id = accidents.getId(); + LocalTime localTime = accidents.convertLocalTime(); + String timeOfDay = TimeOfDay.getTimeOfDay(localTime).toString(); +// System.out.println(id + " => Covert " + localTime + " to " + timeOfDay); + repository.updateTime(id,timeOfDay); + } + return accidentsList.size(); + } catch (ParseException e) { + e.printStackTrace(); + } + return 0; + } + + @Override + public Accidents createAccident(Accidents accidents) { + Integer id; + id = accidents.getWeatherConditions().getCode(); + accidents.setWeatherConditions(weatherConditionRepository.findOne(id)); + id = accidents.getRoadSurfaceConditions().getCode(); + accidents.setRoadSurfaceConditions(roadConditionRepository.findOne(id)); + return repository.save(accidents); + } +} diff --git a/src/main/java/com/epam/springboot/repository/RoadConditionRepository.java b/src/main/java/com/epam/springboot/repository/RoadConditionRepository.java new file mode 100644 index 0000000..171bc8f --- /dev/null +++ b/src/main/java/com/epam/springboot/repository/RoadConditionRepository.java @@ -0,0 +1,12 @@ +package com.epam.springboot.repository; + +import com.epam.springboot.modal.RoadConditions; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +/** + * Created by Bill on 2016/5/26. + */ +@Repository +public interface RoadConditionRepository extends JpaRepository { +} diff --git a/src/main/java/com/epam/springboot/repository/WeatherConditionRepository.java b/src/main/java/com/epam/springboot/repository/WeatherConditionRepository.java new file mode 100644 index 0000000..eeeafff --- /dev/null +++ b/src/main/java/com/epam/springboot/repository/WeatherConditionRepository.java @@ -0,0 +1,12 @@ +package com.epam.springboot.repository; + +import com.epam.springboot.modal.WeatherConditions; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +/** + * Created by bill on 16-5-28. + */ +@Repository +public interface WeatherConditionRepository extends JpaRepository { +} diff --git a/src/main/resources/Accidents.csv b/src/main/resources/Accidents.csv new file mode 100644 index 0000000..ec3aa3b --- /dev/null +++ b/src/main/resources/Accidents.csv @@ -0,0 +1,17 @@ +Accident_Index,Longitude,Latitude,Police_Force,Accident_Severity,Number_of_Vehicles,Number_of_Casualties,Date,Day_of_Week,Time,Local_Authority,Light_Conditions,Weather_Conditions,Road_Surface_Conditions +200901BS70001,-0.201349,51.512273,1,2,2,1,2009-01-01,5,15:11,12,1,1,1 +200901BS70002,-0.199248,51.514399,1,2,2,11,2009-01-05,2,10:59,12,1,1,2 +200901BS70003,-0.179599,51.486668,1,3,2,1,2009-01-04,1,14:19,12,1,1,1 +200901BS70004,-0.20311,51.507804,1,2,2,1,2009-01-05,2,8:10,12,1,8,4 +200901BS70005,-0.173445,51.482076,1,2,2,1,2009-01-06,3,17:25,12,4,1,1 +200901BS70006,-0.185525,51.493415,1,3,2,3,2009-01-01,5,11:48,12,1,1,1 +200901BS70007,-0.178561,51.480177,1,2,2,1,2009-01-08,5,13:58,12,1,1,1 +200901BS70008,-0.178524,51.491957,1,3,1,1,2009-01-02,6,13:18,12,1,1,1 +200901BS70009,-0.167395,51.49646,1,3,1,2,2009-01-07,4,12:15,12,1,1,1 +200901BS70010,-0.183275,51.48115,1,3,1,1,2009-01-10,7,9:52,12,1,8,2 +200901BS70011,-0.173445,51.482076,1,3,2,1,2009-01-07,4,0:09,12,4,1,1 +200901BS70012,-0.183013,51.494995,1,3,1,1,2009-01-16,6,17:49,12,4,1,1 +200901BS70015,-0.206779,51.498778,1,3,2,1,2009-01-12,2,14:00,12,1,2,2 +200901BS70016,-0.209082,51.506187,1,3,2,1,2009-01-09,6,8:15,12,1,2,2 +200901BS70017,-0.169548,51.493077,1,3,2,1,2009-01-17,7,12:15,12,1,1,1 +200901BS70019,-0.173445,51.482076,1,2,2,1,2009-01-25,1,22:05,12,4,1,1 diff --git a/src/main/resources/prep_accidents.awk b/src/main/resources/prep_accidents.awk new file mode 100755 index 0000000..4fe6972 --- /dev/null +++ b/src/main/resources/prep_accidents.awk @@ -0,0 +1,18 @@ +#!/usr/bin/awk -f + +# Process original Road Accidents CSV file for testing + +BEGIN { + FS=","; + OFS=","; +# format = "%s,%s,%06.0s,%s,%s\n"; +} +{ + if (NR > 1) { + split($8,array,"/"); +# printf("%s-%s-%s\n", array[3],array[2],array[1]) + $8 = sprintf("%s-%s-%s", array[3],array[2],array[1]); + } + print($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14); +} + diff --git a/src/main/resources/table-ordering-all.txt b/src/main/resources/table-ordering-all.txt new file mode 100644 index 0000000..b49c461 --- /dev/null +++ b/src/main/resources/table-ordering-all.txt @@ -0,0 +1,6 @@ +accident_severity +disrict_authority +light_conditions +police_force +road_surface +weather_conditions \ No newline at end of file diff --git a/src/main/resources/table-ordering.txt b/src/main/resources/table-ordering.txt new file mode 100644 index 0000000..4ebc4be --- /dev/null +++ b/src/main/resources/table-ordering.txt @@ -0,0 +1,3 @@ +weather_conditions +road_surface +Accidents diff --git a/src/test/java/com/epam/dataservice/HomeWork3Test.java b/src/test/java/com/epam/dataservice/HomeWork3Test.java new file mode 100644 index 0000000..4f45fc4 --- /dev/null +++ b/src/test/java/com/epam/dataservice/HomeWork3Test.java @@ -0,0 +1,157 @@ +package com.epam.dataservice; + +import com.epam.data.AccidentsDataLoader; +import com.epam.data.RoadAccident; +import com.epam.data.RoadAccidentBuilder; +import com.epam.data.TimeOfDay; +import com.epam.processor.DataProcessor; +import org.hamcrest.core.AnyOf; +import org.hamcrest.core.StringStartsWith; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.concurrent.*; + +import static org.hamcrest.core.Is.is; +import org.hamcrest.core.IsInstanceOf; +import org.hamcrest.core.IsNull; +import static org.hamcrest.core.IsCollectionContaining.hasItems; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.hamcrest.core.IsInstanceOf.*; +import static org.junit.Assert.*; + + +/** + * Created by bill on 16-5-1. + */ +public class HomeWork3Test { + public static LinkedList inputFiles = new LinkedList<>(); + private static HomeWork3 homeWork3; + + @BeforeClass + public static void loadData(){ + for (int i = 2009; i <= 2012 ; i++) { + inputFiles.add("src/main/resources/DfTRoadSafety_Accidents_" + Integer.toString(i) +".csv"); + } + homeWork3 = new HomeWork3(inputFiles); + } + + private RoadAccident createRoadAccident(String id, String time) { + RoadAccident roadAccident; + if (id == null) { + roadAccident = new RoadAccidentBuilder(null).build(); + } else { + roadAccident = new RoadAccidentBuilder(id) + .withTime(LocalTime.parse(time, DateTimeFormatter.ISO_LOCAL_TIME)) + .build(); + } + return roadAccident; + } + + @Test + public void testAccidentBatchProcessor() throws Exception { + String[] times = {"00:00:00", "06:00:00", "17:59:59", "23:59:59"}; + BlockingQueue> readQueue = new ArrayBlockingQueue>(2); + List> writeQueues = new ArrayList<>(2); + for (int i = 0; i < 2; i++) { + writeQueues.add( new ArrayBlockingQueue(2) ); + } + ExecutorService procExecutor = Executors.newFixedThreadPool(2); + for (int i = 0; i < 2; i++) { + procExecutor.submit(new AccidentBatchProcessor(readQueue, writeQueues)); + } + for (String time : times ) { + List roadAccidentList = new ArrayList(); + roadAccidentList.add(createRoadAccident(time, time)); + readQueue.put(roadAccidentList); + } +// TimeUnit.SECONDS.sleep(1); + + RoadAccident roadAccident; + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + BlockingQueue writeQueue = writeQueues.get(j); + System.out.println("Q" + j + " Size=" + writeQueue.size()); + roadAccident = writeQueue.take(); + System.out.println(roadAccident.toCSV()); + assertThat(roadAccident, instanceOf(RoadAccident.class)); +/* + if (j==0) { + assertThat(roadAccident.getTimeOfDay(), isIn(Arrays.asList(TimeOfDay.EVENING, TimeOfDay.NIGHT)); + } else { + assertThat(roadAccident.getTimeOfDay(), isIn(Arrays.asList(TimeOfDay.MORNING, TimeOfDay.AFTERNOON)); + } +*/ + assertThat(roadAccident.getTimeOfDay().getCategory(), equalTo(j)); + assertThat(roadAccident.getForceContact(), StringStartsWith.startsWith("13163862")); + } +// TimeUnit.SECONDS.sleep(1); + } + for (int i = 0; i < 2; i++) { + assertThat(writeQueues.get(i).isEmpty(), equalTo(true)); + } + assertThat(readQueue.isEmpty(), equalTo(true)); + procExecutor.shutdown(); + } + + @Test + public void testMain() { + homeWork3.run(); + } + + @Test + public void testEnum() { + TimeOfDay timeOfDay; + timeOfDay = TimeOfDay.getTimeOfDay(LocalTime.parse("09:56:00", DateTimeFormatter.ISO_LOCAL_TIME)); + assertThat(timeOfDay, equalTo(TimeOfDay.MORNING)); + assertThat(timeOfDay.getCategory(), equalTo(1)); + timeOfDay = TimeOfDay.getTimeOfDay(LocalTime.parse("03:56:00", DateTimeFormatter.ISO_LOCAL_TIME)); + assertThat(timeOfDay, equalTo(TimeOfDay.NIGHT)); + assertThat(timeOfDay.getCategory(), equalTo(0)); + } + + @Test + public void testPoliceForceService() { + PoliceForceService policeForceService = new PoliceForceService(); + assertThat(policeForceService.getContactNo("Norfolk"), equalTo("1316386236")); + assertThat(policeForceService.getContactNo("London"), equalTo("13163862")); + assertThat(policeForceService.getContactNo(""), equalTo("13163862")); + assertThat(policeForceService.getContactNo(null), equalTo("13163862")); +// assertThat(policeForceService.getContactNo("London"), IsNull.nullValue()); + } + + @Test + public void sqlDateTimeTest() throws ParseException { + SimpleDateFormat inputFormat = new SimpleDateFormat("dd/MM/yyyy"); + SimpleDateFormat outputFormat = new SimpleDateFormat("yyyy-MM-dd"); + String csvDate = "28/05/2016"; + Date date = inputFormat.parse(csvDate); + System.out.println("Date is: " + date + " year=" + outputFormat.format(date)); +// LocalDate localDate = new LocalDate(date); + SimpleDateFormat shortFormat = new SimpleDateFormat("yyyy"); + System.out.println("short date: " + shortFormat.parse("2016")); + + } +} +/* + Read accident files for different years and then we need to populate following 2 fields for each record. + 1. ForceContact -- Use PoliceForceService.getContactNo(String forceName) to get contact no. + Assume you dont have control on this method and this method can be time consuming or may even block randomly. + Assume this can be a I/O service for example some web service + + 2. TimeosDay -- Add 1 field DayTime which can have 4 different values as below based on accident time. + MORNING - 6 am to 12 pm + AFTERNOON - 12 pm to 6 pm + EVENING - 6 pm to 12 am + NIGHT - 12 am to 6 am + + And then we need to generate 2 files + 1. File which contains all day time accdent data for the years (MORNING, AFTERNOON) -- DaytimeAccidents.csv + 2. File which contains all night time accdent data for the years (EVENING, NIGHT) -- NighttimeAccidents.csv +*/ diff --git a/src/test/java/com/epam/dataservice/HomeWork5Test.java b/src/test/java/com/epam/dataservice/HomeWork5Test.java new file mode 100644 index 0000000..0dce4ce --- /dev/null +++ b/src/test/java/com/epam/dataservice/HomeWork5Test.java @@ -0,0 +1,184 @@ +package com.epam.dataservice; + +import com.epam.springboot.AccidentsRestApplication; +import com.epam.springboot.modal.Accidents; +import com.epam.springboot.modal.RoadConditions; +import com.epam.springboot.repository.AccidentRepository; +import com.epam.springboot.repository.AccidentService; +import com.epam.springboot.repository.AccidentServiceImpl; +import com.epam.springboot.repository.RoadConditionRepository; +import com.github.springtestdbunit.DbUnitTestExecutionListener; +import com.github.springtestdbunit.annotation.DatabaseSetup; +import org.apache.log4j.Logger; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.IntegrationTest; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.boot.test.TestRestTemplate; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.web.client.RestTemplate; + +import java.net.URL; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsEqual.equalTo; + +/** + * Created by bill on 16-5-22. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = AccidentsRestApplication.class) //MockServletContext +@TestExecutionListeners({DependencyInjectionTestExecutionListener.class, +// DirtiesContextTestExecutionListener.class, +// TransactionalTestExecutionListener.class, + DbUnitTestExecutionListener.class}) +@WebAppConfiguration +@IntegrationTest("server.port:0") +@DatabaseSetup("/sampleData.xml") +//@DatabaseTearDown(type = DatabaseOperation.DELETE_ALL, value = { ItemRepositoryIT.DATASET }) +public class HomeWork5Test { + @Autowired + RoadConditionRepository roadConditionRepository; + @Autowired + AccidentRepository repository; + + @Autowired + AccidentService accidentService; + + @Value("${local.server.port}") + private int port; + + private URL base; + private RestTemplate template; + + static Logger log = Logger.getLogger(HomeWork5Test.class.getName()); + public static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); + + @Before + public void init() throws Exception { + this.base = new URL("http://localhost:" + port + "/"); + template = new TestRestTemplate(); +// RestAssured.port = port; + } + + @Test + public void AcciedentsTest() { + Accidents acciedent = new Accidents("200901BS70001"); + log.info(acciedent); + + } + + @Test + public void AccidentRepositoryTest() { + String testId = "200901BS70004"; + assertThat(repository.count(), equalTo(16L)); + Accidents accidents = repository.findOne(testId); + log.info(accidents); + assertThat(accidents.getId(), equalTo(testId)); + } + + @Test + public void getHello() throws Exception { + ResponseEntity response = template.getForEntity(base.toString() + "hello/Bill", String.class); + assertThat(response.getBody(), equalTo("hello, Bill")); + } + + @Test + public void getAllAccidentsByRoadConditionTest() { + RoadConditions roadCondition = roadConditionRepository.findOne(2); + List accidentsList = repository.findByRoadSurfaceConditions(roadCondition); + log.info(accidentsList); + assertThat(accidentsList.size(), equalTo(4)); + assertThat(accidentsList.get(0).getRoadSurfaceConditions().getCode(), equalTo(roadCondition.getCode())); + } + + @Test + // Use default countBy query + public void getAllAccidentsGroupByRoadCondition1Test() { + List roadConditionsList = roadConditionRepository.findAll(); + for (RoadConditions roadCondition : roadConditionsList) { + log.info(roadCondition.getCode() + " , " + roadCondition.getLabel() + " , Count=" + + repository.countByRoadSurfaceConditions(roadCondition)); + } + assertThat(roadConditionsList.size(), equalTo(8)); + } + + @Test + // Use accident service + public void getAllAccidentsGroupByRoadCondition2Test() { + Map roadConditionsList = accidentService.getAccidentCountGroupByRoadCondition(); + // Java8 + roadConditionsList.forEach((k, v) -> log.info("Road Condition: " + k + ", Count=" + v)); + assertThat(roadConditionsList.get("Dry"), equalTo(11)); + } + + @Test + public void getAllAccidentsByDateTest() throws ParseException { + RoadConditions roadCondition = roadConditionRepository.findOne(1); + List accidentsList = repository.findByRoadSurfaceConditionsAndDateBetween( + roadCondition, + simpleDateFormat.parse("2009-01-01"), + simpleDateFormat.parse("2009-01-10")); + log.info(accidentsList); + assertThat(accidentsList.size(), equalTo(8)); + } + + @Test + public void getAllAccidentsByWeatherConditionAndYearTest() { + Map weatherConditionsList = accidentService.getAccidentCountGroupByWeatherConditionAndYear("2009"); + weatherConditionsList.forEach((k, v) -> log.info("Weather Condition: " + k + ", Count=" + v)); + assertThat(weatherConditionsList.get("Fine no high winds"), equalTo(12)); + } + + @Test + public void updateAccidentByDate() throws ParseException { + String testDate = "2009-01-07"; + int count = accidentService.updateAccidentTimeByDate(testDate); + assertThat(count, equalTo(2)); + List accidentsList = repository.findByDate(simpleDateFormat.parse(testDate)); + log.info(accidentsList); + assertThat(accidentsList.get(0).getTime(), equalTo("AFTERNOON")); + assertThat(accidentsList.get(1).getTime(), equalTo("NIGHT")); + } + + @Test + public void RoadConditionRepositoryTest() { + assertThat(roadConditionRepository.count(), equalTo(8L)); + List roadConditionsList = roadConditionRepository.findAll(); + log.info(roadConditionsList); + assertThat(roadConditionsList.size(), equalTo(8)); + } + +} +/* + +Scenarios to be implemented for Homework: + + + 1. Find all the accidents by ID(Note: We can use findOne method which will accept the Accident ID as PK). + + 2. Find all the accidents count groupby all roadsurface conditions . + + 3. Find all the accidents count groupby accident year and weather condition .( For eg: in year 2009 we need to know the number of accidents based on each weather condition). + + 4. On a given date, fetch all the accidents and update the Time based on the below rules + + Time Logic: + MORNING - 6 am to 12 pm + AFTERNOON - 12 pm to 6 pm + EVENING - 6 pm to 12 am + NIGHT - 12 am to 6 am + */ \ No newline at end of file diff --git a/src/test/java/com/epam/dataservice/HomeWork6Test.java b/src/test/java/com/epam/dataservice/HomeWork6Test.java new file mode 100644 index 0000000..412093a --- /dev/null +++ b/src/test/java/com/epam/dataservice/HomeWork6Test.java @@ -0,0 +1,150 @@ +package com.epam.dataservice; + +import com.epam.springboot.AccidentsRestApplication; +import com.epam.springboot.WeatherUnitTest; +import com.epam.springboot.controller.AccidentController; +import com.epam.springboot.modal.Accidents; +import com.epam.springboot.modal.RoadConditions; +import com.epam.springboot.modal.WeatherConditions; +import com.github.springtestdbunit.DbUnitTestExecutionListener; +import com.github.springtestdbunit.annotation.DatabaseSetup; +import org.apache.log4j.Logger; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.http.MediaType; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + + +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.isEmptyString; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Created by bill on 16-5-22. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = AccidentsRestApplication.class) //MockServletContext +@TestExecutionListeners({DependencyInjectionTestExecutionListener.class, DbUnitTestExecutionListener.class}) +@WebAppConfiguration +@DatabaseSetup("/sampleData.xml") +public class HomeWork6Test { + + @Autowired + AccidentController accidentController; + + static Logger log = Logger.getLogger(HomeWork6Test.class.getName()); + private final static String REST_URL = "/accidents"; + private MockMvc mvc; + + @Before + public void setUp() throws Exception { + mvc = MockMvcBuilders.standaloneSetup(accidentController).build(); + } + + @Test + public void contextLoads() { + } + +/* + @Test + public void RoadConditionRepositoryTest() { + assertThat(roadConditionRepository.count(), equalTo(8L)); + List roadConditionsList = roadConditionRepository.findAll(); + log.info(roadConditionsList); + assertThat(roadConditionsList.size(), equalTo(8)); + } +*/ + + @Test + public void helloTest() throws Exception { + String name = "Bill"; + mvc.perform(MockMvcRequestBuilders.get("/hello/" + name).accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().string(equalTo("hello, " + name))); + } + + @Test + public void accidentAllTest() throws Exception { + mvc.perform(MockMvcRequestBuilders.get("/accidents").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().string(containsString("2009-01"))); + } + + @Test + public void accidentOneTest() throws Exception { + String test_id = "200901BS70016"; + mvc.perform(MockMvcRequestBuilders.get(REST_URL + "/" + test_id).accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().string(containsString(test_id))); + } + + @Test + public void createAccidentTest() throws Exception { + //"{"id":"200901BS70001","weatherConditions":{"code":6,"label":"Snowing + high winds"},"roadSurfaceConditions":{"code":3,"label":"Snow"},"date":"2009-01-01","time":"15:11","longitude":-0.201349,"latitude":51.512273,"policeForce":1,"severity":2,"numberOfVehicles":2,"numberOfCasualties":1,"dayOfWeek":5,"districtAuthority":12,"lightConditions":1}" + String test_id = "201606BS70008"; + Accidents accidents = new Accidents(test_id, new WeatherConditions(6), new RoadConditions(3), new java.util.Date()); + mvc.perform(MockMvcRequestBuilders.post(REST_URL) + .contentType(MediaType.APPLICATION_JSON) + .content(WeatherUnitTest.toJsonBytes(accidents)) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().string(containsString(accidents.getId()))); + mvc.perform(MockMvcRequestBuilders.get(REST_URL + "/" + test_id).accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().string(containsString("Snow"))); + } + + @Test + public void updateAccidentTest() throws Exception { + String test_id = "200901BS70004"; + String url = REST_URL + "/" + test_id; + String json = "{\"id\":\"" + test_id + "\",\"dayOfWeek\":5}"; + Accidents accidents = new Accidents(test_id); + mvc.perform(MockMvcRequestBuilders.put(url) + .contentType(MediaType.APPLICATION_JSON) + .content(json) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().string(containsString(accidents.getId()))); + mvc.perform(MockMvcRequestBuilders.get(url).accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().string(containsString("\"dayOfWeek\":5"))); + } + + @Test + public void deleteAccidentTest() throws Exception { + String test_id = "200901BS70004"; + String url = REST_URL + "/" + test_id; + mvc.perform(MockMvcRequestBuilders.get(url).accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().string(containsString(test_id))); + mvc.perform(MockMvcRequestBuilders.delete(url).accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + mvc.perform(MockMvcRequestBuilders.get(url).accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().string(isEmptyString())); + } + + +} +/* +//todo add Restful services to get all accidents +//todo add Restful services to get accident by Id +//todo add Restful services to get accident by day +//todo add Restful services to create new accident (with POST and PUT - you do remember difference, right? ;) ) +//todo add Restful services to update existed accident +//todo add Restful services to delete accident +//todo for put method response 404(Not Found) when encounter the exception that the id is not exist​ + + */ \ No newline at end of file diff --git a/src/test/java/com/epam/processor/DataProcessorTest.java b/src/test/java/com/epam/processor/DataProcessorTest.java index de1b3e8..9199257 100644 --- a/src/test/java/com/epam/processor/DataProcessorTest.java +++ b/src/test/java/com/epam/processor/DataProcessorTest.java @@ -57,13 +57,13 @@ public void should_return_null_if_no_such_index(){ @Test public void should_count_by_road_conditions7(){ Map countByRoadConditions = dataProcessor.getCountByRoadSurfaceCondition7(); - assertThat(countByRoadConditions.get("Dry"), is(110277)); + assertThat(countByRoadConditions.get("Dry"), is(110277L)); } @Test public void should_count_by_road_conditions(){ Map countByRoadConditions = dataProcessor.getCountByRoadSurfaceCondition(); - assertThat(countByRoadConditions.get("Dry"), is(110277)); + assertThat(countByRoadConditions.get("Dry"), is(110277L)); } @Test diff --git a/src/test/java/com/epam/processor/DbPrepare.java b/src/test/java/com/epam/processor/DbPrepare.java new file mode 100644 index 0000000..1097d15 --- /dev/null +++ b/src/test/java/com/epam/processor/DbPrepare.java @@ -0,0 +1,90 @@ +package com.epam.processor; + +import org.dbunit.dataset.DataSetException; +import org.dbunit.dataset.IDataSet; +import org.dbunit.dataset.ITable; +import org.dbunit.dataset.ITableIterator; +import org.dbunit.dataset.csv.CsvDataSet; +import org.dbunit.dataset.xml.FlatXmlWriter; +import org.dbunit.dataset.xml.XmlDataSet; +import org.dbunit.util.TableFormatter; +import org.dbunit.util.fileloader.CsvDataFileLoader; +import org.dbunit.util.fileloader.DataFileLoader; +import org.junit.Test; + +import java.io.*; +import java.nio.file.Path; +import java.nio.file.Paths; + +/** + * Created by bill on 16-5-25. + */ +public class DbPrepare { + public static String CSV_FILE_PATH = Paths.get("src","main","resources").toString(); + public static String DB_DATA_PATH = Paths.get("src","test","resources").toString(); + public static Path table_xml = Paths.get(DB_DATA_PATH,"tables.xml"); + public static Path sampleData_xml = Paths.get(DB_DATA_PATH,"sampleData.xml"); + + public void dumpTables(IDataSet dataSet) { + try { + ITableIterator tableIterator = dataSet.iterator(); + while (tableIterator.next()) { + ITable table = tableIterator.getTable(); + System.out.println(new TableFormatter().format(table)); + } + } catch (DataSetException e) { + e.printStackTrace(); + } + } + + @Test + public void csvToXml() { + DataFileLoader loader = new CsvDataFileLoader(); + String[] tables = new String[0]; + try { + IDataSet dataSet = loader.load(CSV_FILE_PATH); + tables = dataSet.getTableNames(); + } catch (DataSetException e) { + e.printStackTrace(); + } + System.out.println(tables); + } + + @Test + public void flatXmlDataSetTest() { +// IDataSet dataSet = new FlatXmlDataSetLoader(new FileInputStream(table_xml.toString())); + } + @Test + public void csvToFlatXml() throws Exception { + IDataSet dataSet = new CsvDataSet(new File(CSV_FILE_PATH)); +// dumpTables(dataSet); + System.out.println("Writing to " + sampleData_xml.toString()); + FileOutputStream fos = new FileOutputStream(sampleData_xml.toString()); + FlatXmlWriter flatXmlWriter = new FlatXmlWriter(fos); + flatXmlWriter.write(dataSet); + } + + @Test + public void loadXml() { + try { + System.out.println("Loading " + table_xml.toString()); + IDataSet dataSet2 = new XmlDataSet(new FileInputStream(table_xml.toString())); + dumpTables(dataSet2); + } catch (IOException e) { + e.printStackTrace(); + } catch (DataSetException e) { + e.printStackTrace(); + } + } + + @Test + public void csvToDbunitXml() throws Exception { + IDataSet dataSet = new CsvDataSet(new File(CSV_FILE_PATH)); + dumpTables(dataSet); + FileOutputStream fos = new FileOutputStream(sampleData_xml.toString()); + FlatXmlWriter flatXmlWriter = new FlatXmlWriter(fos); +// XmlDataSetWriter xmlWriter = new XmlDataSetWriter(fos, null); +// xmlWriter.write(dataSet); + flatXmlWriter.write(dataSet); + } +} diff --git a/src/test/java/com/epam/springboot/WeatherUnitTest.java b/src/test/java/com/epam/springboot/WeatherUnitTest.java new file mode 100644 index 0000000..bbfd388 --- /dev/null +++ b/src/test/java/com/epam/springboot/WeatherUnitTest.java @@ -0,0 +1,138 @@ +package com.epam.springboot; + +import com.epam.springboot.controller.WeatherController; +import com.epam.springboot.modal.WeatherConditions; +import com.epam.springboot.repository.WeatherConditionRepository; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.springtestdbunit.DbUnitTestExecutionListener; +import com.github.springtestdbunit.annotation.DatabaseSetup; +import org.apache.log4j.Logger; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.boot.test.WebIntegrationTest; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockServletContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import java.util.HashMap; +import java.util.Map; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * Created by bill on 16-5-29. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = AccidentsRestApplication.class) //MockServletContext +//@ContextConfiguration(classes = MockServletContext.class) +@TestExecutionListeners({DependencyInjectionTestExecutionListener.class, DbUnitTestExecutionListener.class}) +@WebAppConfiguration +//@WebIntegrationTest +//@IntegrationTest("server.port:8090") +@DatabaseSetup("/sampleData.xml") +//@WebMvcTest(WeatherController.class) +public class WeatherUnitTest { +// @Autowired +// WeatherConditionRepository weatherConditionRepository; + @Autowired + WeatherController weatherController; + + static Logger log = Logger.getLogger(WeatherUnitTest.class.getName()); + private MockMvc mvc; + private WeatherConditions sampleWatherConditions = new WeatherConditions(20,"Test20 Sample"); + + @Before + public void setUp() throws Exception { + mvc = MockMvcBuilders.standaloneSetup(weatherController).build(); + } + + @Test + public void simpleWeatherTest() throws Exception { + mvc.perform(MockMvcRequestBuilders.get("/weather/test").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().string(containsString("Test20"))); + } + + @Test + public void weatherAllTest() throws Exception { + mvc.perform(MockMvcRequestBuilders.get("/weather").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + } + + @Test + public void weatherOneTest() throws Exception { + mvc.perform(MockMvcRequestBuilders.get("/weather/3").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().string(containsString("Snowing"))); + } + + @Test + // curl -X POST -H "Content-Type:application/json" -d '{ "code": "20", "label":"Test20" }' http://localhost:8080/weather + public void weatherCreateTest() throws Exception { + mvc.perform(MockMvcRequestBuilders.post("/weather") + .contentType(MediaType.APPLICATION_JSON) + .content(toJsonBytes(sampleWatherConditions)) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().string(containsString(sampleWatherConditions.getLabel()))); + } + + @Test + // curl -X PUT -H "Content-Type:application/json" -d '{ "code": "8" }' http://localhost:8080/weather/8 + public void weatherPutTest() throws Exception { + WeatherConditions weatherConditions = new WeatherConditions(8,"Updated info"); + log.info("Update /weather/"+weatherConditions.getCode()); + mvc.perform(MockMvcRequestBuilders.put("/weather/"+weatherConditions.getCode()) + .contentType(MediaType.APPLICATION_JSON) + .content(toJsonBytes(weatherConditions)) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().string(containsString(weatherConditions.getLabel()))); + } + + @Test + // curl -X DELETE http://localhost:8080/weather/3 + public void weatherDeleteTest() throws Exception { + mvc.perform(MockMvcRequestBuilders.delete("/weather/3").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().string(containsString("Snowing"))); + } + + @Test + // curl -X PUT -H "Content-Type:application/json" -d '{ "code": "20" }' http://localhost:8080/weather/20 + public void weatherPutExceptionTest() throws Exception { + mvc.perform(MockMvcRequestBuilders.put("/weather/"+sampleWatherConditions.getCode()) + .contentType(MediaType.APPLICATION_JSON) + .content(toJsonBytes(sampleWatherConditions)) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().is4xxClientError()); + } + + + public static byte[] toJsonBytes(Object obj) { + ObjectMapper mapper = new ObjectMapper(); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + try { + return mapper.writeValueAsBytes(obj); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + return null; + } + +} diff --git a/src/test/resources/sampleData.xml b/src/test/resources/sampleData.xml new file mode 100644 index 0000000..4e7141c --- /dev/null +++ b/src/test/resources/sampleData.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/tables.xml b/src/test/resources/tables.xml new file mode 100644 index 0000000..1020c3a --- /dev/null +++ b/src/test/resources/tables.xml @@ -0,0 +1,497 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +