Compare commits
	
		
			No commits in common. "master" and "crypto-primitives" have entirely different histories. 
		
	
	
		
			master
			...
			crypto-pri
		
	
		|  | @ -13,26 +13,4 @@ out | ||||||
| *.prefs | *.prefs | ||||||
| *.project | *.project | ||||||
| *.classpath | *.classpath | ||||||
| *.db | bulletin-board-server/local-instances/meerkat.db | ||||||
| *.sql |  | ||||||
| .arcconfig |  | ||||||
| /meerkat_election_params_tempfile.dat |  | ||||||
| /meerkat_booth_system_messages.dat |  | ||||||
| local.properties |  | ||||||
| # Angular junk |  | ||||||
| */tmp |  | ||||||
| */dist |  | ||||||
| */out-tsc |  | ||||||
| */node_modules |  | ||||||
| */e2e/*.js |  | ||||||
| */e2e/*.map |  | ||||||
| */.sass-cache |  | ||||||
| */connect.lock |  | ||||||
| */coverage |  | ||||||
| */libpeerconnection.log |  | ||||||
| npm-debug.log |  | ||||||
| testem.log |  | ||||||
| */typings |  | ||||||
| bundle.js |  | ||||||
| bundle.d.ts |  | ||||||
| 
 |  | ||||||
|  |  | ||||||
|  | @ -1,10 +0,0 @@ | ||||||
| This file describes Wombat Code & Documentation Conventions: |  | ||||||
| 
 |  | ||||||
| Code Conventions: |  | ||||||
|     *   Code-       The good old classic java code conventions |  | ||||||
|                     camelCase convention, constants should be capital letters with underscore etc... |  | ||||||
| 
 |  | ||||||
| Documentation Conventions: |  | ||||||
|     *   Comments-   The good old classic java code documentation, Block Comments (to describe |  | ||||||
|                     method/class/interface etc...) // comments too describe complex code |  | ||||||
|                     (only if the code block complicated) |  | ||||||
|  | @ -1,82 +0,0 @@ | ||||||
| buildscript { |  | ||||||
|     repositories { |  | ||||||
|         jcenter() |  | ||||||
|     } |  | ||||||
|     dependencies { |  | ||||||
|         classpath 'com.android.tools.build:gradle:2.3.3' |  | ||||||
| 
 |  | ||||||
|         // NOTE: Do not place your application dependencies here; they belong |  | ||||||
|         // in the individual module build.gradle files |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| apply plugin: 'com.android.application' |  | ||||||
| 
 |  | ||||||
| android { |  | ||||||
|     compileSdkVersion 26 |  | ||||||
|     buildToolsVersion "26.0.0" |  | ||||||
|     defaultConfig { |  | ||||||
|         applicationId "com.meerkat.laura.fakescannerapp" |  | ||||||
|         minSdkVersion 14 |  | ||||||
|         targetSdkVersion 26 |  | ||||||
|         versionCode 1 |  | ||||||
|         versionName "1.0" |  | ||||||
|         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" |  | ||||||
| 
 |  | ||||||
|         // Enabling multidex support. |  | ||||||
|         multiDexEnabled true |  | ||||||
|         vectorDrawables.useSupportLibrary = true |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     buildTypes { |  | ||||||
|         debug { |  | ||||||
|             debuggable true |  | ||||||
|         } |  | ||||||
|         release { |  | ||||||
|             minifyEnabled false |  | ||||||
|             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| configurations.all { |  | ||||||
|     resolutionStrategy.dependencySubstitution { |  | ||||||
|         // Avoid Android compilation error caused by two different javax.inject dependencies. |  | ||||||
|         substitute module('org.glassfish.hk2.external:javax.inject:2.4.0-b34') with module('javax.inject:javax.inject:1') |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // Exclude non-android logback modules |  | ||||||
|     exclude group:"ch.qos.logback", module:"logback-core" |  | ||||||
|     exclude group:"ch.qos.logback", module:"logback-classic" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| dependencies { |  | ||||||
| //    compile fileTree(dir: 'libs', include: ['*.jar']) |  | ||||||
| 
 |  | ||||||
|     compile project(':meerkat-common') |  | ||||||
|     compile project(':scanner-api-common') |  | ||||||
| 
 |  | ||||||
|     compile 'com.android.support:appcompat-v7:26.0.+' |  | ||||||
|     compile 'com.google.protobuf:protobuf-java:3.+' |  | ||||||
|     compile 'com.android.support:support-v4:26.0.+' |  | ||||||
|     compile 'com.android.support:support-vector-drawable:26.0.+' |  | ||||||
|     testCompile 'junit:junit:4.12' |  | ||||||
| 
 |  | ||||||
|     // Android logging |  | ||||||
|     compile 'com.github.tony19:logback-android-core:1.1.1-6' |  | ||||||
|     compile('com.github.tony19:logback-android-classic:1.1.1-6') { |  | ||||||
|         // workaround issue #73 |  | ||||||
|         exclude group: 'com.google.android', module: 'android' |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| repositories { |  | ||||||
|     mavenLocal() |  | ||||||
| 
 |  | ||||||
|     jcenter() |  | ||||||
| } |  | ||||||
|  | @ -1 +0,0 @@ | ||||||
| ../gradlew |  | ||||||
|  | @ -1,7 +0,0 @@ | ||||||
| <?xml version="1.0" encoding="UTF-8"?> |  | ||||||
| <lint> |  | ||||||
|     <issue id="InvalidPackage"> |  | ||||||
|         <!-- Ignore javax.mail from logback https://github.com/tony19/logback-android/issues/140--> |  | ||||||
|         <ignore regexp="javax.mail"/> |  | ||||||
|     </issue> |  | ||||||
| </lint> |  | ||||||
|  | @ -1,13 +0,0 @@ | ||||||
| package com.meerkat.laura.fakescannerapp; |  | ||||||
| 
 |  | ||||||
| import android.app.Application; |  | ||||||
| import android.test.ApplicationTestCase; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a> |  | ||||||
|  */ |  | ||||||
| public class ApplicationTest extends ApplicationTestCase<Application> { |  | ||||||
|     public ApplicationTest() { |  | ||||||
|         super(Application.class); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,28 +0,0 @@ | ||||||
| <?xml version="1.0" encoding="utf-8"?> |  | ||||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
|           package="com.meerkat.laura.fakescannerapp"> |  | ||||||
| 
 |  | ||||||
|     <uses-permission android:name="android.permission.INTERNET"/> |  | ||||||
|     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> |  | ||||||
|     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> |  | ||||||
| 
 |  | ||||||
|     <application |  | ||||||
|             android:allowBackup="true" |  | ||||||
|             android:icon="@mipmap/ic_launcher" |  | ||||||
|             android:label="@string/app_name" |  | ||||||
|             android:supportsRtl="true" |  | ||||||
|             android:theme="@style/AppTheme"> |  | ||||||
|         <activity android:name=".MainActivity"> |  | ||||||
|             <intent-filter> |  | ||||||
|                 <action android:name="android.intent.action.MAIN"/> |  | ||||||
| 
 |  | ||||||
|                 <category android:name="android.intent.category.LAUNCHER"/> |  | ||||||
|             </intent-filter> |  | ||||||
|         </activity> |  | ||||||
|         <activity |  | ||||||
|                 android:name=".SettingsActivity" |  | ||||||
|                 android:label="@string/title_activity_settings"> |  | ||||||
|         </activity> |  | ||||||
|     </application> |  | ||||||
| 
 |  | ||||||
| </manifest> |  | ||||||
|  | @ -1,15 +0,0 @@ | ||||||
| <?xml version="1.0" encoding="utf-8"?> |  | ||||||
| <configuration> |  | ||||||
|     <appender name="logcat" class="ch.qos.logback.classic.android.LogcatAppender"> |  | ||||||
|         <tagEncoder> |  | ||||||
|             <pattern>%logger{12}</pattern> |  | ||||||
|         </tagEncoder> |  | ||||||
|         <encoder> |  | ||||||
|             <pattern>[%-20thread] %msg</pattern> |  | ||||||
|         </encoder> |  | ||||||
|     </appender> |  | ||||||
| 
 |  | ||||||
|     <root level="DEBUG"> |  | ||||||
|         <appender-ref ref="logcat" /> |  | ||||||
|     </root> |  | ||||||
| </configuration> |  | ||||||
|  | @ -1,508 +0,0 @@ | ||||||
| package com.google.zxing.integration.android; |  | ||||||
| 
 |  | ||||||
| /* |  | ||||||
|  * Copyright 2009 ZXing authors |  | ||||||
|  * |  | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|  * you may not use this file except in compliance with the License. |  | ||||||
|  * You may obtain a copy of the License at |  | ||||||
|  * |  | ||||||
|  *      http://www.apache.org/licenses/LICENSE-2.0
 |  | ||||||
|  * |  | ||||||
|  * Unless required by applicable law or agreed to in writing, software |  | ||||||
|  * distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|  * See the License for the specific language governing permissions and |  | ||||||
|  * limitations under the License. |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| import java.util.Arrays; |  | ||||||
| import java.util.Collection; |  | ||||||
| import java.util.Collections; |  | ||||||
| import java.util.HashMap; |  | ||||||
| import java.util.List; |  | ||||||
| import java.util.Map; |  | ||||||
| 
 |  | ||||||
| import android.app.Activity; |  | ||||||
| import android.app.AlertDialog; |  | ||||||
| import android.app.Fragment; |  | ||||||
| import android.content.ActivityNotFoundException; |  | ||||||
| import android.content.DialogInterface; |  | ||||||
| import android.content.Intent; |  | ||||||
| import android.content.pm.PackageManager; |  | ||||||
| import android.content.pm.ResolveInfo; |  | ||||||
| import android.net.Uri; |  | ||||||
| import android.os.Bundle; |  | ||||||
| import android.util.Log; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * <p>A utility class which helps ease integration with Barcode Scanner via {@link Intent}s. This is a simple |  | ||||||
|  * way to invoke barcode scanning and receive the result, without any need to integrate, modify, or learn the |  | ||||||
|  * project's source code.</p> |  | ||||||
|  * |  | ||||||
|  * <h2>Initiating a barcode scan</h2> |  | ||||||
|  * |  | ||||||
|  * <p>To integrate, create an instance of {@code IntentIntegrator} and call {@link #initiateScan()} and wait |  | ||||||
|  * for the result in your app.</p> |  | ||||||
|  * |  | ||||||
|  * <p>It does require that the Barcode Scanner (or work-alike) application is installed. The |  | ||||||
|  * {@link #initiateScan()} method will prompt the user to download the application, if needed.</p> |  | ||||||
|  * |  | ||||||
|  * <p>There are a few steps to using this integration. First, your {@link Activity} must implement |  | ||||||
|  * the method {@link Activity#onActivityResult(int, int, Intent)} and include a line of code like this:</p> |  | ||||||
|  * |  | ||||||
|  * <pre>{@code |  | ||||||
|  * public void onActivityResult(int requestCode, int resultCode, Intent intent) { |  | ||||||
|  *   IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent); |  | ||||||
|  *   if (scanResult != null) { |  | ||||||
|  *     // handle scan result
 |  | ||||||
|  *   } |  | ||||||
|  *   // else continue with any other code you need in the method
 |  | ||||||
|  *   ... |  | ||||||
|  * } |  | ||||||
|  * }</pre> |  | ||||||
|  * |  | ||||||
|  * <p>This is where you will handle a scan result.</p> |  | ||||||
|  * |  | ||||||
|  * <p>Second, just call this in response to a user action somewhere to begin the scan process:</p> |  | ||||||
|  * |  | ||||||
|  * <pre>{@code |  | ||||||
|  * IntentIntegrator integrator = new IntentIntegrator(yourActivity); |  | ||||||
|  * integrator.initiateScan(); |  | ||||||
|  * }</pre> |  | ||||||
|  * |  | ||||||
|  * <p>Note that {@link #initiateScan()} returns an {@link AlertDialog} which is non-null if the |  | ||||||
|  * user was prompted to download the application. This lets the calling app potentially manage the dialog. |  | ||||||
|  * In particular, ideally, the app dismisses the dialog if it's still active in its {@link Activity#onPause()} |  | ||||||
|  * method.</p> |  | ||||||
|  * |  | ||||||
|  * <p>You can use {@link #setTitle(String)} to customize the title of this download prompt dialog (or, use |  | ||||||
|  * {@link #setTitleByID(int)} to set the title by string resource ID.) Likewise, the prompt message, and |  | ||||||
|  * yes/no button labels can be changed.</p> |  | ||||||
|  * |  | ||||||
|  * <p>Finally, you can use {@link #addExtra(String, Object)} to add more parameters to the Intent used |  | ||||||
|  * to invoke the scanner. This can be used to set additional options not directly exposed by this |  | ||||||
|  * simplified API.</p> |  | ||||||
|  * |  | ||||||
|  * <p>By default, this will only allow applications that are known to respond to this intent correctly |  | ||||||
|  * do so. The apps that are allowed to response can be set with {@link #setTargetApplications(List)}. |  | ||||||
|  * For example, set to {@link #TARGET_BARCODE_SCANNER_ONLY} to only target the Barcode Scanner app itself.</p> |  | ||||||
|  * |  | ||||||
|  * <h2>Sharing text via barcode</h2> |  | ||||||
|  * |  | ||||||
|  * <p>To share text, encoded as a QR Code on-screen, similarly, see {@link #shareText(CharSequence)}.</p> |  | ||||||
|  * |  | ||||||
|  * <p>Some code, particularly download integration, was contributed from the Anobiit application.</p> |  | ||||||
|  * |  | ||||||
|  * <h2>Enabling experimental barcode formats</h2> |  | ||||||
|  * |  | ||||||
|  * <p>Some formats are not enabled by default even when scanning with {@link #ALL_CODE_TYPES}, such as |  | ||||||
|  * PDF417. Use {@link #initiateScan(Collection)} with |  | ||||||
|  * a collection containing the names of formats to scan for explicitly, like "PDF_417", to use such |  | ||||||
|  * formats.</p> |  | ||||||
|  * |  | ||||||
|  * @author Sean Owen |  | ||||||
|  * @author Fred Lin |  | ||||||
|  * @author Isaac Potoczny-Jones |  | ||||||
|  * @author Brad Drehmer |  | ||||||
|  * @author gcstang |  | ||||||
|  */ |  | ||||||
| public class IntentIntegrator { |  | ||||||
| 
 |  | ||||||
|     public static final int REQUEST_CODE = 0x0000c0de; // Only use bottom 16 bits
 |  | ||||||
|     private static final String TAG = IntentIntegrator.class.getSimpleName(); |  | ||||||
| 
 |  | ||||||
|     public static final String DEFAULT_TITLE = "Install Barcode Scanner?"; |  | ||||||
|     public static final String DEFAULT_MESSAGE = |  | ||||||
|             "This application requires Barcode Scanner. Would you like to install it?"; |  | ||||||
|     public static final String DEFAULT_YES = "Yes"; |  | ||||||
|     public static final String DEFAULT_NO = "No"; |  | ||||||
| 
 |  | ||||||
|     private static final String BS_PACKAGE = "com.google.zxing.client.android"; |  | ||||||
|     private static final String BSPLUS_PACKAGE = "com.srowen.bs.android"; |  | ||||||
| 
 |  | ||||||
|     // supported barcode formats
 |  | ||||||
|     public static final Collection<String> PRODUCT_CODE_TYPES = list("UPC_A", "UPC_E", "EAN_8", "EAN_13", "RSS_14"); |  | ||||||
|     public static final Collection<String> ONE_D_CODE_TYPES = |  | ||||||
|             list("UPC_A", "UPC_E", "EAN_8", "EAN_13", "CODE_39", "CODE_93", "CODE_128", |  | ||||||
|                     "ITF", "RSS_14", "RSS_EXPANDED"); |  | ||||||
|     public static final Collection<String> QR_CODE_TYPES = Collections.singleton("QR_CODE"); |  | ||||||
|     public static final Collection<String> DATA_MATRIX_TYPES = Collections.singleton("DATA_MATRIX"); |  | ||||||
| 
 |  | ||||||
|     public static final Collection<String> ALL_CODE_TYPES = null; |  | ||||||
| 
 |  | ||||||
|     public static final List<String> TARGET_BARCODE_SCANNER_ONLY = Collections.singletonList(BS_PACKAGE); |  | ||||||
|     public static final List<String> TARGET_ALL_KNOWN = list( |  | ||||||
|             BSPLUS_PACKAGE,             // Barcode Scanner+
 |  | ||||||
|             BSPLUS_PACKAGE + ".simple", // Barcode Scanner+ Simple
 |  | ||||||
|             BS_PACKAGE                  // Barcode Scanner
 |  | ||||||
|             // What else supports this intent?
 |  | ||||||
|     ); |  | ||||||
| 
 |  | ||||||
|     private final Activity activity; |  | ||||||
|     private final Fragment fragment; |  | ||||||
| 
 |  | ||||||
|     private String title; |  | ||||||
|     private String message; |  | ||||||
|     private String buttonYes; |  | ||||||
|     private String buttonNo; |  | ||||||
|     private List<String> targetApplications; |  | ||||||
|     private final Map<String,Object> moreExtras = new HashMap<String,Object>(3); |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @param activity {@link Activity} invoking the integration |  | ||||||
|      */ |  | ||||||
|     public IntentIntegrator(Activity activity) { |  | ||||||
|         this.activity = activity; |  | ||||||
|         this.fragment = null; |  | ||||||
|         initializeConfiguration(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @param fragment {@link Fragment} invoking the integration. |  | ||||||
|      *  {@link #startActivityForResult(Intent, int)} will be called on the {@link Fragment} instead |  | ||||||
|      *  of an {@link Activity} |  | ||||||
|      */ |  | ||||||
|     public IntentIntegrator(Fragment fragment) { |  | ||||||
|         this.activity = fragment.getActivity(); |  | ||||||
|         this.fragment = fragment; |  | ||||||
|         initializeConfiguration(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private void initializeConfiguration() { |  | ||||||
|         title = DEFAULT_TITLE; |  | ||||||
|         message = DEFAULT_MESSAGE; |  | ||||||
|         buttonYes = DEFAULT_YES; |  | ||||||
|         buttonNo = DEFAULT_NO; |  | ||||||
|         targetApplications = TARGET_ALL_KNOWN; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public String getTitle() { |  | ||||||
|         return title; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public void setTitle(String title) { |  | ||||||
|         this.title = title; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public void setTitleByID(int titleID) { |  | ||||||
|         title = activity.getString(titleID); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public String getMessage() { |  | ||||||
|         return message; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public void setMessage(String message) { |  | ||||||
|         this.message = message; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public void setMessageByID(int messageID) { |  | ||||||
|         message = activity.getString(messageID); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public String getButtonYes() { |  | ||||||
|         return buttonYes; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public void setButtonYes(String buttonYes) { |  | ||||||
|         this.buttonYes = buttonYes; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public void setButtonYesByID(int buttonYesID) { |  | ||||||
|         buttonYes = activity.getString(buttonYesID); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public String getButtonNo() { |  | ||||||
|         return buttonNo; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public void setButtonNo(String buttonNo) { |  | ||||||
|         this.buttonNo = buttonNo; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public void setButtonNoByID(int buttonNoID) { |  | ||||||
|         buttonNo = activity.getString(buttonNoID); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public Collection<String> getTargetApplications() { |  | ||||||
|         return targetApplications; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public final void setTargetApplications(List<String> targetApplications) { |  | ||||||
|         if (targetApplications.isEmpty()) { |  | ||||||
|             throw new IllegalArgumentException("No target applications"); |  | ||||||
|         } |  | ||||||
|         this.targetApplications = targetApplications; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public void setSingleTargetApplication(String targetApplication) { |  | ||||||
|         this.targetApplications = Collections.singletonList(targetApplication); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public Map<String,?> getMoreExtras() { |  | ||||||
|         return moreExtras; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public final void addExtra(String key, Object value) { |  | ||||||
|         moreExtras.put(key, value); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Initiates a scan for all known barcode types with the default camera. |  | ||||||
|      * |  | ||||||
|      * @return the {@link AlertDialog} that was shown to the user prompting them to download the app |  | ||||||
|      *   if a prompt was needed, or null otherwise. |  | ||||||
|      */ |  | ||||||
|     public final AlertDialog initiateScan() { |  | ||||||
|         return initiateScan(ALL_CODE_TYPES, -1); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Initiates a scan for all known barcode types with the specified camera. |  | ||||||
|      * |  | ||||||
|      * @param cameraId camera ID of the camera to use. A negative value means "no preference". |  | ||||||
|      * @return the {@link AlertDialog} that was shown to the user prompting them to download the app |  | ||||||
|      *   if a prompt was needed, or null otherwise. |  | ||||||
|      */ |  | ||||||
|     public final AlertDialog initiateScan(int cameraId) { |  | ||||||
|         return initiateScan(ALL_CODE_TYPES, cameraId); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Initiates a scan, using the default camera, only for a certain set of barcode types, given as strings corresponding |  | ||||||
|      * to their names in ZXing's {@code BarcodeFormat} class like "UPC_A". You can supply constants |  | ||||||
|      * like {@link #PRODUCT_CODE_TYPES} for example. |  | ||||||
|      * |  | ||||||
|      * @param desiredBarcodeFormats names of {@code BarcodeFormat}s to scan for |  | ||||||
|      * @return the {@link AlertDialog} that was shown to the user prompting them to download the app |  | ||||||
|      *   if a prompt was needed, or null otherwise. |  | ||||||
|      */ |  | ||||||
|     public final AlertDialog initiateScan(Collection<String> desiredBarcodeFormats) { |  | ||||||
|         return initiateScan(desiredBarcodeFormats, -1); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Initiates a scan, using the specified camera, only for a certain set of barcode types, given as strings corresponding |  | ||||||
|      * to their names in ZXing's {@code BarcodeFormat} class like "UPC_A". You can supply constants |  | ||||||
|      * like {@link #PRODUCT_CODE_TYPES} for example. |  | ||||||
|      * |  | ||||||
|      * @param desiredBarcodeFormats names of {@code BarcodeFormat}s to scan for |  | ||||||
|      * @param cameraId camera ID of the camera to use. A negative value means "no preference". |  | ||||||
|      * @return the {@link AlertDialog} that was shown to the user prompting them to download the app |  | ||||||
|      *   if a prompt was needed, or null otherwise |  | ||||||
|      */ |  | ||||||
|     public final AlertDialog initiateScan(Collection<String> desiredBarcodeFormats, int cameraId) { |  | ||||||
|         Intent intentScan = new Intent(BS_PACKAGE + ".SCAN"); |  | ||||||
|         intentScan.addCategory(Intent.CATEGORY_DEFAULT); |  | ||||||
| 
 |  | ||||||
|         // check which types of codes to scan for
 |  | ||||||
|         if (desiredBarcodeFormats != null) { |  | ||||||
|             // set the desired barcode types
 |  | ||||||
|             StringBuilder joinedByComma = new StringBuilder(); |  | ||||||
|             for (String format : desiredBarcodeFormats) { |  | ||||||
|                 if (joinedByComma.length() > 0) { |  | ||||||
|                     joinedByComma.append(','); |  | ||||||
|                 } |  | ||||||
|                 joinedByComma.append(format); |  | ||||||
|             } |  | ||||||
|             intentScan.putExtra("SCAN_FORMATS", joinedByComma.toString()); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // check requested camera ID
 |  | ||||||
|         if (cameraId >= 0) { |  | ||||||
|             intentScan.putExtra("SCAN_CAMERA_ID", cameraId); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         String targetAppPackage = findTargetAppPackage(intentScan); |  | ||||||
|         if (targetAppPackage == null) { |  | ||||||
|             return showDownloadDialog(); |  | ||||||
|         } |  | ||||||
|         intentScan.setPackage(targetAppPackage); |  | ||||||
|         intentScan.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); |  | ||||||
|         intentScan.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); |  | ||||||
|         attachMoreExtras(intentScan); |  | ||||||
|         startActivityForResult(intentScan, REQUEST_CODE); |  | ||||||
|         return null; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Start an activity. This method is defined to allow different methods of activity starting for |  | ||||||
|      * newer versions of Android and for compatibility library. |  | ||||||
|      * |  | ||||||
|      * @param intent Intent to start. |  | ||||||
|      * @param code Request code for the activity |  | ||||||
|      * @see Activity#startActivityForResult(Intent, int) |  | ||||||
|      * @see Fragment#startActivityForResult(Intent, int) |  | ||||||
|      */ |  | ||||||
|     protected void startActivityForResult(Intent intent, int code) { |  | ||||||
|         if (fragment == null) { |  | ||||||
|             activity.startActivityForResult(intent, code); |  | ||||||
|         } else { |  | ||||||
|             fragment.startActivityForResult(intent, code); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private String findTargetAppPackage(Intent intent) { |  | ||||||
|         PackageManager pm = activity.getPackageManager(); |  | ||||||
|         List<ResolveInfo> availableApps = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); |  | ||||||
|         if (availableApps != null) { |  | ||||||
|             for (String targetApp : targetApplications) { |  | ||||||
|                 if (contains(availableApps, targetApp)) { |  | ||||||
|                     return targetApp; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         return null; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private static boolean contains(Iterable<ResolveInfo> availableApps, String targetApp) { |  | ||||||
|         for (ResolveInfo availableApp : availableApps) { |  | ||||||
|             String packageName = availableApp.activityInfo.packageName; |  | ||||||
|             if (targetApp.equals(packageName)) { |  | ||||||
|                 return true; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private AlertDialog showDownloadDialog() { |  | ||||||
|         AlertDialog.Builder downloadDialog = new AlertDialog.Builder(activity); |  | ||||||
|         downloadDialog.setTitle(title); |  | ||||||
|         downloadDialog.setMessage(message); |  | ||||||
|         downloadDialog.setPositiveButton(buttonYes, new DialogInterface.OnClickListener() { |  | ||||||
|             @Override |  | ||||||
|             public void onClick(DialogInterface dialogInterface, int i) { |  | ||||||
|                 String packageName; |  | ||||||
|                 if (targetApplications.contains(BS_PACKAGE)) { |  | ||||||
|                     // Prefer to suggest download of BS if it's anywhere in the list
 |  | ||||||
|                     packageName = BS_PACKAGE; |  | ||||||
|                 } else { |  | ||||||
|                     // Otherwise, first option:
 |  | ||||||
|                     packageName = targetApplications.get(0); |  | ||||||
|                 } |  | ||||||
|                 Uri uri = Uri.parse("market://details?id=" + packageName); |  | ||||||
|                 Intent intent = new Intent(Intent.ACTION_VIEW, uri); |  | ||||||
|                 try { |  | ||||||
|                     if (fragment == null) { |  | ||||||
|                         activity.startActivity(intent); |  | ||||||
|                     } else { |  | ||||||
|                         fragment.startActivity(intent); |  | ||||||
|                     } |  | ||||||
|                 } catch (ActivityNotFoundException anfe) { |  | ||||||
|                     // Hmm, market is not installed
 |  | ||||||
|                     Log.w(TAG, "Google Play is not installed; cannot install " + packageName); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|         downloadDialog.setNegativeButton(buttonNo, null); |  | ||||||
|         downloadDialog.setCancelable(true); |  | ||||||
|         return downloadDialog.show(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * <p>Call this from your {@link Activity}'s |  | ||||||
|      * {@link Activity#onActivityResult(int, int, Intent)} method.</p> |  | ||||||
|      * |  | ||||||
|      * @param requestCode request code from {@code onActivityResult()} |  | ||||||
|      * @param resultCode result code from {@code onActivityResult()} |  | ||||||
|      * @param intent {@link Intent} from {@code onActivityResult()} |  | ||||||
|      * @return null if the event handled here was not related to this class, or |  | ||||||
|      *  else an {@link IntentResult} containing the result of the scan. If the user cancelled scanning, |  | ||||||
|      *  the fields will be null. |  | ||||||
|      */ |  | ||||||
|     public static IntentResult parseActivityResult(int requestCode, int resultCode, Intent intent) { |  | ||||||
|         if (requestCode == REQUEST_CODE) { |  | ||||||
|             if (resultCode == Activity.RESULT_OK) { |  | ||||||
|                 String contents = intent.getStringExtra("SCAN_RESULT"); |  | ||||||
|                 String formatName = intent.getStringExtra("SCAN_RESULT_FORMAT"); |  | ||||||
|                 byte[] rawBytes = intent.getByteArrayExtra("SCAN_RESULT_BYTES"); |  | ||||||
|                 int intentOrientation = intent.getIntExtra("SCAN_RESULT_ORIENTATION", Integer.MIN_VALUE); |  | ||||||
|                 Integer orientation = intentOrientation == Integer.MIN_VALUE ? null : intentOrientation; |  | ||||||
|                 String errorCorrectionLevel = intent.getStringExtra("SCAN_RESULT_ERROR_CORRECTION_LEVEL"); |  | ||||||
|                 return new IntentResult(contents, |  | ||||||
|                         formatName, |  | ||||||
|                         rawBytes, |  | ||||||
|                         orientation, |  | ||||||
|                         errorCorrectionLevel); |  | ||||||
|             } |  | ||||||
|             return new IntentResult(); |  | ||||||
|         } |  | ||||||
|         return null; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Defaults to type "TEXT_TYPE". |  | ||||||
|      * |  | ||||||
|      * @param text the text string to encode as a barcode |  | ||||||
|      * @return the {@link AlertDialog} that was shown to the user prompting them to download the app |  | ||||||
|      *   if a prompt was needed, or null otherwise |  | ||||||
|      * @see #shareText(CharSequence, CharSequence) |  | ||||||
|      */ |  | ||||||
|     public final AlertDialog shareText(CharSequence text) { |  | ||||||
|         return shareText(text, "TEXT_TYPE"); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Shares the given text by encoding it as a barcode, such that another user can |  | ||||||
|      * scan the text off the screen of the device. |  | ||||||
|      * |  | ||||||
|      * @param text the text string to encode as a barcode |  | ||||||
|      * @param type type of data to encode. See {@code com.google.zxing.client.android.Contents.Type} constants. |  | ||||||
|      * @return the {@link AlertDialog} that was shown to the user prompting them to download the app |  | ||||||
|      *   if a prompt was needed, or null otherwise |  | ||||||
|      */ |  | ||||||
|     public final AlertDialog shareText(CharSequence text, CharSequence type) { |  | ||||||
|         Intent intent = new Intent(); |  | ||||||
|         intent.addCategory(Intent.CATEGORY_DEFAULT); |  | ||||||
|         intent.setAction(BS_PACKAGE + ".ENCODE"); |  | ||||||
|         intent.putExtra("ENCODE_TYPE", type); |  | ||||||
|         intent.putExtra("ENCODE_DATA", text); |  | ||||||
|         String targetAppPackage = findTargetAppPackage(intent); |  | ||||||
|         if (targetAppPackage == null) { |  | ||||||
|             return showDownloadDialog(); |  | ||||||
|         } |  | ||||||
|         intent.setPackage(targetAppPackage); |  | ||||||
|         intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); |  | ||||||
|         intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); |  | ||||||
|         attachMoreExtras(intent); |  | ||||||
|         if (fragment == null) { |  | ||||||
|             activity.startActivity(intent); |  | ||||||
|         } else { |  | ||||||
|             fragment.startActivity(intent); |  | ||||||
|         } |  | ||||||
|         return null; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private static List<String> list(String... values) { |  | ||||||
|         return Collections.unmodifiableList(Arrays.asList(values)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private void attachMoreExtras(Intent intent) { |  | ||||||
|         for (Map.Entry<String,Object> entry : moreExtras.entrySet()) { |  | ||||||
|             String key = entry.getKey(); |  | ||||||
|             Object value = entry.getValue(); |  | ||||||
|             // Kind of hacky
 |  | ||||||
|             if (value instanceof Integer) { |  | ||||||
|                 intent.putExtra(key, (Integer) value); |  | ||||||
|             } else if (value instanceof Long) { |  | ||||||
|                 intent.putExtra(key, (Long) value); |  | ||||||
|             } else if (value instanceof Boolean) { |  | ||||||
|                 intent.putExtra(key, (Boolean) value); |  | ||||||
|             } else if (value instanceof Double) { |  | ||||||
|                 intent.putExtra(key, (Double) value); |  | ||||||
|             } else if (value instanceof Float) { |  | ||||||
|                 intent.putExtra(key, (Float) value); |  | ||||||
|             } else if (value instanceof Bundle) { |  | ||||||
|                 intent.putExtra(key, (Bundle) value); |  | ||||||
|             } else { |  | ||||||
|                 intent.putExtra(key, value.toString()); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
|  | @ -1,94 +0,0 @@ | ||||||
| /* |  | ||||||
|  * Copyright 2009 ZXing authors |  | ||||||
|  * |  | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|  * you may not use this file except in compliance with the License. |  | ||||||
|  * You may obtain a copy of the License at |  | ||||||
|  * |  | ||||||
|  *      http://www.apache.org/licenses/LICENSE-2.0
 |  | ||||||
|  * |  | ||||||
|  * Unless required by applicable law or agreed to in writing, software |  | ||||||
|  * distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|  * See the License for the specific language governing permissions and |  | ||||||
|  * limitations under the License. |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| package com.google.zxing.integration.android; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * <p>Encapsulates the result of a barcode scan invoked through {@link IntentIntegrator}.</p> |  | ||||||
|  * |  | ||||||
|  * @author Sean Owen |  | ||||||
|  */ |  | ||||||
| public final class IntentResult { |  | ||||||
| 
 |  | ||||||
|     private final String contents; |  | ||||||
|     private final String formatName; |  | ||||||
|     private final byte[] rawBytes; |  | ||||||
|     private final Integer orientation; |  | ||||||
|     private final String errorCorrectionLevel; |  | ||||||
| 
 |  | ||||||
|     IntentResult() { |  | ||||||
|         this(null, null, null, null, null); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     IntentResult(String contents, |  | ||||||
|                  String formatName, |  | ||||||
|                  byte[] rawBytes, |  | ||||||
|                  Integer orientation, |  | ||||||
|                  String errorCorrectionLevel) { |  | ||||||
|         this.contents = contents; |  | ||||||
|         this.formatName = formatName; |  | ||||||
|         this.rawBytes = rawBytes; |  | ||||||
|         this.orientation = orientation; |  | ||||||
|         this.errorCorrectionLevel = errorCorrectionLevel; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return raw content of barcode |  | ||||||
|      */ |  | ||||||
|     public String getContents() { |  | ||||||
|         return contents; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return name of format, like "QR_CODE", "UPC_A". See {@code BarcodeFormat} for more format names. |  | ||||||
|      */ |  | ||||||
|     public String getFormatName() { |  | ||||||
|         return formatName; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return raw bytes of the barcode content, if applicable, or null otherwise |  | ||||||
|      */ |  | ||||||
|     public byte[] getRawBytes() { |  | ||||||
|         return rawBytes; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return rotation of the image, in degrees, which resulted in a successful scan. May be null. |  | ||||||
|      */ |  | ||||||
|     public Integer getOrientation() { |  | ||||||
|         return orientation; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return name of the error correction level used in the barcode, if applicable |  | ||||||
|      */ |  | ||||||
|     public String getErrorCorrectionLevel() { |  | ||||||
|         return errorCorrectionLevel; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public String toString() { |  | ||||||
|         int rawBytesLength = rawBytes == null ? 0 : rawBytes.length; |  | ||||||
|         return "Format: " + formatName + '\n' + |  | ||||||
|                 "Contents: " + contents + '\n' + |  | ||||||
|                 "Raw bytes: (" + rawBytesLength + " bytes)\n" + |  | ||||||
|                 "Orientation: " + orientation + '\n' + |  | ||||||
|                 "EC level: " + errorCorrectionLevel + '\n'; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
|  | @ -1,109 +0,0 @@ | ||||||
| package com.meerkat.laura.fakescannerapp; |  | ||||||
| 
 |  | ||||||
| import android.content.res.Configuration; |  | ||||||
| import android.os.Bundle; |  | ||||||
| import android.preference.PreferenceActivity; |  | ||||||
| import android.support.annotation.LayoutRes; |  | ||||||
| import android.support.annotation.Nullable; |  | ||||||
| import android.support.v7.app.ActionBar; |  | ||||||
| import android.support.v7.app.AppCompatDelegate; |  | ||||||
| import android.support.v7.widget.Toolbar; |  | ||||||
| import android.view.MenuInflater; |  | ||||||
| import android.view.View; |  | ||||||
| import android.view.ViewGroup; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * A {@link android.preference.PreferenceActivity} which implements and proxies the necessary calls |  | ||||||
|  * to be used with AppCompat. |  | ||||||
|  */ |  | ||||||
| public abstract class AppCompatPreferenceActivity extends PreferenceActivity { |  | ||||||
| 
 |  | ||||||
|     private AppCompatDelegate mDelegate; |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     protected void onCreate(Bundle savedInstanceState) { |  | ||||||
|         getDelegate().installViewFactory(); |  | ||||||
|         getDelegate().onCreate(savedInstanceState); |  | ||||||
|         super.onCreate(savedInstanceState); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     protected void onPostCreate(Bundle savedInstanceState) { |  | ||||||
|         super.onPostCreate(savedInstanceState); |  | ||||||
|         getDelegate().onPostCreate(savedInstanceState); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public ActionBar getSupportActionBar() { |  | ||||||
|         return getDelegate().getSupportActionBar(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public void setSupportActionBar(@Nullable Toolbar toolbar) { |  | ||||||
|         getDelegate().setSupportActionBar(toolbar); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public MenuInflater getMenuInflater() { |  | ||||||
|         return getDelegate().getMenuInflater(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void setContentView(@LayoutRes int layoutResID) { |  | ||||||
|         getDelegate().setContentView(layoutResID); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void setContentView(View view) { |  | ||||||
|         getDelegate().setContentView(view); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void setContentView(View view, ViewGroup.LayoutParams params) { |  | ||||||
|         getDelegate().setContentView(view, params); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void addContentView(View view, ViewGroup.LayoutParams params) { |  | ||||||
|         getDelegate().addContentView(view, params); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     protected void onPostResume() { |  | ||||||
|         super.onPostResume(); |  | ||||||
|         getDelegate().onPostResume(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     protected void onTitleChanged(CharSequence title, int color) { |  | ||||||
|         super.onTitleChanged(title, color); |  | ||||||
|         getDelegate().setTitle(title); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void onConfigurationChanged(Configuration newConfig) { |  | ||||||
|         super.onConfigurationChanged(newConfig); |  | ||||||
|         getDelegate().onConfigurationChanged(newConfig); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     protected void onStop() { |  | ||||||
|         super.onStop(); |  | ||||||
|         getDelegate().onStop(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     protected void onDestroy() { |  | ||||||
|         super.onDestroy(); |  | ||||||
|         getDelegate().onDestroy(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public void invalidateOptionsMenu() { |  | ||||||
|         getDelegate().invalidateOptionsMenu(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private AppCompatDelegate getDelegate() { |  | ||||||
|         if (mDelegate == null) { |  | ||||||
|             mDelegate = AppCompatDelegate.create(this, null); |  | ||||||
|         } |  | ||||||
|         return mDelegate; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,252 +0,0 @@ | ||||||
| package com.meerkat.laura.fakescannerapp; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| import android.content.Intent; |  | ||||||
| import android.content.SharedPreferences; |  | ||||||
| import android.graphics.Color; |  | ||||||
| import android.os.AsyncTask; |  | ||||||
| import android.os.Bundle; |  | ||||||
| import android.preference.Preference; |  | ||||||
| import android.preference.PreferenceManager; |  | ||||||
| import android.support.v7.app.AppCompatActivity; |  | ||||||
| import android.util.Base64; |  | ||||||
| import android.util.Log; |  | ||||||
| import android.view.Menu; |  | ||||||
| import android.view.MenuInflater; |  | ||||||
| import android.view.MenuItem; |  | ||||||
| import android.view.View; |  | ||||||
| import android.widget.Button; |  | ||||||
| import android.widget.TextView; |  | ||||||
| import android.widget.Toast; |  | ||||||
| import ch.qos.logback.classic.android.BasicLogcatConfigurator; |  | ||||||
| import com.google.protobuf.InvalidProtocolBufferException; |  | ||||||
| import com.google.protobuf.Message; |  | ||||||
| import com.google.zxing.integration.android.IntentIntegrator; |  | ||||||
| import com.google.zxing.integration.android.IntentResult; |  | ||||||
| import meerkat.crypto.DigitalSignatureGenerator; |  | ||||||
| import meerkat.crypto.concrete.ECDSADeterministicSignature; |  | ||||||
| import meerkat.pollingstation.PollingStationScanner; |  | ||||||
| import meerkat.pollingstation.ScannerClientAPI; |  | ||||||
| import meerkat.protobuf.PollingStation; |  | ||||||
| 
 |  | ||||||
| import java.math.BigInteger; |  | ||||||
| import java.util.Date; |  | ||||||
| 
 |  | ||||||
| import static com.meerkat.laura.fakescannerapp.R.xml.preferences; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| public class MainActivity extends AppCompatActivity implements View.OnClickListener,  SharedPreferences.OnSharedPreferenceChangeListener { |  | ||||||
| 
 |  | ||||||
|     static { |  | ||||||
|         BasicLogcatConfigurator.configureDefaultContext(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     final public static String SCANNER_NAME = "AndroidScanner"; |  | ||||||
|     SharedPreferences sharedPref; |  | ||||||
| 
 |  | ||||||
|     Button scanBtn; |  | ||||||
|     TextView formatTxt, contentTxt, responseTxt, serverStatus; |  | ||||||
|     PollingStationScanner.ScannerClient scannerClient; |  | ||||||
|     DigitalSignatureGenerator signer; |  | ||||||
| 
 |  | ||||||
|     class AsyncScanConnect extends AsyncTask<Void, Void, Boolean> { |  | ||||||
|         PollingStation.ConnectionServerData serverData; |  | ||||||
| 
 |  | ||||||
|         public AsyncScanConnect(PollingStation.ConnectionServerData connectionServerData) { |  | ||||||
|             this.serverData = connectionServerData; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         protected void onPreExecute() { |  | ||||||
|             super.onPreExecute(); |  | ||||||
|             serverStatus.setTextColor(Color.BLUE); |  | ||||||
|             serverStatus.setText("Connecting to " + serverData.getServerUrl()); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         protected Boolean doInBackground(Void... foo) { |  | ||||||
|             try { |  | ||||||
|                 return scannerClient.connect(serverData); |  | ||||||
|             } catch (RuntimeException e) { |  | ||||||
|                 Log.e("MainActivity", "Exception during connect: " + e.toString()); |  | ||||||
|                 return false; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         protected void onPostExecute(Boolean res) { |  | ||||||
|             super.onPostExecute(res); |  | ||||||
|             if (res) { |  | ||||||
|                 serverStatus.setTextColor(Color.GREEN); |  | ||||||
|                 serverStatus.setText("Connected to " + serverData.getServerUrl()); |  | ||||||
|             } else { |  | ||||||
|                 serverStatus.setTextColor(Color.RED); |  | ||||||
|                 serverStatus.setText("Connection failed: " + serverData.getServerUrl()); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     class AsyncScanSend extends AsyncTask<Void, Void, Boolean> { |  | ||||||
|         Message data; |  | ||||||
| 
 |  | ||||||
|         public AsyncScanSend(Message data) { |  | ||||||
|             this.data = data; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         protected void onPreExecute() { |  | ||||||
|             super.onPreExecute(); |  | ||||||
|             serverStatus.setTextColor(Color.BLUE); |  | ||||||
|             serverStatus.setText("Sending to server..."); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         protected Boolean doInBackground(Void... foo) { |  | ||||||
|             try { |  | ||||||
|                 if (data instanceof PollingStation.ScannedBallot) { |  | ||||||
|                     return scannerClient.newScan((PollingStation.ScannedBallot) data); |  | ||||||
|                 } else if (data instanceof PollingStation.ScanError) { |  | ||||||
|                     return scannerClient.reportError((PollingStation.ScanError) data); |  | ||||||
|                 } else { |  | ||||||
|                     Log.e("MainActivity", "Trying to send invalid message to scanner"); |  | ||||||
|                     return false; |  | ||||||
|                 } |  | ||||||
|             } catch (RuntimeException e) { |  | ||||||
|                 Log.e("MainActivity", "Exception during send: " + e.toString()); |  | ||||||
|                 return false; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         protected void onPostExecute(Boolean res) { |  | ||||||
|             super.onPostExecute(res); |  | ||||||
|             if (res) { |  | ||||||
|                 serverStatus.setTextColor(Color.GREEN); |  | ||||||
|                 serverStatus.setText("Sent successfully"); |  | ||||||
|                 Log.i("MainActivity", "post submitted to API."); |  | ||||||
|             } else { |  | ||||||
|                 serverStatus.setTextColor(Color.RED); |  | ||||||
|                 serverStatus.setText("Send failed"); |  | ||||||
|                 Log.e("MainActivity", "Unable to submit post to API."); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public boolean onCreateOptionsMenu(Menu menu) { |  | ||||||
|         MenuInflater inflater = getMenuInflater(); |  | ||||||
|         inflater.inflate(R.menu.options_menu, menu); |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public boolean onOptionsItemSelected(MenuItem item) { |  | ||||||
|         // Handle item selection
 |  | ||||||
|         switch (item.getItemId()) { |  | ||||||
|             case R.id.action_settings: |  | ||||||
|                 Intent i = new Intent(this, SettingsActivity.class); |  | ||||||
|                 startActivity(i); |  | ||||||
|                 return true; |  | ||||||
|             default: |  | ||||||
|                 return super.onOptionsItemSelected(item); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     void setNewScannerClientAPI(SharedPreferences sharedPref) { |  | ||||||
|         String pscUrlString = sharedPref.getString(SettingsFragment.PSC_URL, ""); |  | ||||||
|         long nonce = 0; |  | ||||||
| 
 |  | ||||||
|         try { |  | ||||||
|             nonce = Long.parseLong(sharedPref.getString(SettingsFragment.PSC_NONCE, "0")); |  | ||||||
|         } catch (NumberFormatException e) { |  | ||||||
|             // Ignore
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (pscUrlString.isEmpty()) |  | ||||||
|             return; |  | ||||||
| 
 |  | ||||||
|         PollingStation.ConnectionServerData serverData = PollingStation.ConnectionServerData.newBuilder() |  | ||||||
|                 .setServerUrl(pscUrlString) |  | ||||||
|                 .setNonce(nonce) |  | ||||||
|                 .build(); |  | ||||||
| 
 |  | ||||||
|         new AsyncScanConnect(serverData).execute(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     protected void onCreate(Bundle savedInstanceState) { |  | ||||||
|         super.onCreate(savedInstanceState); |  | ||||||
|         setContentView(R.layout.activity_main); |  | ||||||
| 
 |  | ||||||
|         signer = new ECDSADeterministicSignature(); |  | ||||||
|         signer.generateSigningCertificate(BigInteger.ONE, new Date(System.currentTimeMillis()), |  | ||||||
|                 new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 365), SCANNER_NAME); |  | ||||||
|         scannerClient = new ScannerClientAPI(signer); |  | ||||||
| 
 |  | ||||||
|         PreferenceManager.setDefaultValues(this, preferences, false); |  | ||||||
|         sharedPref = PreferenceManager.getDefaultSharedPreferences(this); |  | ||||||
|         sharedPref.registerOnSharedPreferenceChangeListener(this); |  | ||||||
| 
 |  | ||||||
|         scanBtn = (Button) findViewById(R.id.scan_button); |  | ||||||
|         formatTxt = (TextView) findViewById(R.id.scan_format); |  | ||||||
|         contentTxt = (TextView) findViewById(R.id.scan_content); |  | ||||||
|         responseTxt = (TextView) findViewById(R.id.server_response); |  | ||||||
|         serverStatus = (TextView) findViewById(R.id.serverStatus); |  | ||||||
| 
 |  | ||||||
|         setNewScannerClientAPI(sharedPref); |  | ||||||
|         scanBtn.setOnClickListener(this); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public void onClick(View v) { |  | ||||||
|         if (v.getId() == R.id.scan_button) { |  | ||||||
|             IntentIntegrator scanIntegrator = new IntentIntegrator(this); |  | ||||||
|             formatTxt.setText("Initiating scan..."); |  | ||||||
|             scanIntegrator.initiateScan(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public void onActivityResult(int requestCode, int resultCode, Intent intent) { |  | ||||||
|         IntentResult scanningResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent); |  | ||||||
|         if (scanningResult != null) { |  | ||||||
|             String scanContent = scanningResult.getContents(); |  | ||||||
|             byte[] rawBytes = scanningResult.getRawBytes(); |  | ||||||
|             String scanFormat = scanningResult.getFormatName(); |  | ||||||
|             formatTxt.setText("FORMAT: " + scanFormat); |  | ||||||
|             contentTxt.setText("CONTENT: " + scanContent + "sending data to server..."); |  | ||||||
| //            sendPost(rawBytes);
 |  | ||||||
|             sendPost(scanContent); |  | ||||||
|         } else { |  | ||||||
|             Toast toast = Toast.makeText(getApplicationContext(), |  | ||||||
|                     "No scan data received!", Toast.LENGTH_SHORT); |  | ||||||
|             toast.show(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     //    public void sendPost(byte[] body) {
 |  | ||||||
|     public void sendPost(String scanContent) { |  | ||||||
| 
 |  | ||||||
|         try { |  | ||||||
|             PollingStation.ScannedBallot scannedBallot = PollingStation.ScannedBallot.parseFrom(Base64.decode(scanContent.getBytes(), Base64.DEFAULT)); |  | ||||||
|             //
 |  | ||||||
|             //        PollingStation.ScannedData scannedData = PollingStation.ScannedData.newBuilder()
 |  | ||||||
|             //                .setChannel(ByteString.copyFrom(body))
 |  | ||||||
|             //                .build();
 |  | ||||||
| 
 |  | ||||||
|             new AsyncScanSend(scannedBallot).execute(); |  | ||||||
|         } catch (InvalidProtocolBufferException e) { |  | ||||||
|             e.printStackTrace(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public void showResponse(String response) { |  | ||||||
|         responseTxt.setText(response); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key){ |  | ||||||
|         setNewScannerClientAPI(sharedPreferences); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,26 +0,0 @@ | ||||||
| package com.meerkat.laura.fakescannerapp; |  | ||||||
| 
 |  | ||||||
| import android.app.Activity; |  | ||||||
| import android.content.SharedPreferences; |  | ||||||
| import android.os.Bundle; |  | ||||||
| import android.preference.PreferenceManager; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by talm on 25/06/17. |  | ||||||
|  */ |  | ||||||
| public class SettingsActivity extends Activity { |  | ||||||
|     SettingsFragment settings; |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     protected void onCreate(Bundle savedInstanceState) { |  | ||||||
|         super.onCreate(savedInstanceState); |  | ||||||
| 
 |  | ||||||
|         settings = new SettingsFragment(); |  | ||||||
|         PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(settings); |  | ||||||
| 
 |  | ||||||
|         // Display the fragment as the main content.
 |  | ||||||
|         getFragmentManager().beginTransaction() |  | ||||||
|                 .replace(android.R.id.content, settings) |  | ||||||
|                 .commit(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,34 +0,0 @@ | ||||||
| package com.meerkat.laura.fakescannerapp; |  | ||||||
| 
 |  | ||||||
| import android.content.SharedPreferences; |  | ||||||
| import android.os.Bundle; |  | ||||||
| import android.preference.Preference; |  | ||||||
| import android.preference.PreferenceActivity; |  | ||||||
| import android.preference.PreferenceFragment; |  | ||||||
| import android.preference.PreferenceManager; |  | ||||||
| 
 |  | ||||||
| public class SettingsFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener { |  | ||||||
|     public final static String PSC_URL = "psc_url"; |  | ||||||
|     public final static String PSC_NONCE = "psc_nonce"; |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void onCreate(Bundle savedInstanceState) { |  | ||||||
|         super.onCreate(savedInstanceState); |  | ||||||
|         addPreferencesFromResource(R.xml.preferences); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { |  | ||||||
|         Preference pref = findPreference(key); |  | ||||||
|         if (pref == null) |  | ||||||
|             return; |  | ||||||
|         switch(key) { |  | ||||||
|             case SettingsFragment.PSC_URL: |  | ||||||
|                 pref.setSummary(sharedPreferences.getString(key, "")); |  | ||||||
|                 break; |  | ||||||
|             case SettingsFragment.PSC_NONCE: |  | ||||||
|                 pref.setSummary(sharedPreferences.getString(key, "")); |  | ||||||
|                 break; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,9 +0,0 @@ | ||||||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
|         android:width="24dp" |  | ||||||
|         android:height="24dp" |  | ||||||
|         android:viewportWidth="24.0" |  | ||||||
|         android:viewportHeight="24.0"> |  | ||||||
|     <path |  | ||||||
|             android:fillColor="#FF000000" |  | ||||||
|             android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm1,15h-2v-6h2v6zm0,-8h-2V7h2v2z"/> |  | ||||||
| </vector> |  | ||||||
|  | @ -1,9 +0,0 @@ | ||||||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
|         android:width="24dp" |  | ||||||
|         android:height="24dp" |  | ||||||
|         android:viewportWidth="24.0" |  | ||||||
|         android:viewportHeight="24.0"> |  | ||||||
|     <path |  | ||||||
|             android:fillColor="#FF000000" |  | ||||||
|             android:pathData="M11.5,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.9,2 2,2zm6.5,-6v-5.5c0,-3.07 -2.13,-5.64 -5,-6.32V3.5c0,-0.83 -0.67,-1.5 -1.5,-1.5S10,2.67 10,3.5v0.68c-2.87,0.68 -5,3.25 -5,6.32V16l-2,2v1h17v-1l-2,-2z"/> |  | ||||||
| </vector> |  | ||||||
|  | @ -1,9 +0,0 @@ | ||||||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
|         android:width="24dp" |  | ||||||
|         android:height="24dp" |  | ||||||
|         android:viewportWidth="24.0" |  | ||||||
|         android:viewportHeight="24.0"> |  | ||||||
|     <path |  | ||||||
|             android:fillColor="#FF000000" |  | ||||||
|             android:pathData="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01,-.25 1.97,-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0,-4.42,-3.58,-8,-8,-8zm0 14c-3.31 0,-6,-2.69,-6,-6 0,-1.01.25,-1.97.7,-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4,-4,-4,-4v3z"/> |  | ||||||
| </vector> |  | ||||||
|  | @ -1,58 +0,0 @@ | ||||||
| <?xml version="1.0" encoding="utf-8"?> |  | ||||||
| <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
|     xmlns:tools="http://schemas.android.com/tools" |  | ||||||
|     android:layout_width="match_parent" |  | ||||||
|     android:layout_height="match_parent" |  | ||||||
|     android:paddingLeft="@dimen/activity_horizontal_margin" |  | ||||||
|     android:paddingRight="@dimen/activity_horizontal_margin" |  | ||||||
|     android:paddingTop="@dimen/activity_vertical_margin" |  | ||||||
|     android:paddingBottom="@dimen/activity_vertical_margin" |  | ||||||
|     tools:context=".MainActivity"> |  | ||||||
| 
 |  | ||||||
|     <TextView android:text="@string/instructions" android:layout_width="wrap_content" |  | ||||||
|               android:layout_height="wrap_content" |  | ||||||
|               android:id="@+id/textView" /> |  | ||||||
| 
 |  | ||||||
|     <Button |  | ||||||
|         android:layout_width="wrap_content" |  | ||||||
|         android:layout_height="wrap_content" |  | ||||||
|         android:text="@string/button_txt" |  | ||||||
|         android:id="@+id/scan_button" |  | ||||||
|         android:layout_below="@+id/textView" |  | ||||||
|         android:layout_centerHorizontal="true" /> |  | ||||||
| 
 |  | ||||||
|     <TextView |  | ||||||
|         android:layout_width="wrap_content" |  | ||||||
|         android:layout_height="wrap_content" |  | ||||||
|         android:textIsSelectable="true" |  | ||||||
|         android:id="@+id/scan_format" |  | ||||||
|         android:layout_below="@+id/scan_button" |  | ||||||
|         android:layout_centerHorizontal="true" |  | ||||||
|         android:layout_marginTop="44dp" /> |  | ||||||
| 
 |  | ||||||
|     <TextView |  | ||||||
|         android:layout_width="wrap_content" |  | ||||||
|         android:layout_height="wrap_content" |  | ||||||
|         android:textIsSelectable="true" |  | ||||||
|         android:id="@+id/scan_content" |  | ||||||
|         android:layout_below="@+id/scan_format" |  | ||||||
|         android:layout_centerHorizontal="true" |  | ||||||
|         android:layout_marginTop="32dp" /> |  | ||||||
| 
 |  | ||||||
|     <TextView |  | ||||||
|         android:id="@+id/server_response" |  | ||||||
|         android:layout_below="@+id/scan_content" |  | ||||||
|         android:layout_width="wrap_content" |  | ||||||
|         android:layout_height="wrap_content" |  | ||||||
|         android:textIsSelectable="true" |  | ||||||
|         android:layout_centerHorizontal="true" |  | ||||||
|         android:layout_marginTop="44dp" /> |  | ||||||
|     <TextView |  | ||||||
|             android:text="Not Connected" |  | ||||||
|             android:layout_width="wrap_content" |  | ||||||
|             android:layout_height="wrap_content" android:id="@+id/serverStatus" |  | ||||||
|             android:layout_alignParentBottom="true" |  | ||||||
|             android:layout_alignParentRight="true" android:layout_alignParentEnd="true" |  | ||||||
|             android:layout_alignParentLeft="true" android:layout_alignParentStart="true"/> |  | ||||||
| 
 |  | ||||||
| </RelativeLayout> |  | ||||||
|  | @ -1,9 +0,0 @@ | ||||||
| <?xml version="1.0" encoding="utf-8"?> |  | ||||||
| <menu xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
|     xmlns:app="http://schemas.android.com/apk/res-auto"> |  | ||||||
| 
 |  | ||||||
|     <!-- Settings, should always be in the overflow --> |  | ||||||
|     <item android:id="@+id/action_settings" |  | ||||||
|         android:title="@string/action_settings" |  | ||||||
|         app:showAsAction="never"/> |  | ||||||
| </menu> |  | ||||||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 3.3 KiB | 
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 2.2 KiB | 
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 4.7 KiB | 
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 7.5 KiB | 
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 10 KiB | 
|  | @ -1,6 +0,0 @@ | ||||||
| <resources> |  | ||||||
|     <!-- Example customization of dimensions originally defined in res/values/dimens.xml |  | ||||||
|          (such as screen margins) for screens with more than 820dp of available width. This |  | ||||||
|          would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). --> |  | ||||||
|     <dimen name="activity_horizontal_margin">64dp</dimen> |  | ||||||
| </resources> |  | ||||||
|  | @ -1,6 +0,0 @@ | ||||||
| <?xml version="1.0" encoding="utf-8"?> |  | ||||||
| <resources> |  | ||||||
|     <color name="colorPrimary">#3F51B5</color> |  | ||||||
|     <color name="colorPrimaryDark">#303F9F</color> |  | ||||||
|     <color name="colorAccent">#FF4081</color> |  | ||||||
| </resources> |  | ||||||
|  | @ -1,5 +0,0 @@ | ||||||
| <resources> |  | ||||||
|     <!-- Default screen margins, per the Android Design guidelines. --> |  | ||||||
|     <dimen name="activity_horizontal_margin">16dp</dimen> |  | ||||||
|     <dimen name="activity_vertical_margin">16dp</dimen> |  | ||||||
| </resources> |  | ||||||
|  | @ -1,80 +0,0 @@ | ||||||
| <resources> |  | ||||||
|     <string name="app_name">FakeScannerApp</string> |  | ||||||
|     <string name="button_txt">SCAN</string> |  | ||||||
|     <string name="instructions">Please, press the button below to scan the vote receipt.</string> |  | ||||||
|     <string name="title_activity_settings">Settings</string> |  | ||||||
| 
 |  | ||||||
|     <!-- Strings related to Settings --> |  | ||||||
|     <string name="action_settings">Settings</string> |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     <!-- Example General settings --> |  | ||||||
|     <string name="pref_header_general">General</string> |  | ||||||
| 
 |  | ||||||
|     <string name="pref_title_social_recommendations">Enable social recommendations</string> |  | ||||||
|     <string name="pref_description_social_recommendations">Recommendations for people to contact based on your message |  | ||||||
|         history |  | ||||||
|     </string> |  | ||||||
| 
 |  | ||||||
|     <string name="pref_title_psc_url">Scanner Service URL</string> |  | ||||||
|     <string name="pref_default_psc_url">http://192.168.70.10:8880/scan</string> |  | ||||||
| 
 |  | ||||||
|     <string name="pref_title_add_friends_to_messages">Add friends to messages</string> |  | ||||||
|     <string-array name="pref_example_list_titles"> |  | ||||||
|         <item>Always</item> |  | ||||||
|         <item>When possible</item> |  | ||||||
|         <item>Never</item> |  | ||||||
|     </string-array> |  | ||||||
|     <string-array name="pref_example_list_values"> |  | ||||||
|         <item>1</item> |  | ||||||
|         <item>0</item> |  | ||||||
|         <item>-1</item> |  | ||||||
|     </string-array> |  | ||||||
| 
 |  | ||||||
|     <!-- Example settings for Data & Sync --> |  | ||||||
|     <string name="pref_header_data_sync">Data & sync</string> |  | ||||||
| 
 |  | ||||||
|     <string name="pref_title_sync_frequency">Sync frequency</string> |  | ||||||
|     <string-array name="pref_sync_frequency_titles"> |  | ||||||
|         <item>15 minutes</item> |  | ||||||
|         <item>30 minutes</item> |  | ||||||
|         <item>1 hour</item> |  | ||||||
|         <item>3 hours</item> |  | ||||||
|         <item>6 hours</item> |  | ||||||
|         <item>Never</item> |  | ||||||
|     </string-array> |  | ||||||
|     <string-array name="pref_sync_frequency_values"> |  | ||||||
|         <item>15</item> |  | ||||||
|         <item>30</item> |  | ||||||
|         <item>60</item> |  | ||||||
|         <item>180</item> |  | ||||||
|         <item>360</item> |  | ||||||
|         <item>-1</item> |  | ||||||
|     </string-array> |  | ||||||
| 
 |  | ||||||
|     <string-array name="list_preference_entries"> |  | ||||||
|         <item>Entry 1</item> |  | ||||||
|         <item>Entry 2</item> |  | ||||||
|         <item>Entry 3</item> |  | ||||||
|     </string-array> |  | ||||||
| 
 |  | ||||||
|     <string-array name="list_preference_entry_values"> |  | ||||||
|         <item>1</item> |  | ||||||
|         <item>2</item> |  | ||||||
|         <item>3</item> |  | ||||||
|     </string-array> |  | ||||||
| 
 |  | ||||||
|     <string-array name="multi_select_list_preference_default_value"/> |  | ||||||
| 
 |  | ||||||
|     <string name="pref_title_system_sync_settings">System sync settings</string> |  | ||||||
| 
 |  | ||||||
|     <!-- Example settings for Notifications --> |  | ||||||
|     <string name="pref_header_notifications">Notifications</string> |  | ||||||
| 
 |  | ||||||
|     <string name="pref_title_new_message_notifications">New message notifications</string> |  | ||||||
| 
 |  | ||||||
|     <string name="pref_title_ringtone">Ringtone</string> |  | ||||||
|     <string name="pref_ringtone_silent">Silent</string> |  | ||||||
| 
 |  | ||||||
|     <string name="pref_title_vibrate">Vibrate</string> |  | ||||||
| </resources> |  | ||||||
|  | @ -1,11 +0,0 @@ | ||||||
| <resources> |  | ||||||
| 
 |  | ||||||
|     <!-- Base application theme. --> |  | ||||||
|     <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> |  | ||||||
|         <!-- Customize your theme here. --> |  | ||||||
|         <item name="colorPrimary">@color/colorPrimary</item> |  | ||||||
|         <item name="colorPrimaryDark">@color/colorPrimaryDark</item> |  | ||||||
|         <item name="colorAccent">@color/colorAccent</item> |  | ||||||
|     </style> |  | ||||||
| 
 |  | ||||||
| </resources> |  | ||||||
|  | @ -1,28 +0,0 @@ | ||||||
| <PreferenceScreen xmlns:tools="http://schemas.android.com/tools" |  | ||||||
|     xmlns:android="http://schemas.android.com/apk/res/android"> |  | ||||||
| 
 |  | ||||||
|     <!-- NOTE: EditTextPreference accepts EditText attributes. --> |  | ||||||
|     <!-- NOTE: EditTextPreference's summary should be set to its value by the activity code. --> |  | ||||||
|     <EditTextPreference |  | ||||||
|             android:key="psc_url" |  | ||||||
|             android:title="@string/pref_title_psc_url" |  | ||||||
|             android:defaultValue="@string/pref_default_psc_url" |  | ||||||
|             android:selectAllOnFocus="true" |  | ||||||
|             android:singleLine="true" |  | ||||||
|             android:maxLines="1" |  | ||||||
|         android:inputType="textUri" /> |  | ||||||
| 
 |  | ||||||
|     <EditTextPreference |  | ||||||
|         android:key="psc_nonce" |  | ||||||
|         android:title="Nonce" |  | ||||||
|         android:defaultValue="0" |  | ||||||
|         android:selectAllOnFocus="true" |  | ||||||
|         android:singleLine="true" |  | ||||||
|         android:maxLines="1" |  | ||||||
|         android:inputType="number"/> |  | ||||||
| 
 |  | ||||||
|     <!-- NOTE: Hide buttons to simplify the UI. Users can touch outside the dialog to |  | ||||||
|          dismiss it. --> |  | ||||||
|     <!-- NOTE: ListPreference's summary should be set to its value by the activity code. --> |  | ||||||
| 
 |  | ||||||
| </PreferenceScreen> |  | ||||||
|  | @ -1,15 +0,0 @@ | ||||||
| package com.meerkat.laura.fakescannerapp; |  | ||||||
| 
 |  | ||||||
| import org.junit.Test; |  | ||||||
| 
 |  | ||||||
| import static org.junit.Assert.*; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * To work on unit tests, switch the Test Artifact in the Build Variants view. |  | ||||||
|  */ |  | ||||||
| public class ExampleUnitTest { |  | ||||||
|     @Test |  | ||||||
|     public void addition_isCorrect() throws Exception { |  | ||||||
|         assertEquals(4, 2 + 2); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
							
								
								
									
										51
									
								
								build.gradle
								
								
								
								
							
							
						
						
									
										51
									
								
								build.gradle
								
								
								
								
							|  | @ -1,56 +1,11 @@ | ||||||
| apply plugin: com.google.gradle.osdetector.OsDetectorPlugin |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| subprojects { proj -> | subprojects { proj -> | ||||||
|     proj.afterEvaluate { |     proj.afterEvaluate { | ||||||
|         // Used to generate initial maven-dir layout |         // Used to generate initial maven-dir layout | ||||||
|         task "create-dirs" { |         task "create-dirs" { description = "Create default maven directory structure" } << { | ||||||
|             description = "Create default maven directory structure" |             sourceSets*.java.srcDirs*.each { it.mkdirs() } | ||||||
|             doLast { |             sourceSets*.resources.srcDirs*.each { it.mkdirs() } | ||||||
|                 sourceSets*.java.srcDirs*.each { it.mkdirs() } |  | ||||||
|                 sourceSets*.resources.srcDirs*.each { it.mkdirs() } |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 |  | ||||||
| // Script to create a copy of the protobuf compiler jar in the top-level build directory |  | ||||||
| // (will make it easier to configure the intellij protobuf plugin) |  | ||||||
| repositories { |  | ||||||
|     mavenLocal(); |  | ||||||
|     mavenCentral(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| project.ext.protocLocation = 'com.google.protobuf:protoc:3.+' |  | ||||||
| 
 |  | ||||||
| // Copied from Google's protobuf plugin code |  | ||||||
| File getProtocDep(protocLocation) { |  | ||||||
|     // create a project configuration dependency for the artifact |  | ||||||
|     Configuration config = project.configurations.create("protobufToolsLocator") { |  | ||||||
|         visible = false |  | ||||||
|         transitive = false |  | ||||||
|         extendsFrom = [] |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     def groupId, artifact, version |  | ||||||
|     (groupId, artifact, version) = protocLocation.split(":") |  | ||||||
|     def notation = [group: groupId, |  | ||||||
|                     name: artifact, |  | ||||||
|                     version: version, |  | ||||||
|                     classifier: project.osdetector.classifier, |  | ||||||
|                     ext: 'exe'] |  | ||||||
|     Dependency dep = project.dependencies.add(config.name, notation) |  | ||||||
| 
 |  | ||||||
|     File file = config.fileCollection(dep).singleFile |  | ||||||
| 
 |  | ||||||
|     return file |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| task getprotoc(type: Copy) { |  | ||||||
|     from getProtocDep(project.ext.protocLocation) |  | ||||||
|     rename { file -> return "protoc" } |  | ||||||
|     into "${buildDir}/" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| 
 | 
 | ||||||
| plugins { | plugins { | ||||||
|   id "us.kirchmeier.capsule" version "1.0.2" |   id "us.kirchmeier.capsule" version "1.0.1" | ||||||
|   id 'com.google.protobuf' version '0.8.1' |   id 'com.google.protobuf' version '0.7.0' | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| apply plugin: 'java' | apply plugin: 'java' | ||||||
|  | @ -25,11 +25,8 @@ ext { | ||||||
|          |          | ||||||
|         // Credentials for IDC nexus repositories (needed only for using unstable repositories and publishing) |         // Credentials for IDC nexus repositories (needed only for using unstable repositories and publishing) | ||||||
|         // Should be set in ${HOME}/.gradle/gradle.properties |         // Should be set in ${HOME}/.gradle/gradle.properties | ||||||
| 
 |         nexusUser =  project.hasProperty('nexusUser') ? project.property('nexusUser') : "" | ||||||
|         // Credentials for publishing repositories |         nexusPassword = project.hasProperty('nexusPassword') ? project.property('nexusPassword') : "" | ||||||
|         publishRepository = "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}" |  | ||||||
|         publishUser =  project.hasProperty('publishUser') ? project.property('publishUser') : "" |  | ||||||
|         publishPassword = project.hasProperty('publishPassword') ? project.property('publishPassword') : "" |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| description = "TODO: Add a description" | description = "TODO: Add a description" | ||||||
|  | @ -134,19 +131,17 @@ if (project.hasProperty('mainClassName') && (mainClassName != null)) { | ||||||
| 
 | 
 | ||||||
|         destinationDir = buildDir |         destinationDir = buildDir | ||||||
| 
 | 
 | ||||||
|         def fatMain |         def fatMain = hasProperty('fatmain') ? fatmain : mainClassName | ||||||
|          |  | ||||||
|         if (this.hasProperty('fatmain')) { |  | ||||||
|             fatMain = fatmain  |  | ||||||
|             appendix = "fat-${fatMain}" |  | ||||||
|         } else { |  | ||||||
|             fatMain = mainClassName |  | ||||||
|             appendix = "fat" |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         applicationClass fatMain |         applicationClass fatMain | ||||||
| 
 | 
 | ||||||
|         def testJar = this.hasProperty('test') |         def testJar = hasProperty('test') | ||||||
|  | 
 | ||||||
|  |         if (hasProperty('fatmain')) { | ||||||
|  |             appendix = "fat-${fatMain}" | ||||||
|  |         } else { | ||||||
|  |             appendix = "fat" | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         if (testJar) { |         if (testJar) { | ||||||
|             from sourceSets.test.output |             from sourceSets.test.output | ||||||
|  | @ -160,6 +155,21 @@ if (project.hasProperty('mainClassName') && (mainClassName != null)) { | ||||||
|  *===================================*/ |  *===================================*/ | ||||||
| 
 | 
 | ||||||
| repositories { | repositories { | ||||||
|  |          | ||||||
|  |         // Prefer the local nexus repository (it may have 3rd party artifacts not found in mavenCentral) | ||||||
|  |         maven {  | ||||||
|  |             url nexusRepository | ||||||
|  |              | ||||||
|  |             if (isSnapshot) { | ||||||
|  |                 credentials { username  | ||||||
|  |                     password  | ||||||
|  | 
 | ||||||
|  |                     username nexusUser | ||||||
|  |                     password nexusPassword  | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         // Use local maven repository |         // Use local maven repository | ||||||
|         mavenLocal() |         mavenLocal() | ||||||
| 
 | 
 | ||||||
|  | @ -167,15 +177,13 @@ repositories { | ||||||
|         mavenCentral() |         mavenCentral() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| task "info"  { | task "info" << { | ||||||
|     doLast { |  | ||||||
|         println "Project: ${project.name}" |         println "Project: ${project.name}" | ||||||
|         println "Description: ${project.description}" | println "Description: ${project.description}" | ||||||
|         println "--------------------------" |         println "--------------------------" | ||||||
|         println "GroupId: $groupId" |         println "GroupId: $groupId" | ||||||
|         println "Version: $version (${isSnapshot ? 'snapshot' : 'release'})" |         println "Version: $version (${isSnapshot ? 'snapshot' : 'release'})" | ||||||
|         println "" |         println "" | ||||||
|     } |  | ||||||
| } | } | ||||||
| info.description 'Print some information about project parameters' | info.description 'Print some information about project parameters' | ||||||
| 
 | 
 | ||||||
|  | @ -197,12 +205,12 @@ publishing { | ||||||
|     } |     } | ||||||
|     repositories { |     repositories { | ||||||
|         maven { |         maven { | ||||||
|             url publishRepository |             url "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}" | ||||||
|             credentials { username  |             credentials { username  | ||||||
|                 password  |                 password  | ||||||
| 
 | 
 | ||||||
|                 username publishUser |                 username nexusUser | ||||||
|                 password publishPassword |                 password nexusPassword  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -1,25 +0,0 @@ | ||||||
| apply plugin: 'groovy' |  | ||||||
| 
 |  | ||||||
| description = 'A Gradle plugin that detects the OS name and architecture, providing a uniform\ |  | ||||||
|  classifier to be used in the names of native artifacts.' |  | ||||||
| group = 'com.google.gradle' |  | ||||||
| 
 |  | ||||||
| // The major and minor versions are aligned with the Maven plugin's. |  | ||||||
| version = '1.5.0-SNAPSHOT' |  | ||||||
| 
 |  | ||||||
| dependencies { |  | ||||||
|     compile gradleApi(), |  | ||||||
|           localGroovy(), |  | ||||||
|           'kr.motd.maven:os-maven-plugin:1.5.0.Final' |  | ||||||
|     // Newer versions of gradle conflict with the default guava 10. dependency of the maven |  | ||||||
|     // plugin. |  | ||||||
|     compile("com.google.guava:guava:19.0") { |  | ||||||
|         force = true |  | ||||||
|     } |  | ||||||
|     testCompile 'junit:junit:4.12' |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| repositories { |  | ||||||
|   mavenLocal() |  | ||||||
|   mavenCentral() |  | ||||||
| } |  | ||||||
|  | @ -1 +0,0 @@ | ||||||
| ../gradlew |  | ||||||
|  | @ -1,25 +0,0 @@ | ||||||
| /* |  | ||||||
|  * Copyright 2015 Google Inc. All Rights Reserved. |  | ||||||
|  * |  | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|  * you may not use this file except in compliance with the License. |  | ||||||
|  * You may obtain a copy of the License at |  | ||||||
|  * |  | ||||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  * |  | ||||||
|  *     Unless required by applicable law or agreed to in writing, software |  | ||||||
|  *     distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|  *     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|  *     See the License for the specific language governing permissions and |  | ||||||
|  *     limitations under the License. |  | ||||||
|  */ |  | ||||||
| package com.google.gradle.osdetector |  | ||||||
| 
 |  | ||||||
| import org.gradle.api.Plugin |  | ||||||
| import org.gradle.api.Project |  | ||||||
| 
 |  | ||||||
| class OsDetectorPlugin implements Plugin<Project> { |  | ||||||
|   void apply(final Project project) { |  | ||||||
|     project.extensions.create('osdetector', OsDetector) |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  | @ -1,120 +0,0 @@ | ||||||
| /* |  | ||||||
|  * Copyright 2015 Google Inc. All Rights Reserved. |  | ||||||
|  * |  | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|  * you may not use this file except in compliance with the License. |  | ||||||
|  * You may obtain a copy of the License at |  | ||||||
|  * |  | ||||||
|  *     http://www.apache.org/licenses/LICENSE-2.0
 |  | ||||||
|  * |  | ||||||
|  *     Unless required by applicable law or agreed to in writing, software |  | ||||||
|  *     distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|  *     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|  *     See the License for the specific language governing permissions and |  | ||||||
|  *     limitations under the License. |  | ||||||
|  */ |  | ||||||
| package com.google.gradle.osdetector; |  | ||||||
| 
 |  | ||||||
| import kr.motd.maven.os.Detector; |  | ||||||
| 
 |  | ||||||
| import org.slf4j.Logger; |  | ||||||
| import org.slf4j.LoggerFactory; |  | ||||||
| 
 |  | ||||||
| import java.util.ArrayList; |  | ||||||
| import java.util.List; |  | ||||||
| import java.util.Properties; |  | ||||||
| 
 |  | ||||||
| public class OsDetector { |  | ||||||
|   private static final Logger logger = LoggerFactory.getLogger(OsDetector.class.getName()); |  | ||||||
| 
 |  | ||||||
|   private final List<String> classifierWithLikes = new ArrayList<String>(); |  | ||||||
|   private Impl impl; |  | ||||||
| 
 |  | ||||||
|   public String getOs() { |  | ||||||
|     return (String) getImpl().detectedProperties.get(Detector.DETECTED_NAME); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   public String getArch() { |  | ||||||
|     return (String) getImpl().detectedProperties.get(Detector.DETECTED_ARCH); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   public String getClassifier() { |  | ||||||
|     return (String) getImpl().detectedProperties.get(Detector.DETECTED_CLASSIFIER); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   public Release getRelease() { |  | ||||||
|     Impl impl = getImpl(); |  | ||||||
|     Object releaseId = impl.detectedProperties.get(Detector.DETECTED_RELEASE); |  | ||||||
|     if (releaseId == null) { |  | ||||||
|       return null; |  | ||||||
|     } |  | ||||||
|     return new Release(impl); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   public synchronized void setClassifierWithLikes(List<String> classifierWithLikes) { |  | ||||||
|     if (impl != null) { |  | ||||||
|       throw new IllegalStateException("classifierWithLikes must be set before osdetector is read."); |  | ||||||
|     } |  | ||||||
|     this.classifierWithLikes.clear(); |  | ||||||
|     this.classifierWithLikes.addAll(classifierWithLikes); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   private synchronized Impl getImpl() { |  | ||||||
|     if (impl == null) { |  | ||||||
|       impl = new Impl(classifierWithLikes); |  | ||||||
|     } |  | ||||||
|     return impl; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /** |  | ||||||
|    * Accessor to information about the current OS release. |  | ||||||
|    */ |  | ||||||
|   public static class Release { |  | ||||||
|     private final Impl impl; |  | ||||||
| 
 |  | ||||||
|     private Release(Impl impl) { |  | ||||||
|       this.impl = impl; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Returns the release ID. |  | ||||||
|      */ |  | ||||||
|     public String getId() { |  | ||||||
|       return (String) impl.detectedProperties.get(Detector.DETECTED_RELEASE); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Returns the version ID. |  | ||||||
|      */ |  | ||||||
|     public String getVersion() { |  | ||||||
|       return (String) impl.detectedProperties.get(Detector.DETECTED_RELEASE_VERSION); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Returns {@code true} if this release is a variant of the given base release (for example, |  | ||||||
|      * ubuntu is "like" debian). |  | ||||||
|      */ |  | ||||||
|     public boolean isLike(String baseRelease) { |  | ||||||
|       return impl.detectedProperties.containsKey( |  | ||||||
|           Detector.DETECTED_RELEASE_LIKE_PREFIX + baseRelease); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   private static class Impl extends Detector { |  | ||||||
|     final Properties detectedProperties = System.getProperties(); |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     protected void log(String message) { |  | ||||||
|       logger.info(message); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     protected void logProperty(String name, String value) { |  | ||||||
|       logger.info(name + "=" + value); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     Impl(List<String> classifierWithLikes) { |  | ||||||
|       detect(detectedProperties, classifierWithLikes); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  | @ -1 +0,0 @@ | ||||||
| implementation-class=com.google.gradle.osdetector.OsDetectorPlugin |  | ||||||
|  | @ -1,62 +0,0 @@ | ||||||
| /* |  | ||||||
|  * Copyright 2015 Google Inc. All Rights Reserved. |  | ||||||
|  * |  | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  | ||||||
|  * you may not use this file except in compliance with the License. |  | ||||||
|  * You may obtain a copy of the License at |  | ||||||
|  * |  | ||||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
|  * |  | ||||||
|  *     Unless required by applicable law or agreed to in writing, software |  | ||||||
|  *     distributed under the License is distributed on an "AS IS" BASIS, |  | ||||||
|  *     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |  | ||||||
|  *     See the License for the specific language governing permissions and |  | ||||||
|  *     limitations under the License. |  | ||||||
|  */ |  | ||||||
| package com.google.gradle.osdetector |  | ||||||
| 
 |  | ||||||
| import org.gradle.testfixtures.ProjectBuilder |  | ||||||
| import org.gradle.api.Project |  | ||||||
| import org.junit.Test |  | ||||||
| import static org.junit.Assert.assertEquals |  | ||||||
| import static org.junit.Assert.assertNotNull |  | ||||||
| import static org.junit.Assert.fail |  | ||||||
| 
 |  | ||||||
| class OsDetectorPluginTest { |  | ||||||
|   @Test |  | ||||||
|   public void pluginAddsExtensionToProject() { |  | ||||||
|     Project project = ProjectBuilder.builder().build() |  | ||||||
|     project.apply plugin: 'com.google.osdetector' |  | ||||||
|     assertNotNull(project.osdetector) |  | ||||||
|     assertNotNull(project.osdetector.os) |  | ||||||
|     assertNotNull(project.osdetector.arch) |  | ||||||
|     assertEquals(project.osdetector.os + '-' + project.osdetector.arch, |  | ||||||
|         project.osdetector.classifier) |  | ||||||
|     System.err.println('classifier=' + project.osdetector.classifier) |  | ||||||
|     if (project.osdetector.os == 'linux') { |  | ||||||
|       assertNotNull(project.osdetector.release.id) |  | ||||||
|       assertNotNull(project.osdetector.release.version) |  | ||||||
|       System.err.println('release.id=' + project.osdetector.release.id) |  | ||||||
|       System.err.println('release.version=' + project.osdetector.release.version) |  | ||||||
|       System.err.println('release.isLike(debian)=' + project.osdetector.release.isLike('debian')) |  | ||||||
|       System.err.println('release.isLike(redhat)=' + project.osdetector.release.isLike('redhat')) |  | ||||||
|     } else if (project.osdetector.release) { |  | ||||||
|       fail("Should be null") |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @Test |  | ||||||
|   public void setClassifierWithLikes() { |  | ||||||
|     Project project = ProjectBuilder.builder().build() |  | ||||||
|     project.apply plugin: 'com.google.osdetector' |  | ||||||
|     project.osdetector.classifierWithLikes = ['debian', 'fedora'] |  | ||||||
|     assertNotNull(project.osdetector.os) |  | ||||||
|     assertNotNull(project.osdetector.arch) |  | ||||||
|     System.err.println('classifier=' + project.osdetector.classifier) |  | ||||||
|     try { |  | ||||||
|       project.osdetector.classifierWithLikes = ['debian'] |  | ||||||
|       fail("Should throw IllegalStateException") |  | ||||||
|     } catch (IllegalStateException expected) { |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  | @ -1,3 +0,0 @@ | ||||||
| Manifest-Version: 1.0 |  | ||||||
| Main-Class: meerkat.voting.gui.configuration.Converter |  | ||||||
| 
 |  | ||||||
|  | @ -1,214 +0,0 @@ | ||||||
| 
 |  | ||||||
| plugins { |  | ||||||
|     id "us.kirchmeier.capsule" version '1.0.2' |  | ||||||
|     id 'com.google.protobuf' version '0.8.1' |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| apply plugin: 'java' |  | ||||||
| apply plugin: 'com.google.protobuf' |  | ||||||
| apply plugin: 'eclipse' |  | ||||||
| apply plugin: 'idea' |  | ||||||
| 
 |  | ||||||
| apply plugin: 'maven-publish' |  | ||||||
| 
 |  | ||||||
| // Is this a snapshot version? |  | ||||||
| ext { isSnapshot = false } |  | ||||||
| 
 |  | ||||||
| ext { |  | ||||||
|     groupId = 'org.factcenter.meerkat' |  | ||||||
| 
 |  | ||||||
|     // Credentials for publishing repositories |  | ||||||
|     publishRepository = "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}" |  | ||||||
|     publishUser =  project.hasProperty('publishUser') ? project.property('publishUser') : "" |  | ||||||
|     publishPassword = project.hasProperty('publishPassword') ? project.property('publishPassword') : "" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| description = "Meerkat Voting Common Library" |  | ||||||
| 
 |  | ||||||
| // Your project version |  | ||||||
| version = "0.1" |  | ||||||
| 
 |  | ||||||
| version += "${isSnapshot ? '-SNAPSHOT' : ''}" |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| dependencies { |  | ||||||
| 
 |  | ||||||
|     // Meerkat common |  | ||||||
|     compile project(':meerkat-common') |  | ||||||
|     compile project(':restful-api-common') |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     // Databases |  | ||||||
|     compile 'org.xerial:sqlite-jdbc:3.7.+' |  | ||||||
| 
 |  | ||||||
|     // Depend on test resources from meerkat-common |  | ||||||
|     testCompile project(path: ':meerkat-common', configuration: 'testOutput') |  | ||||||
| 
 |  | ||||||
|     // Depend on server compilation for the non-integration tests |  | ||||||
|     testCompile project(path: ':bulletin-board-server') |  | ||||||
| 
 |  | ||||||
|     testCompile 'junit:junit:4.+' |  | ||||||
|     testCompile 'org.hamcrest:hamcrest-all:1.3' |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| test { |  | ||||||
|     exclude '**/*IntegrationTest*' |  | ||||||
| //    outputs.upToDateWhen { false } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| task integrationTest(type: Test) { |  | ||||||
|     include '**/*IntegrationTest*' |  | ||||||
| //    debug = true |  | ||||||
|     outputs.upToDateWhen { false } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /*==== You probably don't have to edit below this line =======*/ |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| // Setup test configuration that can appear as a dependency in |  | ||||||
| // other subprojects |  | ||||||
| configurations { |  | ||||||
|     testOutput.extendsFrom (testCompile) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| task testJar(type: Jar, dependsOn: testClasses) { |  | ||||||
|     classifier = 'tests' |  | ||||||
|     from sourceSets.test.output |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| artifacts { |  | ||||||
|     testOutput testJar |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| // The run task added by the application plugin |  | ||||||
| // is also of type JavaExec. |  | ||||||
| tasks.withType(JavaExec) { |  | ||||||
|     // Assign all Java system properties from |  | ||||||
|     // the command line to the JavaExec task. |  | ||||||
|     systemProperties System.properties |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| protobuf { |  | ||||||
|     // Configure the protoc executable |  | ||||||
|     protoc { |  | ||||||
|         // Download from repositories |  | ||||||
|         artifact = 'com.google.protobuf:protoc:3.+' |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| idea { |  | ||||||
|     module { |  | ||||||
|         project.sourceSets.each { sourceSet -> |  | ||||||
| 
 |  | ||||||
|             def srcDir = "${protobuf.generatedFilesBaseDir}/$sourceSet.name/java" |  | ||||||
| 
 |  | ||||||
|             // add protobuf generated sources to generated source dir. |  | ||||||
|             if ("test".equals(sourceSet.name)) { |  | ||||||
|                 testSourceDirs += file(srcDir) |  | ||||||
|             } else { |  | ||||||
|                 sourceDirs += file(srcDir) |  | ||||||
|             } |  | ||||||
|             generatedSourceDirs += file(srcDir) |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // Don't exclude build directory |  | ||||||
|         excludeDirs -= file(buildDir) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /*=================================== |  | ||||||
|  *        "Fat" Build targets |  | ||||||
|  *===================================*/ |  | ||||||
| 
 |  | ||||||
| if (project.hasProperty('mainClassName') && (mainClassName != null)) { |  | ||||||
| 
 |  | ||||||
|     task mavenCapsule(type: MavenCapsule) { |  | ||||||
|         description = "Generate a capsule jar that automatically downloads and caches dependencies when run." |  | ||||||
|         applicationClass mainClassName |  | ||||||
|         destinationDir = buildDir |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     task fatCapsule(type: FatCapsule) { |  | ||||||
|         description = "Generate a single capsule jar containing everything. Use -Pfatmain=... to override main class" |  | ||||||
| 
 |  | ||||||
|         destinationDir = buildDir |  | ||||||
| 
 |  | ||||||
|         def fatMain = hasProperty('fatmain') ? fatmain : mainClassName |  | ||||||
| 
 |  | ||||||
|         applicationClass fatMain |  | ||||||
| 
 |  | ||||||
|         def testJar = hasProperty('test') |  | ||||||
| 
 |  | ||||||
|         if (hasProperty('fatmain')) { |  | ||||||
|             appendix = "fat-${fatMain}" |  | ||||||
|         } else { |  | ||||||
|             appendix = "fat" |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (testJar) { |  | ||||||
|             from sourceSets.test.output |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /*=================================== |  | ||||||
|  *        Repositories |  | ||||||
|  *===================================*/ |  | ||||||
| 
 |  | ||||||
| repositories { |  | ||||||
|     // Use local repo if possible |  | ||||||
|     mavenLocal(); |  | ||||||
| 
 |  | ||||||
|     // Use 'maven central' for other dependencies. |  | ||||||
|     mavenCentral() |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| task "info"  { |  | ||||||
|     doLast { |  | ||||||
|         println "Project: ${project.name}" |  | ||||||
|         println "Description: ${project.description}" |  | ||||||
|         println "--------------------------" |  | ||||||
|         println "GroupId: $groupId" |  | ||||||
|         println "Version: $version (${isSnapshot ? 'snapshot' : 'release'})" |  | ||||||
|         println "" |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| info.description 'Print some information about project parameters' |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| /*=================================== |  | ||||||
|  *        Publishing |  | ||||||
|  *===================================*/ |  | ||||||
| 
 |  | ||||||
| publishing { |  | ||||||
|     publications { |  | ||||||
|         mavenJava(MavenPublication) { |  | ||||||
|             groupId project.groupId |  | ||||||
|             pom.withXml { |  | ||||||
|                 asNode().appendNode('description', project.description) |  | ||||||
|             } |  | ||||||
|             from project.components.java |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     repositories { |  | ||||||
|         maven { |  | ||||||
|             url publishRepository |  | ||||||
|             credentials { username |  | ||||||
|                 password |  | ||||||
| 
 |  | ||||||
|                 username publishUser |  | ||||||
|                 password publishPassword |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|  | @ -1 +0,0 @@ | ||||||
| ../gradlew |  | ||||||
|  | @ -1,23 +0,0 @@ | ||||||
| package meerkat.bulletinboard; |  | ||||||
| 
 |  | ||||||
| import meerkat.protobuf.BulletinBoardApi.BatchChunk; |  | ||||||
| import meerkat.bulletinboard.AsyncBulletinBoardClient.BatchIdentifier; |  | ||||||
| 
 |  | ||||||
| import java.util.List; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 17-Jan-16. |  | ||||||
|  * Used to store the complete data required for sending a batch data list inside a single object |  | ||||||
|  */ |  | ||||||
| public class BatchDataContainer { |  | ||||||
| 
 |  | ||||||
|     public final MultiServerBatchIdentifier batchId; |  | ||||||
|     public final List<BatchChunk> batchChunkList; |  | ||||||
|     public final int startPosition; |  | ||||||
| 
 |  | ||||||
|     public BatchDataContainer(MultiServerBatchIdentifier batchId, List<BatchChunk> batchChunkList, int startPosition) { |  | ||||||
|         this.batchId = batchId; |  | ||||||
|         this.batchChunkList = batchChunkList; |  | ||||||
|         this.startPosition = startPosition; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,38 +0,0 @@ | ||||||
| package meerkat.bulletinboard; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 09-Dec-15. |  | ||||||
|  * |  | ||||||
|  * This class handles bulletin client work. |  | ||||||
|  * It is meant to be used in a multi-threaded environment. |  | ||||||
|  */ |  | ||||||
| public abstract class BulletinClientWorker<IN> { |  | ||||||
| 
 |  | ||||||
|     protected final IN payload;    // Payload of the job
 |  | ||||||
| 
 |  | ||||||
|     private int maxRetry;   // Number of retries for this job; set to -1 for infinite retries
 |  | ||||||
| 
 |  | ||||||
|     public BulletinClientWorker(IN payload, int maxRetry) { |  | ||||||
|         this.payload = payload; |  | ||||||
|         this.maxRetry = maxRetry; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public IN getPayload() { |  | ||||||
|         return payload; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public int getMaxRetry() { |  | ||||||
|         return maxRetry; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public void decMaxRetry(){ |  | ||||||
|         if (maxRetry > 0) { |  | ||||||
|             maxRetry--; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public boolean isRetry(){ |  | ||||||
|         return (maxRetry != 0); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,490 +0,0 @@ | ||||||
| package meerkat.bulletinboard; |  | ||||||
| 
 |  | ||||||
| import com.google.common.util.concurrent.FutureCallback; |  | ||||||
| import com.google.protobuf.Timestamp; |  | ||||||
| import meerkat.comm.CommunicationException; |  | ||||||
| import meerkat.protobuf.BulletinBoardApi.*; |  | ||||||
| import meerkat.protobuf.Crypto.Signature; |  | ||||||
| import meerkat.protobuf.Voting.*; |  | ||||||
| 
 |  | ||||||
| import java.util.List; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 03-Mar-16. |  | ||||||
|  * This is a full-fledged implementation of a Bulletin Board Client |  | ||||||
|  * It provides asynchronous access to several remote servers, as well as a local cache |  | ||||||
|  * Read operations are performed on the local server |  | ||||||
|  * Batch reads are performed on the local server and, if they fail, also on the remote servers |  | ||||||
|  * Write operations are performed on the local server |  | ||||||
|  * A Synchronizer is employed in order to keep the remote server up to date |  | ||||||
|  * After any read is carried out, a subscription is made for the specific query to make sure the local DB will be updated |  | ||||||
|  * The database also employs a synchronizer which makes sure local data is sent to the remote servers |  | ||||||
|  */ |  | ||||||
| public class CachedBulletinBoardClient implements SubscriptionBulletinBoardClient { |  | ||||||
| 
 |  | ||||||
|     private final AsyncBulletinBoardClient localClient; |  | ||||||
|     private final AsyncBulletinBoardClient remoteClient; |  | ||||||
|     private final AsyncBulletinBoardClient queueClient; |  | ||||||
|     private final BulletinBoardSubscriber subscriber; |  | ||||||
|     private final BulletinBoardSynchronizer synchronizer; |  | ||||||
| 
 |  | ||||||
|     private Thread syncThread; |  | ||||||
| 
 |  | ||||||
|     private final static int DEFAULT_WAIT_CAP = 3000; |  | ||||||
|     private final static int DEFAULT_SLEEP_INTERVAL = 3000; |  | ||||||
| 
 |  | ||||||
|     private class SubscriptionStoreCallback implements FutureCallback<List<BulletinBoardMessage>>  { |  | ||||||
| 
 |  | ||||||
|         private final FutureCallback<List<BulletinBoardMessage>> callback; |  | ||||||
| 
 |  | ||||||
|         public SubscriptionStoreCallback(){ |  | ||||||
|             callback = null; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public SubscriptionStoreCallback(FutureCallback<List<BulletinBoardMessage>> callback){ |  | ||||||
|             this.callback = callback; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onSuccess(List<BulletinBoardMessage> result) { |  | ||||||
|             for (BulletinBoardMessage msg : result) { |  | ||||||
|                 try { |  | ||||||
| 
 |  | ||||||
|                     if (msg.getMsg().getDataTypeCase() == UnsignedBulletinBoardMessage.DataTypeCase.MSGID) { |  | ||||||
| 
 |  | ||||||
|                         // This is a batch message: need to upload batch data as well as the message itself
 |  | ||||||
|                         BulletinBoardMessage completeMessage = localClient.readBatchData(msg); |  | ||||||
| 
 |  | ||||||
|                         localClient.postMessage(completeMessage); |  | ||||||
| 
 |  | ||||||
|                     } else { |  | ||||||
| 
 |  | ||||||
|                         // This is a regular message: post it
 |  | ||||||
|                         localClient.postMessage(msg); |  | ||||||
| 
 |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                 } catch (CommunicationException ignored) { |  | ||||||
|                     // TODO: log
 |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onFailure(Throwable t) { |  | ||||||
|             if (callback != null) { |  | ||||||
|                 callback.onFailure(t);  // This is some hard error that cannot be dealt with
 |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Creates a Cached Client |  | ||||||
|      * Assumes all parameters are initialized |  | ||||||
|      * @param localClient is a Client for the local instance |  | ||||||
|      * @param remoteClient is a Client for the remote instance(s); Should have endless retries for post operations |  | ||||||
|      * @param subscriber is a subscription service to the remote instance(s) |  | ||||||
|      * @param queueClient is a client for a local deletable server to be used as a queue for not-yet-uploaded messages |  | ||||||
|      */ |  | ||||||
|     public CachedBulletinBoardClient(AsyncBulletinBoardClient localClient, |  | ||||||
|                                      AsyncBulletinBoardClient remoteClient, |  | ||||||
|                                      BulletinBoardSubscriber subscriber, |  | ||||||
|                                      DeletableSubscriptionBulletinBoardClient queueClient, |  | ||||||
|                                      int sleepInterval, |  | ||||||
|                                      int waitCap) { |  | ||||||
| 
 |  | ||||||
|         this.localClient = localClient; |  | ||||||
|         this.remoteClient = remoteClient; |  | ||||||
|         this.subscriber = subscriber; |  | ||||||
|         this.queueClient = queueClient; |  | ||||||
| 
 |  | ||||||
|         this.synchronizer = new SimpleBulletinBoardSynchronizer(sleepInterval,waitCap); |  | ||||||
|         synchronizer.init(queueClient, remoteClient); |  | ||||||
|         syncThread = new Thread(synchronizer); |  | ||||||
|         syncThread.start(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Creates a Cached Client |  | ||||||
|      * Used default values foe the time caps |  | ||||||
|      * */ |  | ||||||
|     public CachedBulletinBoardClient(AsyncBulletinBoardClient localClient, |  | ||||||
|                                      AsyncBulletinBoardClient remoteClient, |  | ||||||
|                                      BulletinBoardSubscriber subscriber, |  | ||||||
|                                      DeletableSubscriptionBulletinBoardClient queue) { |  | ||||||
| 
 |  | ||||||
|         this(localClient, remoteClient, subscriber, queue, DEFAULT_SLEEP_INTERVAL, DEFAULT_WAIT_CAP); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public MessageID postMessage(final BulletinBoardMessage msg, final FutureCallback<Boolean> callback) { |  | ||||||
| 
 |  | ||||||
|         return localClient.postMessage(msg, new FutureCallback<Boolean>() { |  | ||||||
|             @Override |  | ||||||
|             public void onSuccess(Boolean result) { |  | ||||||
|                 remoteClient.postMessage(msg, callback); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             @Override |  | ||||||
|             public void onFailure(Throwable t) { |  | ||||||
|                 if (callback != null) |  | ||||||
|                     callback.onFailure(t); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public MessageID postAsBatch(final BulletinBoardMessage msg, final int chunkSize, final FutureCallback<Boolean> callback) { |  | ||||||
| 
 |  | ||||||
|         return localClient.postAsBatch(msg, chunkSize, new FutureCallback<Boolean>() { |  | ||||||
|             @Override |  | ||||||
|             public void onSuccess(Boolean result) { |  | ||||||
|                 remoteClient.postAsBatch(msg, chunkSize, callback); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             @Override |  | ||||||
|             public void onFailure(Throwable t) { |  | ||||||
|                 if (callback != null) |  | ||||||
|                     callback.onFailure(t); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void beginBatch(final Iterable<String> tags, final FutureCallback<BatchIdentifier> callback) { |  | ||||||
| 
 |  | ||||||
|         localClient.beginBatch(tags, new FutureCallback<BatchIdentifier>() { |  | ||||||
| 
 |  | ||||||
|             private BatchIdentifier localIdentifier; |  | ||||||
| 
 |  | ||||||
|             @Override |  | ||||||
|             public void onSuccess(BatchIdentifier result) { |  | ||||||
| 
 |  | ||||||
|                 localIdentifier = result; |  | ||||||
| 
 |  | ||||||
|                 remoteClient.beginBatch(tags, new FutureCallback<BatchIdentifier>() { |  | ||||||
|                     @Override |  | ||||||
|                     public void onSuccess(BatchIdentifier result) { |  | ||||||
|                         if (callback != null) |  | ||||||
|                             callback.onSuccess(new CachedClientBatchIdentifier(localIdentifier, result)); |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     @Override |  | ||||||
|                     public void onFailure(Throwable t) { |  | ||||||
|                         if (callback != null) |  | ||||||
|                             callback.onFailure(t); |  | ||||||
|                     } |  | ||||||
|                 }); |  | ||||||
| 
 |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             @Override |  | ||||||
|             public void onFailure(Throwable t) { |  | ||||||
|                 if (callback != null) |  | ||||||
|                     callback.onFailure(t); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void postBatchData(final BatchIdentifier batchIdentifier, final List<BatchChunk> batchChunkList, |  | ||||||
|                               final int startPosition, final FutureCallback<Boolean> callback) throws IllegalArgumentException{ |  | ||||||
| 
 |  | ||||||
|         if (!(batchIdentifier instanceof CachedClientBatchIdentifier)){ |  | ||||||
|             throw new IllegalArgumentException("Error: batch identifier supplied was not created by this class."); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         final CachedClientBatchIdentifier identifier = (CachedClientBatchIdentifier) batchIdentifier; |  | ||||||
| 
 |  | ||||||
|         localClient.postBatchData(identifier.getLocalIdentifier(), batchChunkList, startPosition, new FutureCallback<Boolean>() { |  | ||||||
|             @Override |  | ||||||
|             public void onSuccess(Boolean result) { |  | ||||||
|                 remoteClient.postBatchData(identifier.getRemoteIdentifier(), batchChunkList, startPosition, callback); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             @Override |  | ||||||
|             public void onFailure(Throwable t) { |  | ||||||
|                 if (callback != null) |  | ||||||
|                     callback.onFailure(t); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void postBatchData(final BatchIdentifier batchIdentifier, final List<BatchChunk> batchChunkList, final FutureCallback<Boolean> callback) |  | ||||||
|             throws IllegalArgumentException{ |  | ||||||
| 
 |  | ||||||
|         if (!(batchIdentifier instanceof CachedClientBatchIdentifier)){ |  | ||||||
|             throw new IllegalArgumentException("Error: batch identifier supplied was not created by this class."); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         final CachedClientBatchIdentifier identifier = (CachedClientBatchIdentifier) batchIdentifier; |  | ||||||
| 
 |  | ||||||
|         localClient.postBatchData(identifier.getLocalIdentifier(), batchChunkList, new FutureCallback<Boolean>() { |  | ||||||
|             @Override |  | ||||||
|             public void onSuccess(Boolean result) { |  | ||||||
|                 remoteClient.postBatchData(identifier.getRemoteIdentifier(), batchChunkList, callback); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             @Override |  | ||||||
|             public void onFailure(Throwable t) { |  | ||||||
|                 if (callback != null) |  | ||||||
|                     callback.onFailure(t); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void closeBatch(final BatchIdentifier batchIdentifier, final Timestamp timestamp, final Iterable<Signature> signatures, |  | ||||||
|                            final FutureCallback<Boolean> callback) { |  | ||||||
| 
 |  | ||||||
|         if (!(batchIdentifier instanceof CachedClientBatchIdentifier)){ |  | ||||||
|             throw new IllegalArgumentException("Error: batch identifier supplied was not created by this class."); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         final CachedClientBatchIdentifier identifier = (CachedClientBatchIdentifier) batchIdentifier; |  | ||||||
| 
 |  | ||||||
|         localClient.closeBatch(identifier.getLocalIdentifier(), timestamp, signatures, new FutureCallback<Boolean>() { |  | ||||||
|             @Override |  | ||||||
|             public void onSuccess(Boolean result) { |  | ||||||
| 
 |  | ||||||
|                 remoteClient.closeBatch(identifier.getRemoteIdentifier(), timestamp, signatures, callback); |  | ||||||
| 
 |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             @Override |  | ||||||
|             public void onFailure(Throwable t) { |  | ||||||
|                 if (callback != null) |  | ||||||
|                     callback.onFailure(t); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void getRedundancy(MessageID id, FutureCallback<Float> callback) { |  | ||||||
| 
 |  | ||||||
|         remoteClient.getRedundancy(id, callback); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void readMessages(MessageFilterList filterList, final FutureCallback<List<BulletinBoardMessage>> callback) { |  | ||||||
| 
 |  | ||||||
|         localClient.readMessages(filterList, callback); |  | ||||||
| 
 |  | ||||||
|         subscriber.subscribe(filterList, new SubscriptionStoreCallback(callback)); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void readMessage(final MessageID msgID, final FutureCallback<BulletinBoardMessage> callback) { |  | ||||||
| 
 |  | ||||||
|         localClient.readMessage(msgID, new FutureCallback<BulletinBoardMessage>() { |  | ||||||
| 
 |  | ||||||
|             @Override |  | ||||||
|             public void onSuccess(BulletinBoardMessage result) { |  | ||||||
|                 if (callback != null) |  | ||||||
|                     callback.onSuccess(result);     // Read from local client was successful
 |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             @Override |  | ||||||
|             public void onFailure(Throwable t) { |  | ||||||
| 
 |  | ||||||
|                 // Read from local unsuccessful: try to read from remote
 |  | ||||||
| 
 |  | ||||||
|                 remoteClient.readMessage(msgID, new FutureCallback<BulletinBoardMessage>() { |  | ||||||
| 
 |  | ||||||
|                     @Override |  | ||||||
|                     public void onSuccess(BulletinBoardMessage result) { |  | ||||||
| 
 |  | ||||||
|                         // Read from remote was successful: store in local and return result
 |  | ||||||
| 
 |  | ||||||
|                         localClient.postMessage(result, null); |  | ||||||
| 
 |  | ||||||
|                         if (callback != null) |  | ||||||
|                             callback.onSuccess(result); |  | ||||||
| 
 |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     @Override |  | ||||||
|                     public void onFailure(Throwable t) { |  | ||||||
| 
 |  | ||||||
|                         // Read from remote was unsuccessful: report error
 |  | ||||||
|                         if (callback != null) |  | ||||||
|                             callback.onFailure(t); |  | ||||||
| 
 |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                 }); |  | ||||||
| 
 |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void readBatchData(final BulletinBoardMessage stub, final FutureCallback<BulletinBoardMessage> callback) throws IllegalArgumentException { |  | ||||||
| 
 |  | ||||||
|         localClient.readBatchData(stub, new FutureCallback<BulletinBoardMessage>() { |  | ||||||
| 
 |  | ||||||
|             @Override |  | ||||||
|             public void onSuccess(BulletinBoardMessage result) { |  | ||||||
|                 if (callback != null) |  | ||||||
|                     callback.onSuccess(result);     // Read from local client was successful
 |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             @Override |  | ||||||
|             public void onFailure(Throwable t) { |  | ||||||
| 
 |  | ||||||
|                 // Read from local unsuccessful: try to read from remote
 |  | ||||||
| 
 |  | ||||||
|                 remoteClient.readBatchData(stub, new FutureCallback<BulletinBoardMessage>() { |  | ||||||
| 
 |  | ||||||
|                     @Override |  | ||||||
|                     public void onSuccess(BulletinBoardMessage result) { |  | ||||||
| 
 |  | ||||||
|                         // Read from remote was successful: store in local and return result
 |  | ||||||
| 
 |  | ||||||
|                         localClient.postMessage(result, null); |  | ||||||
| 
 |  | ||||||
|                         if (callback != null) |  | ||||||
|                             callback.onSuccess(result); |  | ||||||
| 
 |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     @Override |  | ||||||
|                     public void onFailure(Throwable t) { |  | ||||||
| 
 |  | ||||||
|                         // Read from remote was unsuccessful: report error
 |  | ||||||
|                         if (callback != null) |  | ||||||
|                             callback.onFailure(t); |  | ||||||
| 
 |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                 }); |  | ||||||
| 
 |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void querySync(SyncQuery syncQuery, FutureCallback<SyncQueryResponse> callback) { |  | ||||||
| 
 |  | ||||||
|         localClient.querySync(syncQuery, callback); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     /** |  | ||||||
|      * This is a stub method |  | ||||||
|      * All resources are assumed to be initialized |  | ||||||
|      */ |  | ||||||
|     public void init(BulletinBoardClientParams clientParams) {} |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException { |  | ||||||
|         return localClient.postMessage(msg); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public MessageID postAsBatch(BulletinBoardMessage msg, int chunkSize) throws CommunicationException { |  | ||||||
|         MessageID result = localClient.postAsBatch(msg, chunkSize); |  | ||||||
|         remoteClient.postAsBatch(msg, chunkSize); |  | ||||||
|         return  result; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public float getRedundancy(MessageID id) throws CommunicationException { |  | ||||||
|         return remoteClient.getRedundancy(id); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public List<BulletinBoardMessage> readMessages(MessageFilterList filterList) throws CommunicationException { |  | ||||||
|         subscriber.subscribe(filterList, new SubscriptionStoreCallback()); |  | ||||||
|         return localClient.readMessages(filterList); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public BulletinBoardMessage readMessage(MessageID msgID) throws CommunicationException { |  | ||||||
| 
 |  | ||||||
|         BulletinBoardMessage result = null; |  | ||||||
|         try { |  | ||||||
|             result = localClient.readMessage(msgID); |  | ||||||
|         } catch (CommunicationException e) { |  | ||||||
|             //TODO: log
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (result == null){ |  | ||||||
|             result = remoteClient.readMessage(msgID); |  | ||||||
| 
 |  | ||||||
|             if (result != null){ |  | ||||||
|                 localClient.postMessage(result); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return result; |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public BulletinBoardMessage readBatchData(BulletinBoardMessage stub) throws CommunicationException, IllegalArgumentException { |  | ||||||
| 
 |  | ||||||
|         BulletinBoardMessage result = null; |  | ||||||
|         try { |  | ||||||
|             result = localClient.readBatchData(stub); |  | ||||||
|         } catch (CommunicationException e) { |  | ||||||
|             //TODO: log
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (result == null){ |  | ||||||
|             result = remoteClient.readBatchData(stub); |  | ||||||
| 
 |  | ||||||
|             if (result != null){ |  | ||||||
|                 localClient.postMessage(result); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return result; |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) throws CommunicationException { |  | ||||||
|         return localClient.generateSyncQuery(generateSyncQueryParams); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void close() { |  | ||||||
|         localClient.close(); |  | ||||||
|         remoteClient.close(); |  | ||||||
|         synchronizer.stop(); |  | ||||||
|         try { |  | ||||||
|             syncThread.join(); |  | ||||||
|         } catch (InterruptedException e) { |  | ||||||
|             //TODO: log interruption
 |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void subscribe(MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback) { |  | ||||||
|         subscriber.subscribe(filterList, new SubscriptionStoreCallback(callback)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void subscribe(MessageFilterList filterList, long startEntry, FutureCallback<List<BulletinBoardMessage>> callback) { |  | ||||||
|         subscriber.subscribe(filterList, startEntry, new SubscriptionStoreCallback(callback)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,29 +0,0 @@ | ||||||
| package meerkat.bulletinboard; |  | ||||||
| 
 |  | ||||||
| import meerkat.bulletinboard.AsyncBulletinBoardClient.BatchIdentifier; |  | ||||||
| 
 |  | ||||||
| import java.util.Arrays; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 17-Jun-16. |  | ||||||
|  */ |  | ||||||
| public final class CachedClientBatchIdentifier implements BatchIdentifier { |  | ||||||
| 
 |  | ||||||
|     // Per-server identifiers
 |  | ||||||
|     private final BatchIdentifier localIdentifier; |  | ||||||
|     private final BatchIdentifier remoteIdentifier; |  | ||||||
| 
 |  | ||||||
|     public CachedClientBatchIdentifier(BatchIdentifier localIdentifier, BatchIdentifier remoteIdentifier) { |  | ||||||
|         this.localIdentifier = localIdentifier; |  | ||||||
|         this.remoteIdentifier = remoteIdentifier; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public BatchIdentifier getLocalIdentifier() { |  | ||||||
|         return localIdentifier; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public BatchIdentifier getRemoteIdentifier() { |  | ||||||
|         return remoteIdentifier; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,689 +0,0 @@ | ||||||
| package meerkat.bulletinboard; |  | ||||||
| 
 |  | ||||||
| import com.google.common.util.concurrent.*; |  | ||||||
| import com.google.protobuf.Int64Value; |  | ||||||
| import com.google.protobuf.Timestamp; |  | ||||||
| import meerkat.comm.CommunicationException; |  | ||||||
| import meerkat.comm.MessageInputStream; |  | ||||||
| import meerkat.comm.MessageInputStream.MessageInputStreamFactory; |  | ||||||
| import meerkat.comm.MessageOutputStream; |  | ||||||
| import meerkat.crypto.concrete.SHA256Digest; |  | ||||||
| import meerkat.protobuf.BulletinBoardApi.*; |  | ||||||
| import meerkat.protobuf.Crypto.Signature; |  | ||||||
| import meerkat.protobuf.Voting.*; |  | ||||||
| import meerkat.util.BulletinBoardUtils; |  | ||||||
| 
 |  | ||||||
| import javax.ws.rs.NotFoundException; |  | ||||||
| import java.io.ByteArrayInputStream; |  | ||||||
| import java.io.ByteArrayOutputStream; |  | ||||||
| import java.util.List; |  | ||||||
| import java.util.concurrent.Callable; |  | ||||||
| import java.util.concurrent.Executors; |  | ||||||
| import java.util.concurrent.TimeUnit; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 15-Mar-16. |  | ||||||
|  * This client wraps a BulletinBoardServer in an asynchronous client. |  | ||||||
|  * It is meant to be used as a local cache handler and for testing purposes. |  | ||||||
|  * This means the access to the server is direct (via method calls) instead of through a TCP connection. |  | ||||||
|  * The client implements both synchronous and asynchronous method calls, but calls to the server itself are performed synchronously. |  | ||||||
|  */ |  | ||||||
| public class LocalBulletinBoardClient implements DeletableSubscriptionBulletinBoardClient { |  | ||||||
| 
 |  | ||||||
|     private final DeletableBulletinBoardServer server; |  | ||||||
|     private final ListeningScheduledExecutorService executorService; |  | ||||||
|     private final BulletinBoardDigest digest; |  | ||||||
|     private final long subsrciptionDelay; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Initializes an instance of the client |  | ||||||
|      * @param server an initialized Bulletin Board Server instance which will perform the actual processing of the requests |  | ||||||
|      * @param threadNum is the number of concurrent threads to allocate for the client |  | ||||||
|      */ |  | ||||||
|     public LocalBulletinBoardClient(DeletableBulletinBoardServer server, int threadNum, int subscriptionDelay) { |  | ||||||
|         this.server = server; |  | ||||||
|         this.executorService = MoreExecutors.listeningDecorator(Executors.newScheduledThreadPool(threadNum)); |  | ||||||
|         this.digest = new GenericBulletinBoardDigest(new SHA256Digest()); |  | ||||||
|         this.subsrciptionDelay = subscriptionDelay; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private class MessagePoster implements Callable<Boolean> { |  | ||||||
| 
 |  | ||||||
|         private final BulletinBoardMessage msg; |  | ||||||
| 
 |  | ||||||
|         public MessagePoster(BulletinBoardMessage msg) { |  | ||||||
|             this.msg = msg; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public Boolean call() throws CommunicationException { |  | ||||||
|             return server.postMessage(msg).getValue(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public MessageID postMessage(BulletinBoardMessage msg, FutureCallback<Boolean> callback) { |  | ||||||
| 
 |  | ||||||
|         Futures.addCallback(executorService.submit(new MessagePoster(msg)), callback); |  | ||||||
| 
 |  | ||||||
|         digest.update(msg.getMsg()); |  | ||||||
|         return digest.digestAsMessageID(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private class CompleteBatchPoster implements Callable<Boolean> { |  | ||||||
| 
 |  | ||||||
|         private final BulletinBoardMessage msg; |  | ||||||
|         private final int chunkSize; |  | ||||||
| 
 |  | ||||||
|         public CompleteBatchPoster(BulletinBoardMessage msg, int chunkSize) { |  | ||||||
|             this.msg = msg; |  | ||||||
|             this.chunkSize = chunkSize; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public Boolean call() throws CommunicationException { |  | ||||||
| 
 |  | ||||||
|             BeginBatchMessage beginBatchMessage = BeginBatchMessage.newBuilder() |  | ||||||
|                     .addAllTag(msg.getMsg().getTagList()) |  | ||||||
|                     .build(); |  | ||||||
| 
 |  | ||||||
|             Int64Value batchId = server.beginBatch(beginBatchMessage); |  | ||||||
| 
 |  | ||||||
|             BatchMessage.Builder builder = BatchMessage.newBuilder() |  | ||||||
|                     .setBatchId(batchId.getValue()); |  | ||||||
| 
 |  | ||||||
|             List<BatchChunk> batchChunkList = BulletinBoardUtils.breakToBatch(msg, chunkSize); |  | ||||||
| 
 |  | ||||||
|             int i=0; |  | ||||||
|             for (BatchChunk chunk : batchChunkList){ |  | ||||||
| 
 |  | ||||||
|                 server.postBatchMessage(builder.setSerialNum(i).setData(chunk).build()); |  | ||||||
| 
 |  | ||||||
|                 i++; |  | ||||||
| 
 |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             CloseBatchMessage closeBatchMessage = BulletinBoardUtils.generateCloseBatchMessage(batchId, batchChunkList.size(), msg); |  | ||||||
| 
 |  | ||||||
|             return server.closeBatch(closeBatchMessage).getValue(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public MessageID postAsBatch(BulletinBoardMessage msg, int chunkSize, FutureCallback<Boolean> callback) { |  | ||||||
| 
 |  | ||||||
|         Futures.addCallback(executorService.submit(new CompleteBatchPoster(msg, chunkSize)), callback); |  | ||||||
| 
 |  | ||||||
|         digest.reset(); |  | ||||||
|         digest.update(msg); |  | ||||||
|         return digest.digestAsMessageID(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private class BatchBeginner implements Callable<SingleServerBatchIdentifier> { |  | ||||||
| 
 |  | ||||||
|         private final BeginBatchMessage msg; |  | ||||||
| 
 |  | ||||||
|         public BatchBeginner(BeginBatchMessage msg) { |  | ||||||
|             this.msg = msg; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public SingleServerBatchIdentifier call() throws Exception { |  | ||||||
|             return new SingleServerBatchIdentifier(server.beginBatch(msg)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void beginBatch(Iterable<String> tags, FutureCallback<BatchIdentifier> callback) { |  | ||||||
| 
 |  | ||||||
|         BeginBatchMessage beginBatchMessage = BeginBatchMessage.newBuilder() |  | ||||||
|                 .addAllTag(tags) |  | ||||||
|                 .build(); |  | ||||||
| 
 |  | ||||||
|         Futures.addCallback(executorService.submit(new BatchBeginner(beginBatchMessage)), callback); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private class BatchDataPoster implements Callable<Boolean> { |  | ||||||
| 
 |  | ||||||
|         private final SingleServerBatchIdentifier batchId; |  | ||||||
|         private final List<BatchChunk> batchChunkList; |  | ||||||
|         private final int startPosition; |  | ||||||
| 
 |  | ||||||
|         public BatchDataPoster(SingleServerBatchIdentifier batchId, List<BatchChunk> batchChunkList, int startPosition) { |  | ||||||
|             this.batchId = batchId; |  | ||||||
|             this.batchChunkList = batchChunkList; |  | ||||||
|             this.startPosition = startPosition; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public Boolean call() throws Exception { |  | ||||||
| 
 |  | ||||||
|             BatchMessage.Builder msgBuilder = BatchMessage.newBuilder() |  | ||||||
|                     .setBatchId(batchId.getBatchId().getValue()); |  | ||||||
| 
 |  | ||||||
|             int i = startPosition; |  | ||||||
|             for (BatchChunk data : batchChunkList){ |  | ||||||
| 
 |  | ||||||
|                 msgBuilder.setSerialNum(i) |  | ||||||
|                         .setData(data); |  | ||||||
| 
 |  | ||||||
|                 if (!server.postBatchMessage(msgBuilder.build()).getValue()) |  | ||||||
|                     return false; |  | ||||||
| 
 |  | ||||||
|                 i++; |  | ||||||
| 
 |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             batchId.setLength(i); |  | ||||||
| 
 |  | ||||||
|             return true; |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void postBatchData(BatchIdentifier batchId, List<BatchChunk> batchChunkList, int startPosition, FutureCallback<Boolean> callback) |  | ||||||
|         throws IllegalArgumentException{ |  | ||||||
| 
 |  | ||||||
|         // Cast identifier to usable form
 |  | ||||||
| 
 |  | ||||||
|         if (!(batchId instanceof SingleServerBatchIdentifier)){ |  | ||||||
|             throw new IllegalArgumentException("Error: batch identifier supplied was not created by this class."); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         SingleServerBatchIdentifier identifier = (SingleServerBatchIdentifier) batchId; |  | ||||||
| 
 |  | ||||||
|         // Add worker
 |  | ||||||
| 
 |  | ||||||
|         Futures.addCallback(executorService.submit(new BatchDataPoster(identifier, batchChunkList, startPosition)), callback); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void postBatchData(BatchIdentifier batchId, List<BatchChunk> batchChunkList, FutureCallback<Boolean> callback) throws IllegalArgumentException{ |  | ||||||
|         postBatchData(batchId, batchChunkList, 0, callback); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     private class BatchCloser implements Callable<Boolean> { |  | ||||||
| 
 |  | ||||||
|         private final CloseBatchMessage msg; |  | ||||||
| 
 |  | ||||||
|         public BatchCloser(CloseBatchMessage msg) { |  | ||||||
|             this.msg = msg; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public Boolean call() throws Exception { |  | ||||||
|             return server.closeBatch(msg).getValue(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void closeBatch(BatchIdentifier batchId, Timestamp timestamp, Iterable<Signature> signatures, FutureCallback<Boolean> callback) { |  | ||||||
| 
 |  | ||||||
|         // Cast identifier to usable form
 |  | ||||||
| 
 |  | ||||||
|         if (!(batchId instanceof SingleServerBatchIdentifier)){ |  | ||||||
|             throw new IllegalArgumentException("Error: batch identifier supplied was not created by this class."); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         SingleServerBatchIdentifier identifier = (SingleServerBatchIdentifier) batchId; |  | ||||||
| 
 |  | ||||||
|         // Add worker
 |  | ||||||
| 
 |  | ||||||
|         CloseBatchMessage closeBatchMessage = CloseBatchMessage.newBuilder() |  | ||||||
|                 .setBatchId(identifier.getBatchId().getValue()) |  | ||||||
|                 .setBatchLength(identifier.getLength()) |  | ||||||
|                 .setTimestamp(timestamp) |  | ||||||
|                 .addAllSig(signatures) |  | ||||||
|                 .build(); |  | ||||||
| 
 |  | ||||||
|         Futures.addCallback(executorService.submit(new BatchCloser(closeBatchMessage)), callback); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private class RedundancyGetter implements Callable<Float> { |  | ||||||
| 
 |  | ||||||
|         private final MessageID msgId; |  | ||||||
| 
 |  | ||||||
|         public RedundancyGetter(MessageID msgId) { |  | ||||||
|             this.msgId = msgId; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public Float call() throws Exception { |  | ||||||
| 
 |  | ||||||
|             MessageFilterList filterList = MessageFilterList.newBuilder() |  | ||||||
|                     .addFilter(MessageFilter.newBuilder() |  | ||||||
|                             .setType(FilterType.MSG_ID) |  | ||||||
|                             .setId(msgId.getID()) |  | ||||||
|                             .build()) |  | ||||||
|                     .build(); |  | ||||||
| 
 |  | ||||||
|             ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream(); |  | ||||||
|             MessageOutputStream<BulletinBoardMessage> outputStream = new MessageOutputStream<>(byteOutputStream); |  | ||||||
|             server.readMessages(filterList,outputStream); |  | ||||||
| 
 |  | ||||||
|             MessageInputStream<BulletinBoardMessage> inputStream = |  | ||||||
|                     MessageInputStreamFactory.createMessageInputStream( |  | ||||||
|                             new ByteArrayInputStream(byteOutputStream.toByteArray()), |  | ||||||
|                             BulletinBoardMessage.class); |  | ||||||
| 
 |  | ||||||
|             if (inputStream.isAvailable()) |  | ||||||
|                 return 1.0f; |  | ||||||
|             else |  | ||||||
|                 return 0.0f; |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void getRedundancy(MessageID id, FutureCallback<Float> callback) { |  | ||||||
|         Futures.addCallback(executorService.submit(new RedundancyGetter(id)), callback); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private class MessageReader implements Callable<List<BulletinBoardMessage>> { |  | ||||||
| 
 |  | ||||||
|         private final MessageFilterList filterList; |  | ||||||
| 
 |  | ||||||
|         public MessageReader(MessageFilterList filterList) { |  | ||||||
|             this.filterList = filterList; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public List<BulletinBoardMessage> call() throws Exception { |  | ||||||
| 
 |  | ||||||
|             ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream(); |  | ||||||
|             MessageOutputStream<BulletinBoardMessage> outputStream = new MessageOutputStream<>(byteOutputStream); |  | ||||||
|             server.readMessages(filterList, outputStream); |  | ||||||
| 
 |  | ||||||
|             MessageInputStream<BulletinBoardMessage> inputStream = |  | ||||||
|                     MessageInputStreamFactory.createMessageInputStream( |  | ||||||
|                             new ByteArrayInputStream(byteOutputStream.toByteArray()), |  | ||||||
|                             BulletinBoardMessage.class); |  | ||||||
| 
 |  | ||||||
|             return inputStream.asList(); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void readMessages(MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback) { |  | ||||||
|         Futures.addCallback(executorService.submit(new MessageReader(filterList)), callback); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     class SubscriptionCallback implements FutureCallback<List<BulletinBoardMessage>> { |  | ||||||
| 
 |  | ||||||
|         private MessageFilterList filterList; |  | ||||||
|         private final FutureCallback<List<BulletinBoardMessage>> callback; |  | ||||||
| 
 |  | ||||||
|         public SubscriptionCallback(MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback) { |  | ||||||
|             this.filterList = filterList; |  | ||||||
|             this.callback = callback; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onSuccess(List<BulletinBoardMessage> result) { |  | ||||||
| 
 |  | ||||||
|             // Report new messages to user
 |  | ||||||
|             if (callback != null) |  | ||||||
|                 callback.onSuccess(result); |  | ||||||
| 
 |  | ||||||
|             MessageFilterList.Builder filterBuilder = filterList.toBuilder(); |  | ||||||
| 
 |  | ||||||
|             // If any new messages arrived: update the MIN_ENTRY condition
 |  | ||||||
|             if (result.size() > 0) { |  | ||||||
| 
 |  | ||||||
|                 // Remove last filter from list (MIN_ENTRY one)
 |  | ||||||
|                 filterBuilder.removeFilter(filterBuilder.getFilterCount() - 1); |  | ||||||
| 
 |  | ||||||
|                 // Add updated MIN_ENTRY filter (entry number is successor of last received entry's number)
 |  | ||||||
|                 filterBuilder.addFilter(MessageFilter.newBuilder() |  | ||||||
|                         .setType(FilterType.MIN_ENTRY) |  | ||||||
|                         .setEntry(result.get(result.size() - 1).getEntryNum() + 1) |  | ||||||
|                         .build()); |  | ||||||
| 
 |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             filterList = filterBuilder.build(); |  | ||||||
| 
 |  | ||||||
|             // Reschedule job
 |  | ||||||
|             Futures.addCallback(executorService.schedule(new MessageReader(filterList), subsrciptionDelay, TimeUnit.MILLISECONDS), this); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onFailure(Throwable t) { |  | ||||||
| 
 |  | ||||||
|             // Notify caller about failure and terminate subscription
 |  | ||||||
|             if (callback != null) |  | ||||||
|                 callback.onFailure(t); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void subscribe(MessageFilterList filterList, long startEntry, FutureCallback<List<BulletinBoardMessage>> callback) { |  | ||||||
| 
 |  | ||||||
|         MessageFilterList subscriptionFilterList = |  | ||||||
|                 filterList.toBuilder() |  | ||||||
|                     .addFilter(MessageFilter.newBuilder() |  | ||||||
|                             .setType(FilterType.MIN_ENTRY) |  | ||||||
|                             .setEntry(startEntry) |  | ||||||
|                             .build()) |  | ||||||
|                     .build(); |  | ||||||
| 
 |  | ||||||
|         Futures.addCallback(executorService.submit(new MessageReader(subscriptionFilterList)), new SubscriptionCallback(subscriptionFilterList, callback)); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void subscribe(MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback) { |  | ||||||
|         subscribe(filterList, 0, callback); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private class BatchDataReader implements Callable<List<BatchChunk>> { |  | ||||||
| 
 |  | ||||||
|         private final MessageID msgID; |  | ||||||
| 
 |  | ||||||
|         public BatchDataReader(MessageID msgID) { |  | ||||||
|             this.msgID = msgID; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public List<BatchChunk> call() throws Exception { |  | ||||||
| 
 |  | ||||||
|             BatchQuery batchQuery = BatchQuery.newBuilder() |  | ||||||
|                     .setMsgID(msgID) |  | ||||||
|                     .setStartPosition(0) |  | ||||||
|                     .build(); |  | ||||||
| 
 |  | ||||||
|             ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream(); |  | ||||||
|             MessageOutputStream<BatchChunk> batchOutputStream = new MessageOutputStream<>(byteOutputStream); |  | ||||||
|             server.readBatch(batchQuery,batchOutputStream); |  | ||||||
| 
 |  | ||||||
|             MessageInputStream<BatchChunk> inputStream = |  | ||||||
|                     MessageInputStreamFactory.createMessageInputStream( |  | ||||||
|                             new ByteArrayInputStream(byteOutputStream.toByteArray()), |  | ||||||
|                             BatchChunk.class); |  | ||||||
| 
 |  | ||||||
|             return inputStream.asList(); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private class CompleteBatchReader implements Callable<BulletinBoardMessage> { |  | ||||||
| 
 |  | ||||||
|         private final MessageID msgID; |  | ||||||
| 
 |  | ||||||
|         public CompleteBatchReader(MessageID msgID) { |  | ||||||
|             this.msgID = msgID; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public BulletinBoardMessage call() throws Exception { |  | ||||||
| 
 |  | ||||||
|             // Read message (mat be a stub)
 |  | ||||||
| 
 |  | ||||||
|             MessageFilterList filterList = MessageFilterList.newBuilder() |  | ||||||
|                     .addFilter(MessageFilter.newBuilder() |  | ||||||
|                             .setType(FilterType.MSG_ID) |  | ||||||
|                             .setId(msgID.getID()) |  | ||||||
|                             .build()) |  | ||||||
|                     .build(); |  | ||||||
| 
 |  | ||||||
|             MessageReader messageReader = new MessageReader(filterList); |  | ||||||
|             List<BulletinBoardMessage> bulletinBoardMessages = messageReader.call(); |  | ||||||
| 
 |  | ||||||
|             if (bulletinBoardMessages.size() <= 0) { |  | ||||||
|                 throw new NotFoundException("Message does not exist"); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             BulletinBoardMessage msg = bulletinBoardMessages.get(0); |  | ||||||
| 
 |  | ||||||
|             if (msg.getMsg().getDataTypeCase() == UnsignedBulletinBoardMessage.DataTypeCase.MSGID) { |  | ||||||
| 
 |  | ||||||
|                 // Read data
 |  | ||||||
| 
 |  | ||||||
|                 BatchDataReader batchDataReader = new BatchDataReader(msgID); |  | ||||||
|                 List<BatchChunk> batchChunkList = batchDataReader.call(); |  | ||||||
| 
 |  | ||||||
|                 // Combine and return
 |  | ||||||
| 
 |  | ||||||
|                 return BulletinBoardUtils.gatherBatch(msg, batchChunkList); |  | ||||||
| 
 |  | ||||||
|             } else { |  | ||||||
|                 return msg; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private class BatchDataCombiner implements Callable<BulletinBoardMessage> { |  | ||||||
| 
 |  | ||||||
|         private final BulletinBoardMessage stub; |  | ||||||
| 
 |  | ||||||
|         public BatchDataCombiner(BulletinBoardMessage stub) { |  | ||||||
|             this.stub = stub; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public BulletinBoardMessage call() throws Exception { |  | ||||||
| 
 |  | ||||||
|             MessageID msgID = MessageID.newBuilder().setID(stub.getMsg().getMsgId()).build(); |  | ||||||
| 
 |  | ||||||
|             BatchDataReader batchDataReader = new BatchDataReader(msgID); |  | ||||||
| 
 |  | ||||||
|             List<BatchChunk> batchChunkList = batchDataReader.call(); |  | ||||||
| 
 |  | ||||||
|             return BulletinBoardUtils.gatherBatch(stub, batchChunkList); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void readMessage(MessageID msgID, FutureCallback<BulletinBoardMessage> callback) { |  | ||||||
|         Futures.addCallback(executorService.submit(new CompleteBatchReader(msgID)), callback); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void readBatchData(BulletinBoardMessage stub, FutureCallback<BulletinBoardMessage> callback) throws IllegalArgumentException { |  | ||||||
| 
 |  | ||||||
|         if (stub.getMsg().getDataTypeCase() != UnsignedBulletinBoardMessage.DataTypeCase.MSGID){ |  | ||||||
|             throw new IllegalArgumentException("Message is not a stub and does not contain the required message ID"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         Futures.addCallback(executorService.submit(new BatchDataCombiner(stub)),callback); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private class SyncQueryHandler implements Callable<SyncQueryResponse> { |  | ||||||
| 
 |  | ||||||
|         private final SyncQuery syncQuery; |  | ||||||
| 
 |  | ||||||
|         public SyncQueryHandler(SyncQuery syncQuery) { |  | ||||||
|             this.syncQuery = syncQuery; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public SyncQueryResponse call() throws Exception { |  | ||||||
|             return server.querySync(syncQuery); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void querySync(SyncQuery syncQuery, FutureCallback<SyncQueryResponse> callback) { |  | ||||||
|         Futures.addCallback(executorService.submit(new SyncQueryHandler(syncQuery)), callback); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * This method is a stub, since the implementation only considers one server, and that is given in the constructor |  | ||||||
|      * @param ignored is ignored |  | ||||||
|      */ |  | ||||||
|     @Override |  | ||||||
|     public void init(BulletinBoardClientParams ignored) {} |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException { |  | ||||||
| 
 |  | ||||||
|             MessagePoster poster = new MessagePoster(msg); |  | ||||||
|             poster.call(); |  | ||||||
| 
 |  | ||||||
|             digest.update(msg.getMsg()); |  | ||||||
|             return digest.digestAsMessageID(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public MessageID postAsBatch(BulletinBoardMessage msg, int chunkSize) throws CommunicationException { |  | ||||||
| 
 |  | ||||||
|         CompleteBatchPoster poster = new CompleteBatchPoster(msg, chunkSize); |  | ||||||
|         Boolean result = poster.call(); |  | ||||||
| 
 |  | ||||||
|         if (!result) |  | ||||||
|             throw new CommunicationException("Batch post failed"); |  | ||||||
| 
 |  | ||||||
|         digest.reset(); |  | ||||||
|         digest.update(msg); |  | ||||||
|         return digest.digestAsMessageID(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public float getRedundancy(MessageID id) { |  | ||||||
| 
 |  | ||||||
|         try { |  | ||||||
| 
 |  | ||||||
|             RedundancyGetter getter = new RedundancyGetter(id); |  | ||||||
|             return getter.call(); |  | ||||||
| 
 |  | ||||||
|         } catch (Exception e) { |  | ||||||
|             return -1.0f; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public List<BulletinBoardMessage> readMessages(MessageFilterList filterList) throws CommunicationException{ |  | ||||||
| 
 |  | ||||||
|         try { |  | ||||||
| 
 |  | ||||||
|             MessageReader reader = new MessageReader(filterList); |  | ||||||
|             return reader.call(); |  | ||||||
| 
 |  | ||||||
|         } catch (Exception e){ |  | ||||||
|             throw new CommunicationException("Error reading from server"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public BulletinBoardMessage readMessage(MessageID msgID) throws CommunicationException { |  | ||||||
| 
 |  | ||||||
|         MessageFilterList filterList = MessageFilterList.newBuilder() |  | ||||||
|                 .addFilter(MessageFilter.newBuilder() |  | ||||||
|                         .setType(FilterType.MSG_ID) |  | ||||||
|                         .setId(msgID.getID()) |  | ||||||
|                         .build()) |  | ||||||
|                 .build(); |  | ||||||
| 
 |  | ||||||
|         CompleteBatchReader completeBatchReader = new CompleteBatchReader(msgID); |  | ||||||
| 
 |  | ||||||
|         try { |  | ||||||
|             return completeBatchReader.call(); |  | ||||||
|         } catch (Exception e) { |  | ||||||
|             throw new CommunicationException(e.getMessage() + " " + e.getMessage()); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public BulletinBoardMessage readBatchData(BulletinBoardMessage stub) throws CommunicationException, IllegalArgumentException { |  | ||||||
| 
 |  | ||||||
|         if (stub.getMsg().getDataTypeCase() != UnsignedBulletinBoardMessage.DataTypeCase.MSGID){ |  | ||||||
|             throw new IllegalArgumentException("Message is not a stub and does not contain the required message ID"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         BatchDataCombiner combiner = new BatchDataCombiner(stub); |  | ||||||
| 
 |  | ||||||
|         try { |  | ||||||
|             return combiner.call(); |  | ||||||
|         } catch (Exception e) { |  | ||||||
|             throw new CommunicationException(e.getCause() + " " + e.getMessage()); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) throws CommunicationException { |  | ||||||
|         return server.generateSyncQuery(generateSyncQueryParams); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void deleteMessage(MessageID msgID, FutureCallback<Boolean> callback) { |  | ||||||
| 
 |  | ||||||
|         try { |  | ||||||
|             Boolean deleted = server.deleteMessage(msgID).getValue(); |  | ||||||
|             if (callback != null) |  | ||||||
|                 callback.onSuccess(deleted); |  | ||||||
|         } catch (CommunicationException e) { |  | ||||||
|             if (callback != null) |  | ||||||
|                 callback.onFailure(e); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void deleteMessage(long entryNum, FutureCallback<Boolean> callback) { |  | ||||||
| 
 |  | ||||||
|         try { |  | ||||||
|             Boolean deleted = server.deleteMessage(entryNum).getValue(); |  | ||||||
|             if (callback != null) |  | ||||||
|                 callback.onSuccess(deleted); |  | ||||||
|         } catch (CommunicationException e) { |  | ||||||
|             if (callback != null) |  | ||||||
|                 callback.onFailure(e); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public boolean deleteMessage(MessageID msgID) throws CommunicationException { |  | ||||||
|         return server.deleteMessage(msgID).getValue(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public boolean deleteMessage(long entryNum) throws CommunicationException { |  | ||||||
|         return server.deleteMessage(entryNum).getValue(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void close() { |  | ||||||
|         try { |  | ||||||
|             server.close(); |  | ||||||
|         } catch (CommunicationException ignored) {} |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,27 +0,0 @@ | ||||||
| package meerkat.bulletinboard; |  | ||||||
| 
 |  | ||||||
| import meerkat.bulletinboard.AsyncBulletinBoardClient.BatchIdentifier; |  | ||||||
| 
 |  | ||||||
| import java.util.Arrays; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 17-Jun-16. |  | ||||||
|  */ |  | ||||||
| public final class MultiServerBatchIdentifier implements AsyncBulletinBoardClient.BatchIdentifier { |  | ||||||
| 
 |  | ||||||
|     // Per-server identifiers
 |  | ||||||
|     private final Iterable<BatchIdentifier> identifiers; |  | ||||||
| 
 |  | ||||||
|     public MultiServerBatchIdentifier(Iterable<BatchIdentifier> identifiers) { |  | ||||||
|         this.identifiers = identifiers; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public MultiServerBatchIdentifier(BatchIdentifier[] identifiers) { |  | ||||||
|         this.identifiers = Arrays.asList(identifiers); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public Iterable<BatchIdentifier> getIdentifiers() { |  | ||||||
|         return identifiers; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,98 +0,0 @@ | ||||||
| package meerkat.bulletinboard; |  | ||||||
| 
 |  | ||||||
| import com.google.common.util.concurrent.FutureCallback; |  | ||||||
| 
 |  | ||||||
| import com.google.common.util.concurrent.FutureCallback; |  | ||||||
| 
 |  | ||||||
| import java.util.Collections; |  | ||||||
| import java.util.Iterator; |  | ||||||
| import java.util.List; |  | ||||||
| import java.util.concurrent.atomic.AtomicBoolean; |  | ||||||
| import java.util.concurrent.atomic.AtomicInteger; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 09-Dec-15. |  | ||||||
|  * |  | ||||||
|  * This is a general class for handling multi-server work |  | ||||||
|  * It utilizes Single Server Clients to perform the actual per-server work |  | ||||||
|  */ |  | ||||||
| public abstract class MultiServerWorker<IN, OUT> extends BulletinClientWorker<IN> implements Runnable, FutureCallback<OUT>{ |  | ||||||
| 
 |  | ||||||
|     protected final List<SingleServerBulletinBoardClient> clients; |  | ||||||
| 
 |  | ||||||
|     protected AtomicInteger minServers; // The minimal number of servers the job must be successful on for the job to be completed
 |  | ||||||
| 
 |  | ||||||
|     protected AtomicInteger maxFailedServers; // The maximal number of allowed server failures
 |  | ||||||
| 
 |  | ||||||
|     private AtomicBoolean returnedResult; |  | ||||||
| 
 |  | ||||||
|     private final FutureCallback<OUT> futureCallback; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Constructor |  | ||||||
|      * @param clients contains a list of Single Server clients to handle requests |  | ||||||
|      * @param shuffleClients is a boolean stating whether or not it is needed to shuffle the clients |  | ||||||
|      * @param minServers is the minimal amount of servers needed in order to successfully complete the job |  | ||||||
|      * @param payload is the payload for the job |  | ||||||
|      * @param maxRetry is the maximal per-server retry count |  | ||||||
|      * @param futureCallback contains the callback methods used to report the result back to the client |  | ||||||
|      */ |  | ||||||
|     public MultiServerWorker(List<SingleServerBulletinBoardClient> clients, boolean shuffleClients, |  | ||||||
|                              int minServers, IN payload, int maxRetry, |  | ||||||
|                              FutureCallback<OUT> futureCallback) { |  | ||||||
| 
 |  | ||||||
|         super(payload,maxRetry); |  | ||||||
| 
 |  | ||||||
|         this.clients = clients; |  | ||||||
|         if (shuffleClients){ |  | ||||||
|             Collections.shuffle(clients); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         this.minServers = new AtomicInteger(minServers); |  | ||||||
|         maxFailedServers = new AtomicInteger(clients.size() - minServers); |  | ||||||
|         this.futureCallback = futureCallback; |  | ||||||
| 
 |  | ||||||
|         returnedResult = new AtomicBoolean(false); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Constructor overload without client shuffling |  | ||||||
|      */ |  | ||||||
|     public MultiServerWorker(List<SingleServerBulletinBoardClient> clients, |  | ||||||
|                              int minServers, IN payload, int maxRetry, |  | ||||||
|                              FutureCallback<OUT> futureCallback) { |  | ||||||
| 
 |  | ||||||
|         this(clients, false, minServers, payload, maxRetry, futureCallback); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Used to report a successful operation to the client |  | ||||||
|      * Only reports once to the client |  | ||||||
|      * @param result is the result |  | ||||||
|      */ |  | ||||||
|     protected void succeed(OUT result){ |  | ||||||
|         if (returnedResult.compareAndSet(false, true)) { |  | ||||||
|             if (futureCallback != null) |  | ||||||
|                 futureCallback.onSuccess(result); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Used to report a failed operation to the client |  | ||||||
|      * Only reports once to the client |  | ||||||
|      * @param t contains the error/exception that occurred |  | ||||||
|      */ |  | ||||||
|     protected void fail(Throwable t){ |  | ||||||
|         if (returnedResult.compareAndSet(false, true)) { |  | ||||||
|             if (futureCallback != null) |  | ||||||
|                 futureCallback.onFailure(t); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     protected int getClientNumber() { |  | ||||||
|         return clients.size(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,329 +0,0 @@ | ||||||
| package meerkat.bulletinboard; |  | ||||||
| 
 |  | ||||||
| import com.google.protobuf.BoolValue; |  | ||||||
| import com.google.protobuf.ByteString; |  | ||||||
| import com.google.protobuf.Int64Value; |  | ||||||
| import meerkat.bulletinboard.workers.singleserver.*; |  | ||||||
| import meerkat.comm.CommunicationException; |  | ||||||
| import meerkat.crypto.concrete.SHA256Digest; |  | ||||||
| import meerkat.protobuf.BulletinBoardApi; |  | ||||||
| import meerkat.protobuf.BulletinBoardApi.*; |  | ||||||
| import meerkat.protobuf.Voting.*; |  | ||||||
| import meerkat.rest.*; |  | ||||||
| import meerkat.util.BulletinBoardUtils; |  | ||||||
| 
 |  | ||||||
| import java.util.List; |  | ||||||
| 
 |  | ||||||
| import javax.ws.rs.client.Client; |  | ||||||
| import javax.ws.rs.client.ClientBuilder; |  | ||||||
| import javax.ws.rs.client.Entity; |  | ||||||
| import javax.ws.rs.client.WebTarget; |  | ||||||
| import javax.ws.rs.core.Response; |  | ||||||
| 
 |  | ||||||
| import static meerkat.bulletinboard.BulletinBoardConstants.*; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 05-Dec-15. |  | ||||||
|  * Implements BulletinBoardClient interface in a simple, straightforward manner |  | ||||||
|  */ |  | ||||||
| public class SimpleBulletinBoardClient implements BulletinBoardClient{ |  | ||||||
| 
 |  | ||||||
|     protected List<String> meerkatDBs; |  | ||||||
| 
 |  | ||||||
|     protected Client client; |  | ||||||
| 
 |  | ||||||
|     protected BulletinBoardDigest digest; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Stores database locations and initializes the web Client |  | ||||||
|      * @param clientParams contains the data needed to access the DBs |  | ||||||
|      */ |  | ||||||
|     @Override |  | ||||||
|     public void init(BulletinBoardClientParams clientParams) { |  | ||||||
| 
 |  | ||||||
|         this.meerkatDBs = clientParams.getBulletinBoardAddressList(); |  | ||||||
| 
 |  | ||||||
|         client = ClientBuilder.newClient(); |  | ||||||
|         client.register(ProtobufMessageBodyReader.class); |  | ||||||
|         client.register(ProtobufMessageBodyWriter.class); |  | ||||||
| 
 |  | ||||||
|         // Wrap the Digest into a BatchDigest
 |  | ||||||
|         digest = new GenericBulletinBoardDigest(new SHA256Digest()); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Post message to all DBs |  | ||||||
|      * Make only one try per DB. |  | ||||||
|      * @param msg is the message, |  | ||||||
|      * @return the message ID for later retrieval |  | ||||||
|      * @throws CommunicationException |  | ||||||
|      */ |  | ||||||
|     @Override |  | ||||||
|     public MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException { |  | ||||||
| 
 |  | ||||||
|         WebTarget webTarget; |  | ||||||
|         Response response = null; |  | ||||||
| 
 |  | ||||||
|         // Post message to all databases
 |  | ||||||
|         try { |  | ||||||
|             for (String db : meerkatDBs) { |  | ||||||
| 
 |  | ||||||
|                 SingleServerPostMessageWorker worker = new SingleServerPostMessageWorker(db, msg, 0); |  | ||||||
| 
 |  | ||||||
|                 worker.call(); |  | ||||||
| 
 |  | ||||||
|             } |  | ||||||
|         } catch (Exception e) { // Occurs only when server replies with valid status but invalid data
 |  | ||||||
|             throw new CommunicationException("Error accessing database: " + e.getMessage()); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // Calculate the correct message ID and return it
 |  | ||||||
|         digest.reset(); |  | ||||||
|         digest.update(msg.getMsg()); |  | ||||||
|         return MessageID.newBuilder().setID(ByteString.copyFrom(digest.digest())).build(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Access each database and search for a given message ID |  | ||||||
|      * Return the number of databases in which the message was found |  | ||||||
|      * Only try once per DB |  | ||||||
|      * Ignore communication exceptions in specific databases |  | ||||||
|      * @param id is the requested message ID |  | ||||||
|      * @return the number of DBs in which retrieval was successful |  | ||||||
|      */ |  | ||||||
|     @Override |  | ||||||
|     public float getRedundancy(MessageID id) { |  | ||||||
|         WebTarget webTarget; |  | ||||||
|         Response response; |  | ||||||
| 
 |  | ||||||
|         MessageFilterList filterList = MessageFilterList.newBuilder() |  | ||||||
|                 .addFilter(MessageFilter.newBuilder() |  | ||||||
|                     .setType(FilterType.MSG_ID) |  | ||||||
|                     .setId(id.getID()) |  | ||||||
|                     .build()) |  | ||||||
|                 .build(); |  | ||||||
| 
 |  | ||||||
|         float count = 0; |  | ||||||
| 
 |  | ||||||
|         for (String db : meerkatDBs) { |  | ||||||
|             try { |  | ||||||
|                 webTarget = client.target(db).path(BULLETIN_BOARD_SERVER_PATH).path(READ_MESSAGES_PATH); |  | ||||||
| 
 |  | ||||||
|                 response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(filterList, Constants.MEDIATYPE_PROTOBUF)); |  | ||||||
| 
 |  | ||||||
|                 if (response.readEntity(BulletinBoardMessageList.class).getMessageCount() > 0){ |  | ||||||
|                     count++; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|             } catch (Exception e) {} |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return count / ((float) meerkatDBs.size()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Go through the DBs and try to retrieve messages according to the specified filter |  | ||||||
|      * If at the operation is successful for some DB: return the results and stop iterating |  | ||||||
|      * If no operation is successful: return null (NOT blank list) |  | ||||||
|      * @param filterList return only messages that match the filters (null means no filtering). |  | ||||||
|      * @return the list of Bulletin Board messages that are returned from a server |  | ||||||
|      */ |  | ||||||
|     @Override |  | ||||||
|     public List<BulletinBoardMessage> readMessages(MessageFilterList filterList) throws CommunicationException{ |  | ||||||
| 
 |  | ||||||
|         // Replace null filter list with blank one.
 |  | ||||||
|         if (filterList == null){ |  | ||||||
|             filterList = MessageFilterList.getDefaultInstance(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         String exceptionString = ""; |  | ||||||
| 
 |  | ||||||
|         for (String db : meerkatDBs) { |  | ||||||
| 
 |  | ||||||
|             try { |  | ||||||
| 
 |  | ||||||
|                 SingleServerReadMessagesWorker worker = new SingleServerReadMessagesWorker(db, filterList, 0); |  | ||||||
| 
 |  | ||||||
|                 List<BulletinBoardMessage> result = worker.call(); |  | ||||||
| 
 |  | ||||||
|                 return result; |  | ||||||
| 
 |  | ||||||
|             } catch (Exception e) { |  | ||||||
|                 //TODO: log
 |  | ||||||
|                 exceptionString += e.getMessage() + "\n"; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         throw new CommunicationException("Could not find message in any DB. Errors follow:\n" + exceptionString); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public MessageID postAsBatch(BulletinBoardMessage msg, int chunkSize) throws CommunicationException { |  | ||||||
| 
 |  | ||||||
|         List<BatchChunk> chunkList = BulletinBoardUtils.breakToBatch(msg, chunkSize); |  | ||||||
| 
 |  | ||||||
|         BeginBatchMessage beginBatchMessage = BulletinBoardUtils.generateBeginBatchMessage(msg); |  | ||||||
| 
 |  | ||||||
|         boolean posted = false; |  | ||||||
| 
 |  | ||||||
|         // Post message to all databases
 |  | ||||||
| 
 |  | ||||||
|         for (String db : meerkatDBs) { |  | ||||||
| 
 |  | ||||||
|             try { |  | ||||||
| 
 |  | ||||||
|                 int pos = 0; |  | ||||||
| 
 |  | ||||||
|                 SingleServerBeginBatchWorker beginBatchWorker = new SingleServerBeginBatchWorker(db, beginBatchMessage, 0); |  | ||||||
| 
 |  | ||||||
|                 Int64Value batchId = beginBatchWorker.call(); |  | ||||||
| 
 |  | ||||||
|                 BatchMessage.Builder builder = BatchMessage.newBuilder().setBatchId(batchId.getValue()); |  | ||||||
| 
 |  | ||||||
|                 for (BatchChunk batchChunk : chunkList) { |  | ||||||
| 
 |  | ||||||
|                     SingleServerPostBatchWorker postBatchWorker = |  | ||||||
|                             new SingleServerPostBatchWorker( |  | ||||||
|                                     db, |  | ||||||
|                                     builder.setData(batchChunk).setSerialNum(pos).build(), |  | ||||||
|                                     0); |  | ||||||
| 
 |  | ||||||
|                     postBatchWorker.call(); |  | ||||||
| 
 |  | ||||||
|                     pos++; |  | ||||||
| 
 |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 CloseBatchMessage closeBatchMessage = BulletinBoardUtils.generateCloseBatchMessage(batchId, chunkList.size(), msg); |  | ||||||
| 
 |  | ||||||
|                 SingleServerCloseBatchWorker closeBatchWorker = new SingleServerCloseBatchWorker(db, closeBatchMessage, 0); |  | ||||||
| 
 |  | ||||||
|                 closeBatchWorker.call(); |  | ||||||
| 
 |  | ||||||
|                 posted = true; |  | ||||||
| 
 |  | ||||||
|             } catch(Exception ignored) {} |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (!posted){ |  | ||||||
|             throw new CommunicationException("Could not post to any server"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         digest.reset(); |  | ||||||
|         digest.update(msg); |  | ||||||
|         return digest.digestAsMessageID(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public BulletinBoardMessage readMessage(MessageID msgID) throws CommunicationException { |  | ||||||
| 
 |  | ||||||
|         MessageFilterList filterList = MessageFilterList.newBuilder() |  | ||||||
|                 .addFilter(MessageFilter.newBuilder() |  | ||||||
|                         .setType(FilterType.MSG_ID) |  | ||||||
|                         .setId(msgID.getID()) |  | ||||||
|                         .build()) |  | ||||||
|                 .build(); |  | ||||||
| 
 |  | ||||||
|         BatchQuery batchQuery = BatchQuery.newBuilder() |  | ||||||
|                 .setMsgID(msgID) |  | ||||||
|                 .setStartPosition(0) |  | ||||||
|                 .build(); |  | ||||||
| 
 |  | ||||||
|         String exceptionString = ""; |  | ||||||
| 
 |  | ||||||
|         for (String db : meerkatDBs) { |  | ||||||
| 
 |  | ||||||
|             try { |  | ||||||
|                 SingleServerReadMessagesWorker messagesWorker = new SingleServerReadMessagesWorker(db, filterList, 0); |  | ||||||
| 
 |  | ||||||
|                 List<BulletinBoardMessage> messages = messagesWorker.call(); |  | ||||||
| 
 |  | ||||||
|                 if (messages == null || messages.size() < 1) |  | ||||||
|                     continue; |  | ||||||
| 
 |  | ||||||
|                 BulletinBoardMessage stub = messages.get(0); |  | ||||||
| 
 |  | ||||||
|                 SingleServerReadBatchWorker batchWorker = new SingleServerReadBatchWorker(db, batchQuery, 0); |  | ||||||
| 
 |  | ||||||
|                 List<BatchChunk> batchChunkList = batchWorker.call(); |  | ||||||
| 
 |  | ||||||
|                 return BulletinBoardUtils.gatherBatch(stub, batchChunkList); |  | ||||||
| 
 |  | ||||||
|             } catch (Exception e) { |  | ||||||
|                 //TODO: log
 |  | ||||||
|                 exceptionString += e.getMessage() + "\n"; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         throw new CommunicationException("Could not find message in any DB. Errors follow:\n" + exceptionString); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public BulletinBoardMessage readBatchData(BulletinBoardMessage stub) throws CommunicationException, IllegalArgumentException { |  | ||||||
| 
 |  | ||||||
|         if (stub.getMsg().getDataTypeCase() != UnsignedBulletinBoardMessage.DataTypeCase.MSGID){ |  | ||||||
|             throw new IllegalArgumentException("Message is not a stub and does not contain the required message ID"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         BatchQuery batchQuery = BatchQuery.newBuilder() |  | ||||||
|                 .setMsgID(MessageID.newBuilder() |  | ||||||
|                         .setID(stub.getMsg().getMsgId()) |  | ||||||
|                         .build()) |  | ||||||
|                 .setStartPosition(0) |  | ||||||
|                 .build(); |  | ||||||
| 
 |  | ||||||
|         String exceptionString = ""; |  | ||||||
| 
 |  | ||||||
|         for (String db : meerkatDBs) { |  | ||||||
| 
 |  | ||||||
|             try { |  | ||||||
| 
 |  | ||||||
|                 SingleServerReadBatchWorker batchWorker = new SingleServerReadBatchWorker(db, batchQuery, 0); |  | ||||||
| 
 |  | ||||||
|                 List<BatchChunk> batchChunkList = batchWorker.call(); |  | ||||||
| 
 |  | ||||||
|                 return BulletinBoardUtils.gatherBatch(stub, batchChunkList); |  | ||||||
| 
 |  | ||||||
|             } catch (Exception e) { |  | ||||||
|                 //TODO: log
 |  | ||||||
|                 exceptionString += e.getMessage() + "\n"; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         throw new CommunicationException("Could not find message in any DB. Errors follow:\n" + exceptionString); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) throws CommunicationException { |  | ||||||
| 
 |  | ||||||
|         WebTarget webTarget; |  | ||||||
|         Response response; |  | ||||||
| 
 |  | ||||||
|         for (String db : meerkatDBs) { |  | ||||||
| 
 |  | ||||||
|             try { |  | ||||||
|                 webTarget = client.target(db).path(BULLETIN_BOARD_SERVER_PATH).path(GENERATE_SYNC_QUERY_PATH); |  | ||||||
| 
 |  | ||||||
|                 response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(generateSyncQueryParams, Constants.MEDIATYPE_PROTOBUF)); |  | ||||||
| 
 |  | ||||||
|                 return response.readEntity(SyncQuery.class); |  | ||||||
| 
 |  | ||||||
|             } catch (Exception e) {} |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         throw new CommunicationException("Could not contact any server"); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public void close() { |  | ||||||
|         client.close(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,241 +0,0 @@ | ||||||
| package meerkat.bulletinboard; |  | ||||||
| 
 |  | ||||||
| import com.google.common.util.concurrent.FutureCallback; |  | ||||||
| import com.google.protobuf.ByteString; |  | ||||||
| import meerkat.comm.CommunicationException; |  | ||||||
| import meerkat.protobuf.BulletinBoardApi.*; |  | ||||||
| import meerkat.util.BulletinBoardUtils; |  | ||||||
| 
 |  | ||||||
| import java.util.LinkedList; |  | ||||||
| import java.util.List; |  | ||||||
| import java.util.concurrent.Semaphore; |  | ||||||
| import java.util.concurrent.TimeUnit; |  | ||||||
| import java.util.concurrent.atomic.AtomicBoolean; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel on 13/04/2016. |  | ||||||
|  * Simple, straightforward implementation of the {@link BulletinBoardSynchronizer} interface |  | ||||||
|  */ |  | ||||||
| public class SimpleBulletinBoardSynchronizer implements BulletinBoardSynchronizer { |  | ||||||
| 
 |  | ||||||
|     private DeletableSubscriptionBulletinBoardClient localClient; |  | ||||||
|     private AsyncBulletinBoardClient remoteClient; |  | ||||||
| 
 |  | ||||||
|     private AtomicBoolean running; |  | ||||||
|     private volatile SyncStatus syncStatus; |  | ||||||
| 
 |  | ||||||
|     private List<FutureCallback<Integer>> messageCountCallbacks; |  | ||||||
|     private List<FutureCallback<SyncStatus>> syncStatusCallbacks; |  | ||||||
| 
 |  | ||||||
|     private static final MessageFilterList EMPTY_FILTER = MessageFilterList.getDefaultInstance(); |  | ||||||
|     private static final int DEFAULT_SLEEP_INTERVAL = 10000; // 10 Seconds
 |  | ||||||
|     private static final int DEFAULT_WAIT_CAP = 300000; // 5 minutes wait before deciding that the sync has failed fatally
 |  | ||||||
| 
 |  | ||||||
|     private final int SLEEP_INTERVAL; |  | ||||||
|     private final int WAIT_CAP; |  | ||||||
| 
 |  | ||||||
|     private Semaphore semaphore; |  | ||||||
| 
 |  | ||||||
|     private class SyncCallback implements FutureCallback<List<BulletinBoardMessage>> { |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onSuccess(List<BulletinBoardMessage> result) { |  | ||||||
| 
 |  | ||||||
|             // Notify Message Count callbacks if needed
 |  | ||||||
| 
 |  | ||||||
|             if (syncStatus != SyncStatus.SYNCHRONIZED || result.size() > 0) { |  | ||||||
| 
 |  | ||||||
|                 for (FutureCallback<Integer> callback : messageCountCallbacks){ |  | ||||||
|                     callback.onSuccess(result.size()); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             // Handle upload and status change
 |  | ||||||
| 
 |  | ||||||
|             SyncStatus newStatus = SyncStatus.PENDING; |  | ||||||
| 
 |  | ||||||
|             if (result.size() == 0) { |  | ||||||
|                 newStatus = SyncStatus.SYNCHRONIZED; |  | ||||||
|                 semaphore.release(); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             else{   // Upload messages
 |  | ||||||
| 
 |  | ||||||
|                 for (BulletinBoardMessage message : result){ |  | ||||||
| 
 |  | ||||||
|                     try { |  | ||||||
| 
 |  | ||||||
|                         if (message.getMsg().getDataTypeCase() == UnsignedBulletinBoardMessage.DataTypeCase.MSGID) { |  | ||||||
| 
 |  | ||||||
|                             // This is a batch message: need to upload batch data as well as the message itself
 |  | ||||||
| 
 |  | ||||||
|                             BulletinBoardMessage completeMsg = localClient.readBatchData(message); |  | ||||||
| 
 |  | ||||||
|                             remoteClient.postMessage(completeMsg); |  | ||||||
| 
 |  | ||||||
|                             localClient.deleteMessage(completeMsg.getEntryNum()); |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|                         } else { |  | ||||||
| 
 |  | ||||||
|                             // This is a regular message: post it
 |  | ||||||
|                             remoteClient.postMessage(message); |  | ||||||
| 
 |  | ||||||
|                             localClient.deleteMessage(message.getEntryNum()); |  | ||||||
| 
 |  | ||||||
|                         } |  | ||||||
| 
 |  | ||||||
|                     } catch (CommunicationException e) { |  | ||||||
|                         // This is an error with the local server
 |  | ||||||
|                         // TODO: log
 |  | ||||||
|                         updateSyncStatus(SyncStatus.SERVER_ERROR); |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             updateSyncStatus(newStatus); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onFailure(Throwable t) { |  | ||||||
| 
 |  | ||||||
|             updateSyncStatus(SyncStatus.SERVER_ERROR); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public SimpleBulletinBoardSynchronizer(int sleepInterval, int waitCap) { |  | ||||||
|         this.syncStatus = SyncStatus.STOPPED; |  | ||||||
|         this.SLEEP_INTERVAL = sleepInterval; |  | ||||||
|         this.WAIT_CAP = waitCap; |  | ||||||
|         this.running = new AtomicBoolean(false); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public SimpleBulletinBoardSynchronizer() { |  | ||||||
|         this(DEFAULT_SLEEP_INTERVAL, DEFAULT_WAIT_CAP); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private synchronized void updateSyncStatus(SyncStatus newStatus) { |  | ||||||
| 
 |  | ||||||
|         if (!running.get()) { |  | ||||||
| 
 |  | ||||||
|             newStatus = SyncStatus.STOPPED; |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (newStatus != syncStatus){ |  | ||||||
| 
 |  | ||||||
|             syncStatus = newStatus; |  | ||||||
| 
 |  | ||||||
|             for (FutureCallback<SyncStatus> callback : syncStatusCallbacks){ |  | ||||||
|                 if (callback != null) |  | ||||||
|                     callback.onSuccess(syncStatus); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void init(DeletableSubscriptionBulletinBoardClient localClient, AsyncBulletinBoardClient remoteClient) { |  | ||||||
| 
 |  | ||||||
|         updateSyncStatus(SyncStatus.STOPPED); |  | ||||||
| 
 |  | ||||||
|         this.localClient = localClient; |  | ||||||
|         this.remoteClient = remoteClient; |  | ||||||
| 
 |  | ||||||
|         messageCountCallbacks = new LinkedList<>(); |  | ||||||
|         syncStatusCallbacks = new LinkedList<>(); |  | ||||||
| 
 |  | ||||||
|         semaphore = new Semaphore(0); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public SyncStatus getSyncStatus() { |  | ||||||
|         return syncStatus; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void subscribeToSyncStatus(FutureCallback<SyncStatus> callback) { |  | ||||||
|         syncStatusCallbacks.add(callback); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public List<BulletinBoardMessage> getRemainingMessages()  throws CommunicationException{ |  | ||||||
|         return localClient.readMessages(EMPTY_FILTER); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void getRemainingMessages(FutureCallback<List<BulletinBoardMessage>> callback) { |  | ||||||
|         localClient.readMessages(EMPTY_FILTER, callback); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public long getRemainingMessagesCount() throws CommunicationException { |  | ||||||
|         return localClient.readMessages(EMPTY_FILTER).size(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void subscribeToRemainingMessagesCount(FutureCallback<Integer> callback) { |  | ||||||
|         messageCountCallbacks.add(callback); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void run() { |  | ||||||
| 
 |  | ||||||
|         if (running.compareAndSet(false,true)){ |  | ||||||
| 
 |  | ||||||
|             updateSyncStatus(SyncStatus.PENDING); |  | ||||||
|             SyncCallback callback = new SyncCallback(); |  | ||||||
| 
 |  | ||||||
|             while (syncStatus != SyncStatus.STOPPED) { |  | ||||||
| 
 |  | ||||||
|                 do { |  | ||||||
| 
 |  | ||||||
|                     localClient.readMessages(EMPTY_FILTER, callback); |  | ||||||
| 
 |  | ||||||
|                     try { |  | ||||||
| 
 |  | ||||||
|                         semaphore.tryAcquire(WAIT_CAP, TimeUnit.MILLISECONDS); |  | ||||||
|                         //TODO: log hard error. Too much time trying to upload data.
 |  | ||||||
| 
 |  | ||||||
|                     } catch (InterruptedException ignored) { |  | ||||||
|                         // We expect an interruption when the upload will complete
 |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                 } while (syncStatus == SyncStatus.PENDING); |  | ||||||
| 
 |  | ||||||
|                 // Database is synced. Wait for new data.
 |  | ||||||
| 
 |  | ||||||
|                 try { |  | ||||||
|                     semaphore.tryAcquire(SLEEP_INTERVAL, TimeUnit.MILLISECONDS); |  | ||||||
|                 } catch (InterruptedException e) { |  | ||||||
|                     //TODO: log (probably nudged)
 |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void nudge() { |  | ||||||
|         semaphore.release(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void stop() { |  | ||||||
| 
 |  | ||||||
|         running.set(false); |  | ||||||
|         updateSyncStatus(SyncStatus.STOPPED); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,42 +0,0 @@ | ||||||
| package meerkat.bulletinboard; |  | ||||||
| 
 |  | ||||||
| import com.google.protobuf.Int64Value; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 16-Jun-16. |  | ||||||
|  * Single-server implementation of the BatchIdentifier interface |  | ||||||
|  */ |  | ||||||
| final class SingleServerBatchIdentifier implements AsyncBulletinBoardClient.BatchIdentifier { |  | ||||||
| 
 |  | ||||||
|     private final Int64Value batchId; |  | ||||||
| 
 |  | ||||||
|     private int length; |  | ||||||
| 
 |  | ||||||
|     public SingleServerBatchIdentifier(Int64Value batchId) { |  | ||||||
|         this.batchId = batchId; |  | ||||||
|         length = 0; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public SingleServerBatchIdentifier(long batchId) { |  | ||||||
|         this(Int64Value.newBuilder().setValue(batchId).build()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public Int64Value getBatchId() { |  | ||||||
|         return batchId; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Overrides the existing length with the new one only if the new length is longer |  | ||||||
|      * @param newLength |  | ||||||
|      */ |  | ||||||
|     public void setLength(int newLength) { |  | ||||||
|         if (newLength > length) { |  | ||||||
|             length = newLength; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public int getLength() { |  | ||||||
|         return length; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,875 +0,0 @@ | ||||||
| package meerkat.bulletinboard; |  | ||||||
| 
 |  | ||||||
| import com.google.common.util.concurrent.FutureCallback; |  | ||||||
| import com.google.common.util.concurrent.Futures; |  | ||||||
| import com.google.common.util.concurrent.ListeningScheduledExecutorService; |  | ||||||
| import com.google.common.util.concurrent.MoreExecutors; |  | ||||||
| import com.google.protobuf.Int64Value; |  | ||||||
| import com.google.protobuf.Timestamp; |  | ||||||
| import meerkat.bulletinboard.workers.singleserver.*; |  | ||||||
| import meerkat.comm.CommunicationException; |  | ||||||
| import meerkat.crypto.concrete.SHA256Digest; |  | ||||||
| import meerkat.protobuf.BulletinBoardApi.*; |  | ||||||
| import meerkat.protobuf.Crypto; |  | ||||||
| import meerkat.protobuf.Voting.BulletinBoardClientParams; |  | ||||||
| import meerkat.util.BulletinBoardUtils; |  | ||||||
| 
 |  | ||||||
| import javax.ws.rs.client.Client; |  | ||||||
| import java.lang.Iterable; |  | ||||||
| import java.util.Iterator; |  | ||||||
| import java.util.List; |  | ||||||
| import java.util.concurrent.Executors; |  | ||||||
| import java.util.concurrent.TimeUnit; |  | ||||||
| import java.util.concurrent.atomic.AtomicBoolean; |  | ||||||
| import java.util.concurrent.atomic.AtomicInteger; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 28-Dec-15. |  | ||||||
|  * |  | ||||||
|  * This class implements the asynchronous Bulletin Board Client interface |  | ||||||
|  * It only handles a single Bulletin Board Server |  | ||||||
|  * If the list of servers contains more than one server: the server actually used is the first one |  | ||||||
|  * The class further implements a delayed access to the server after a communication error occurs |  | ||||||
|  */ |  | ||||||
| public class SingleServerBulletinBoardClient implements SubscriptionBulletinBoardClient { |  | ||||||
| 
 |  | ||||||
|     protected Client client; |  | ||||||
| 
 |  | ||||||
|     protected BulletinBoardDigest digest; |  | ||||||
| 
 |  | ||||||
|     private String dbAddress; |  | ||||||
| 
 |  | ||||||
|     private final int MAX_RETRIES = 11; |  | ||||||
| 
 |  | ||||||
|     private final ListeningScheduledExecutorService executorService; |  | ||||||
| 
 |  | ||||||
|     private long lastServerErrorTime; |  | ||||||
| 
 |  | ||||||
|     private final long FAIL_DELAY_IN_MILLISECONDS; |  | ||||||
| 
 |  | ||||||
|     private final long SUBSCRIPTION_INTERVAL_IN_MILLISECONDS; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Notify the client that a job has failed |  | ||||||
|      * This makes new scheduled jobs be scheduled for a later time (after the given delay) |  | ||||||
|      */ |  | ||||||
|     protected void fail() { |  | ||||||
| 
 |  | ||||||
|         // Update last fail time
 |  | ||||||
|         lastServerErrorTime = System.currentTimeMillis(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private class SynchronousRetry<OUT> { |  | ||||||
| 
 |  | ||||||
|         private final SingleServerWorker<?,OUT> worker; |  | ||||||
| 
 |  | ||||||
|         private String thrown; |  | ||||||
| 
 |  | ||||||
|         public SynchronousRetry(SingleServerWorker<?,OUT> worker) { |  | ||||||
|             this.worker = worker; |  | ||||||
|             this.thrown = "Could not contact server. Errors follow:\n"; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         OUT run() throws CommunicationException { |  | ||||||
| 
 |  | ||||||
|             do { |  | ||||||
| 
 |  | ||||||
|                 try { |  | ||||||
|                     return worker.call(); |  | ||||||
|                 } catch (Exception e) { |  | ||||||
|                     thrown += e.getCause() + " " + e.getMessage() + "\n"; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 try { |  | ||||||
|                     Thread.sleep(FAIL_DELAY_IN_MILLISECONDS); |  | ||||||
|                 } catch (InterruptedException e) { |  | ||||||
|                     //TODO: log
 |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 worker.decMaxRetry(); |  | ||||||
| 
 |  | ||||||
|             } while (worker.isRetry()); |  | ||||||
| 
 |  | ||||||
|             throw new CommunicationException(thrown); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * This method adds a worker to the scheduled queue of the threadpool |  | ||||||
|      * If the server is in an accessible state: the job is submitted for immediate handling |  | ||||||
|      * If the server is not accessible: the job is scheduled for a later time |  | ||||||
|      * @param worker is the worker that should be scheduled for work |  | ||||||
|      * @param callback is the class containing callbacks for handling job completion/failure |  | ||||||
|      */ |  | ||||||
|     protected void scheduleWorker(SingleServerWorker worker, FutureCallback callback){ |  | ||||||
| 
 |  | ||||||
|         long timeSinceLastServerError = System.currentTimeMillis() - lastServerErrorTime; |  | ||||||
| 
 |  | ||||||
|         if (timeSinceLastServerError >= FAIL_DELAY_IN_MILLISECONDS) { |  | ||||||
| 
 |  | ||||||
|             // Schedule for immediate processing
 |  | ||||||
|             Futures.addCallback(executorService.submit(worker), callback); |  | ||||||
| 
 |  | ||||||
|         } else { |  | ||||||
| 
 |  | ||||||
|             // Schedule for processing immediately following delay expiry
 |  | ||||||
|             Futures.addCallback(executorService.schedule( |  | ||||||
|                     worker, |  | ||||||
|                     FAIL_DELAY_IN_MILLISECONDS - timeSinceLastServerError, |  | ||||||
|                     TimeUnit.MILLISECONDS), |  | ||||||
|                     callback); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Inner class for handling simple operation results and retrying if needed |  | ||||||
|      */ |  | ||||||
|     class RetryCallback<T> implements FutureCallback<T> { |  | ||||||
| 
 |  | ||||||
|         private final SingleServerWorker worker; |  | ||||||
|         private final FutureCallback<T> futureCallback; |  | ||||||
| 
 |  | ||||||
|         public RetryCallback(SingleServerWorker worker, FutureCallback<T> futureCallback) { |  | ||||||
|             this.worker = worker; |  | ||||||
|             this.futureCallback = futureCallback; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onSuccess(T result) { |  | ||||||
|             if (futureCallback != null) |  | ||||||
|                 futureCallback.onSuccess(result); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onFailure(Throwable t) { |  | ||||||
| 
 |  | ||||||
|             // Notify client about failure
 |  | ||||||
|             fail(); |  | ||||||
| 
 |  | ||||||
|             // Check if another attempt should be made
 |  | ||||||
| 
 |  | ||||||
|             worker.decMaxRetry(); |  | ||||||
| 
 |  | ||||||
|             if (worker.isRetry()) { |  | ||||||
|                 // Perform another attempt
 |  | ||||||
|                 scheduleWorker(worker, this); |  | ||||||
|             } else { |  | ||||||
|                 // No more retries: notify caller about failure
 |  | ||||||
|                 if (futureCallback != null) |  | ||||||
|                     futureCallback.onFailure(t); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * This callback ties together all the per-batch-data callbacks into a single callback |  | ||||||
|      * It reports success back to the user only if all of the batch-data were successfully posted |  | ||||||
|      * If any batch-data fails to post: this callback reports failure |  | ||||||
|      */ |  | ||||||
|     class PostBatchChunkListCallback implements FutureCallback<Boolean> { |  | ||||||
| 
 |  | ||||||
|         private final FutureCallback<Boolean> callback; |  | ||||||
| 
 |  | ||||||
|         private AtomicInteger batchDataRemaining; |  | ||||||
|         private AtomicBoolean aggregatedResult; |  | ||||||
| 
 |  | ||||||
|         public PostBatchChunkListCallback(int batchDataLength, FutureCallback<Boolean> callback) { |  | ||||||
| 
 |  | ||||||
|             this.callback = callback; |  | ||||||
|             this.batchDataRemaining = new AtomicInteger(batchDataLength); |  | ||||||
|             this.aggregatedResult = new AtomicBoolean(false); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onSuccess(Boolean result) { |  | ||||||
| 
 |  | ||||||
|             if (result){ |  | ||||||
|                 this.aggregatedResult.set(true); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if (batchDataRemaining.decrementAndGet() == 0){ |  | ||||||
|                 if (callback != null) |  | ||||||
|                     callback.onSuccess(this.aggregatedResult.get()); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onFailure(Throwable t) { |  | ||||||
| 
 |  | ||||||
|             // Notify caller about failure
 |  | ||||||
|             if (callback != null) |  | ||||||
|                 callback.onFailure(t); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private class ReadBatchCallback implements FutureCallback<List<BatchChunk>> { |  | ||||||
| 
 |  | ||||||
|         private final BulletinBoardMessage stub; |  | ||||||
|         private final FutureCallback<BulletinBoardMessage> callback; |  | ||||||
| 
 |  | ||||||
|         public ReadBatchCallback(BulletinBoardMessage stub, FutureCallback<BulletinBoardMessage> callback) { |  | ||||||
|             this.stub = stub; |  | ||||||
|             this.callback = callback; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onSuccess(List<BatchChunk> result) { |  | ||||||
|             callback.onSuccess(BulletinBoardUtils.gatherBatch(stub, result)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onFailure(Throwable t) { |  | ||||||
|             callback.onFailure(t); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * This callback receives a message which may be a stub |  | ||||||
|      * If the message is not a stub: it returns it as is to a callback function |  | ||||||
|      * If it is a stub: it schedules a read of the batch data which will return a complete message to the callback function |  | ||||||
|      */ |  | ||||||
|     class CompleteMessageReadCallback implements FutureCallback<List<BulletinBoardMessage>>{ |  | ||||||
| 
 |  | ||||||
|         private final FutureCallback<BulletinBoardMessage> callback; |  | ||||||
| 
 |  | ||||||
|         public CompleteMessageReadCallback(FutureCallback<BulletinBoardMessage> callback) { |  | ||||||
| 
 |  | ||||||
|             this.callback = callback; |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onSuccess(List<BulletinBoardMessage> result) { |  | ||||||
|             if (result.size() <= 0) { |  | ||||||
|                 onFailure(new CommunicationException("Could not find required message on the server.")); |  | ||||||
|             } else { |  | ||||||
| 
 |  | ||||||
|                 BulletinBoardMessage msg = result.get(0); |  | ||||||
| 
 |  | ||||||
|                 if (msg.getMsg().getDataTypeCase() != UnsignedBulletinBoardMessage.DataTypeCase.MSGID) { |  | ||||||
|                     callback.onSuccess(msg); |  | ||||||
|                 } else { |  | ||||||
| 
 |  | ||||||
|                     // Create job with MAX retries for retrieval of the Batch Data List
 |  | ||||||
| 
 |  | ||||||
|                     BatchQuery batchQuery = BatchQuery.newBuilder() |  | ||||||
|                             .setMsgID(MessageID.newBuilder() |  | ||||||
|                                     .setID(msg.getMsg().getMsgId()) |  | ||||||
|                                     .build()) |  | ||||||
|                             .build(); |  | ||||||
| 
 |  | ||||||
|                     SingleServerReadBatchWorker batchWorker = new SingleServerReadBatchWorker(dbAddress, batchQuery, MAX_RETRIES); |  | ||||||
| 
 |  | ||||||
|                     scheduleWorker(batchWorker, new ReadBatchCallback(msg, callback)); |  | ||||||
| 
 |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onFailure(Throwable t) { |  | ||||||
|             callback.onFailure(t); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Inner class for handling returned values of subscription operations |  | ||||||
|      * This class's methods also ensure continued operation of the subscription |  | ||||||
|      */ |  | ||||||
|     class SubscriptionCallback implements FutureCallback<List<BulletinBoardMessage>> { |  | ||||||
| 
 |  | ||||||
|         private SingleServerReadMessagesWorker worker; |  | ||||||
|         private final FutureCallback<List<BulletinBoardMessage>> callback; |  | ||||||
| 
 |  | ||||||
|         private MessageFilterList.Builder filterBuilder; |  | ||||||
| 
 |  | ||||||
|         public SubscriptionCallback(SingleServerReadMessagesWorker worker, FutureCallback<List<BulletinBoardMessage>> callback) { |  | ||||||
|             this.worker = worker; |  | ||||||
|             this.callback = callback; |  | ||||||
|             filterBuilder = worker.getPayload().toBuilder(); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onSuccess(List<BulletinBoardMessage> result) { |  | ||||||
| 
 |  | ||||||
|             // Report new messages to user
 |  | ||||||
|             if (callback != null) |  | ||||||
|                 callback.onSuccess(result); |  | ||||||
| 
 |  | ||||||
|             // Update filter if needed
 |  | ||||||
| 
 |  | ||||||
|             if (result.size() > 0) { |  | ||||||
| 
 |  | ||||||
|                 // Remove last filter from list (MIN_ENTRY one)
 |  | ||||||
|                 filterBuilder.removeFilter(filterBuilder.getFilterCount() - 1); |  | ||||||
| 
 |  | ||||||
|                 // Add updated MIN_ENTRY filter (entry number is successor of last received entry's number)
 |  | ||||||
|                 filterBuilder.addFilter(MessageFilter.newBuilder() |  | ||||||
|                         .setType(FilterType.MIN_ENTRY) |  | ||||||
|                         .setEntry(result.get(result.size() - 1).getEntryNum() + 1) |  | ||||||
|                         .build()); |  | ||||||
| 
 |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             // Create new worker with updated task
 |  | ||||||
|             worker = new SingleServerReadMessagesWorker(worker.serverAddress, filterBuilder.build(), MAX_RETRIES); |  | ||||||
| 
 |  | ||||||
|             RetryCallback<List<BulletinBoardMessage>> retryCallback = new RetryCallback<>(worker, this); |  | ||||||
| 
 |  | ||||||
|             // Schedule the worker to run after the given interval has elapsed
 |  | ||||||
|             Futures.addCallback(executorService.schedule(worker, SUBSCRIPTION_INTERVAL_IN_MILLISECONDS, TimeUnit.MILLISECONDS), retryCallback); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onFailure(Throwable t) { |  | ||||||
| 
 |  | ||||||
|             // Notify client about failure
 |  | ||||||
|             fail(); |  | ||||||
| 
 |  | ||||||
|             // Notify caller about failure and terminate subscription
 |  | ||||||
|             if (callback != null) |  | ||||||
|                 callback.onFailure(t); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public SingleServerBulletinBoardClient(ListeningScheduledExecutorService executorService, |  | ||||||
|                                            long failDelayInMilliseconds, |  | ||||||
|                                            long subscriptionIntervalInMilliseconds) { |  | ||||||
| 
 |  | ||||||
|         this.executorService = executorService; |  | ||||||
| 
 |  | ||||||
|         this.FAIL_DELAY_IN_MILLISECONDS = failDelayInMilliseconds; |  | ||||||
|         this.SUBSCRIPTION_INTERVAL_IN_MILLISECONDS = subscriptionIntervalInMilliseconds; |  | ||||||
| 
 |  | ||||||
|         // Set server error time to a time sufficiently in the past to make new jobs go through
 |  | ||||||
|         lastServerErrorTime = System.currentTimeMillis() - failDelayInMilliseconds; |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public SingleServerBulletinBoardClient(int threadPoolSize, long failDelayInMilliseconds, long subscriptionIntervalInMilliseconds) { |  | ||||||
| 
 |  | ||||||
|         this(MoreExecutors.listeningDecorator(Executors.newScheduledThreadPool(threadPoolSize)), |  | ||||||
|                 failDelayInMilliseconds, |  | ||||||
|                 subscriptionIntervalInMilliseconds); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Stores database location, initializes the web Client and |  | ||||||
|      * @param clientParams contains the data needed to access the DBs |  | ||||||
|      */ |  | ||||||
|     @Override |  | ||||||
|     public void init(BulletinBoardClientParams clientParams) { |  | ||||||
| 
 |  | ||||||
|         this.digest = new GenericBulletinBoardDigest(new SHA256Digest()); |  | ||||||
| 
 |  | ||||||
|         // Remove all but first DB address
 |  | ||||||
|         this.dbAddress = clientParams.getBulletinBoardAddress(0); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // Synchronous methods
 |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException { |  | ||||||
| 
 |  | ||||||
|         SingleServerPostMessageWorker worker = new SingleServerPostMessageWorker(dbAddress, msg, MAX_RETRIES); |  | ||||||
| 
 |  | ||||||
|         SynchronousRetry<Boolean> retry = new SynchronousRetry<>(worker); |  | ||||||
| 
 |  | ||||||
|         retry.run(); |  | ||||||
| 
 |  | ||||||
|         digest.reset(); |  | ||||||
|         digest.update(msg); |  | ||||||
| 
 |  | ||||||
|         return digest.digestAsMessageID(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public float getRedundancy(MessageID id) throws CommunicationException { |  | ||||||
| 
 |  | ||||||
|         SingleServerGetRedundancyWorker worker = new SingleServerGetRedundancyWorker(dbAddress, id, MAX_RETRIES); |  | ||||||
| 
 |  | ||||||
|         SynchronousRetry<Float> retry = new SynchronousRetry<>(worker); |  | ||||||
| 
 |  | ||||||
|         return retry.run(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public List<BulletinBoardMessage> readMessages(MessageFilterList filterList) throws CommunicationException { |  | ||||||
| 
 |  | ||||||
|         SingleServerReadMessagesWorker worker = new SingleServerReadMessagesWorker(dbAddress, filterList, MAX_RETRIES); |  | ||||||
| 
 |  | ||||||
|         SynchronousRetry<List<BulletinBoardMessage>> retry = new SynchronousRetry<>(worker); |  | ||||||
| 
 |  | ||||||
|         return retry.run(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public MessageID postAsBatch(BulletinBoardMessage msg, int chunkSize) throws CommunicationException { |  | ||||||
| 
 |  | ||||||
|         // Begin the batch and obtain identifier
 |  | ||||||
| 
 |  | ||||||
|         BeginBatchMessage beginBatchMessage = BeginBatchMessage.newBuilder() |  | ||||||
|                 .addAllTag(msg.getMsg().getTagList()) |  | ||||||
|                 .build(); |  | ||||||
| 
 |  | ||||||
|         SingleServerBeginBatchWorker beginBatchWorker = new SingleServerBeginBatchWorker(dbAddress, beginBatchMessage, MAX_RETRIES); |  | ||||||
| 
 |  | ||||||
|         SynchronousRetry<Int64Value> beginRetry = new SynchronousRetry<>(beginBatchWorker); |  | ||||||
| 
 |  | ||||||
|         Int64Value identifier = beginRetry.run(); |  | ||||||
| 
 |  | ||||||
|         // Post data chunks
 |  | ||||||
| 
 |  | ||||||
|         List<BatchChunk> batchChunkList = BulletinBoardUtils.breakToBatch(msg, chunkSize); |  | ||||||
| 
 |  | ||||||
|         BatchMessage.Builder builder = BatchMessage.newBuilder().setBatchId(identifier.getValue()); |  | ||||||
| 
 |  | ||||||
|         int position = 0; |  | ||||||
| 
 |  | ||||||
|         for (BatchChunk data : batchChunkList) { |  | ||||||
| 
 |  | ||||||
|             builder.setSerialNum(position).setData(data); |  | ||||||
| 
 |  | ||||||
|             SingleServerPostBatchWorker dataWorker = new SingleServerPostBatchWorker(dbAddress, builder.build(), MAX_RETRIES); |  | ||||||
| 
 |  | ||||||
|             SynchronousRetry<Boolean> dataRetry = new SynchronousRetry<>(dataWorker); |  | ||||||
| 
 |  | ||||||
|             dataRetry.run(); |  | ||||||
| 
 |  | ||||||
|             // Increment position in batch
 |  | ||||||
|             position++; |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // Close batch
 |  | ||||||
| 
 |  | ||||||
|         CloseBatchMessage closeBatchMessage = CloseBatchMessage.newBuilder() |  | ||||||
|                 .setBatchId(identifier.getValue()) |  | ||||||
|                 .addAllSig(msg.getSigList()) |  | ||||||
|                 .setTimestamp(msg.getMsg().getTimestamp()) |  | ||||||
|                 .setBatchLength(position) |  | ||||||
|                 .build(); |  | ||||||
| 
 |  | ||||||
|         SingleServerCloseBatchWorker closeBatchWorker = new SingleServerCloseBatchWorker(dbAddress, closeBatchMessage, MAX_RETRIES); |  | ||||||
| 
 |  | ||||||
|         SynchronousRetry<Boolean> retry = new SynchronousRetry<>(closeBatchWorker); |  | ||||||
| 
 |  | ||||||
|         retry.run(); |  | ||||||
| 
 |  | ||||||
|         // Calculate ID and return
 |  | ||||||
| 
 |  | ||||||
|         digest.reset(); |  | ||||||
|         digest.update(msg); |  | ||||||
| 
 |  | ||||||
|         return digest.digestAsMessageID(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public BulletinBoardMessage readMessage(MessageID msgID) throws CommunicationException { |  | ||||||
| 
 |  | ||||||
|         // Retrieve message (which may be a stub)
 |  | ||||||
| 
 |  | ||||||
|         MessageFilterList filterList = MessageFilterList.newBuilder() |  | ||||||
|                 .addFilter(MessageFilter.newBuilder() |  | ||||||
|                         .setType(FilterType.MSG_ID) |  | ||||||
|                         .setId(msgID.getID()) |  | ||||||
|                         .build()) |  | ||||||
|                 .build(); |  | ||||||
| 
 |  | ||||||
|         SingleServerReadMessagesWorker stubWorker = new SingleServerReadMessagesWorker(dbAddress, filterList, MAX_RETRIES); |  | ||||||
| 
 |  | ||||||
|         SynchronousRetry<List<BulletinBoardMessage>> retry = new SynchronousRetry<>(stubWorker); |  | ||||||
| 
 |  | ||||||
|         List<BulletinBoardMessage> messages = retry.run(); |  | ||||||
| 
 |  | ||||||
|         if (messages.size() <= 0) { |  | ||||||
|             throw new CommunicationException("Could not find message in database."); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         BulletinBoardMessage msg = messages.get(0); |  | ||||||
| 
 |  | ||||||
|         if (msg.getMsg().getDataTypeCase() != UnsignedBulletinBoardMessage.DataTypeCase.MSGID) { |  | ||||||
| 
 |  | ||||||
|             // We retrieved a complete message. Return it.
 |  | ||||||
|             return msg; |  | ||||||
| 
 |  | ||||||
|         } else { |  | ||||||
| 
 |  | ||||||
|             // We retrieved a stub. Retrieve data.
 |  | ||||||
|             return readBatchData(msg); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public BulletinBoardMessage readBatchData(BulletinBoardMessage stub) throws CommunicationException, IllegalArgumentException { |  | ||||||
| 
 |  | ||||||
|         BatchQuery batchQuery = BatchQuery.newBuilder() |  | ||||||
|                 .setMsgID(MessageID.newBuilder() |  | ||||||
|                         .setID(stub.getMsg().getMsgId()) |  | ||||||
|                         .build()) |  | ||||||
|                 .setStartPosition(0) |  | ||||||
|                 .build(); |  | ||||||
| 
 |  | ||||||
|         SingleServerReadBatchWorker readBatchWorker = new SingleServerReadBatchWorker(dbAddress, batchQuery, MAX_RETRIES); |  | ||||||
| 
 |  | ||||||
|         SynchronousRetry<List<BatchChunk>> batchRetry = new SynchronousRetry<>(readBatchWorker); |  | ||||||
| 
 |  | ||||||
|         List<BatchChunk> batchChunkList = batchRetry.run(); |  | ||||||
| 
 |  | ||||||
|         return BulletinBoardUtils.gatherBatch(stub, batchChunkList); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public SyncQuery generateSyncQuery(GenerateSyncQueryParams generateSyncQueryParams) throws CommunicationException { |  | ||||||
| 
 |  | ||||||
|         SingleServerGenerateSyncQueryWorker worker = |  | ||||||
|                 new SingleServerGenerateSyncQueryWorker(dbAddress, generateSyncQueryParams, MAX_RETRIES); |  | ||||||
| 
 |  | ||||||
|         SynchronousRetry<SyncQuery> retry = new SynchronousRetry<>(worker); |  | ||||||
| 
 |  | ||||||
|         return retry.run(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // Asynchronous methods
 |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public MessageID postMessage(BulletinBoardMessage msg, FutureCallback<Boolean> callback) { |  | ||||||
| 
 |  | ||||||
|         // Create worker with redundancy 1 and MAX_RETRIES retries
 |  | ||||||
|         SingleServerPostMessageWorker worker = new SingleServerPostMessageWorker(dbAddress, msg, MAX_RETRIES); |  | ||||||
| 
 |  | ||||||
|         // Submit worker and create callback
 |  | ||||||
|         scheduleWorker(worker, new RetryCallback<>(worker, callback)); |  | ||||||
| 
 |  | ||||||
|         // Calculate the correct message ID and return it
 |  | ||||||
|         digest.reset(); |  | ||||||
|         digest.update(msg.getMsg()); |  | ||||||
|         return digest.digestAsMessageID(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private class PostBatchDataCallback implements FutureCallback<Boolean> { |  | ||||||
| 
 |  | ||||||
|         private final BulletinBoardMessage msg; |  | ||||||
|         private final BatchIdentifier identifier; |  | ||||||
|         private final FutureCallback<Boolean> callback; |  | ||||||
| 
 |  | ||||||
|         public PostBatchDataCallback(BulletinBoardMessage msg, BatchIdentifier identifier, FutureCallback<Boolean> callback) { |  | ||||||
|             this.msg = msg; |  | ||||||
|             this.identifier = identifier; |  | ||||||
|             this.callback = callback; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onSuccess(Boolean result) { |  | ||||||
|             closeBatch( |  | ||||||
|                     identifier, |  | ||||||
|                     msg.getMsg().getTimestamp(), |  | ||||||
|                     msg.getSigList(), |  | ||||||
|                     callback |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onFailure(Throwable t) { |  | ||||||
|             if (callback != null) |  | ||||||
|                 callback.onFailure(t); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private class ContinueBatchCallback implements FutureCallback<BatchIdentifier> { |  | ||||||
| 
 |  | ||||||
|         private final BulletinBoardMessage msg; |  | ||||||
|         private final int chunkSize; |  | ||||||
|         private final FutureCallback<Boolean> callback; |  | ||||||
| 
 |  | ||||||
|         public ContinueBatchCallback(BulletinBoardMessage msg, int chunkSize, FutureCallback<Boolean> callback) { |  | ||||||
|             this.msg = msg; |  | ||||||
|             this.chunkSize = chunkSize; |  | ||||||
|             this.callback = callback; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onSuccess(BatchIdentifier identifier) { |  | ||||||
| 
 |  | ||||||
|             List<BatchChunk> batchChunkList = BulletinBoardUtils.breakToBatch(msg, chunkSize); |  | ||||||
| 
 |  | ||||||
|             postBatchData( |  | ||||||
|                     identifier, |  | ||||||
|                     batchChunkList, |  | ||||||
|                     0, |  | ||||||
|                     new PostBatchDataCallback(msg, identifier, callback)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onFailure(Throwable t) { |  | ||||||
|             if (callback != null) |  | ||||||
|                 callback.onFailure(t); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public MessageID postAsBatch(BulletinBoardMessage msg, int chunkSize, FutureCallback<Boolean> callback) { |  | ||||||
| 
 |  | ||||||
|         beginBatch( |  | ||||||
|                 msg.getMsg().getTagList(), |  | ||||||
|                 new ContinueBatchCallback(msg, chunkSize, callback) |  | ||||||
|         ); |  | ||||||
| 
 |  | ||||||
|         digest.update(msg); |  | ||||||
| 
 |  | ||||||
|         return digest.digestAsMessageID(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private class BeginBatchCallback implements FutureCallback<Int64Value> { |  | ||||||
| 
 |  | ||||||
|         private final FutureCallback<BatchIdentifier> callback; |  | ||||||
| 
 |  | ||||||
|         public BeginBatchCallback(FutureCallback<BatchIdentifier> callback) { |  | ||||||
|             this.callback = callback; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onSuccess(Int64Value result) { |  | ||||||
|             callback.onSuccess(new SingleServerBatchIdentifier(result)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onFailure(Throwable t) { |  | ||||||
|             callback.onFailure(t); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void beginBatch(Iterable<String> tags, FutureCallback<BatchIdentifier> callback) { |  | ||||||
| 
 |  | ||||||
|         BeginBatchMessage beginBatchMessage = BeginBatchMessage.newBuilder() |  | ||||||
|                 .addAllTag(tags) |  | ||||||
|                 .build(); |  | ||||||
| 
 |  | ||||||
|         // Create worker with redundancy 1 and MAX_RETRIES retries
 |  | ||||||
|         SingleServerBeginBatchWorker worker = |  | ||||||
|                 new SingleServerBeginBatchWorker(dbAddress, beginBatchMessage, MAX_RETRIES); |  | ||||||
| 
 |  | ||||||
|         // Submit worker and create callback
 |  | ||||||
|         scheduleWorker(worker, new RetryCallback<>(worker, new BeginBatchCallback(callback))); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void postBatchData(BatchIdentifier batchIdentifier, List<BatchChunk> batchChunkList, |  | ||||||
|                           int startPosition, FutureCallback<Boolean> callback) throws IllegalArgumentException{ |  | ||||||
| 
 |  | ||||||
|         // Cast identifier to usable form
 |  | ||||||
| 
 |  | ||||||
|         if (!(batchIdentifier instanceof SingleServerBatchIdentifier)){ |  | ||||||
|             throw new IllegalArgumentException("Error: batch identifier supplied was not created by this class."); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         SingleServerBatchIdentifier identifier = (SingleServerBatchIdentifier) batchIdentifier; |  | ||||||
| 
 |  | ||||||
|         // Update batch size
 |  | ||||||
| 
 |  | ||||||
|         identifier.setLength(startPosition + batchChunkList.size()); |  | ||||||
| 
 |  | ||||||
|         // Create a unified callback to aggregate successful posts
 |  | ||||||
| 
 |  | ||||||
|         PostBatchChunkListCallback listCallback = new PostBatchChunkListCallback(batchChunkList.size(), callback); |  | ||||||
| 
 |  | ||||||
|         // Iterate through data list
 |  | ||||||
| 
 |  | ||||||
|         BatchMessage.Builder builder = BatchMessage.newBuilder() |  | ||||||
|                 .setBatchId(identifier.getBatchId().getValue()); |  | ||||||
| 
 |  | ||||||
|         for (BatchChunk data : batchChunkList) { |  | ||||||
|             builder.setSerialNum(startPosition).setData(data); |  | ||||||
| 
 |  | ||||||
|             // Create worker with redundancy 1 and MAX_RETRIES retries
 |  | ||||||
|             SingleServerPostBatchWorker worker = |  | ||||||
|                     new SingleServerPostBatchWorker(dbAddress, builder.build(), MAX_RETRIES); |  | ||||||
| 
 |  | ||||||
|             // Create worker with redundancy 1 and MAX_RETRIES retries
 |  | ||||||
|             scheduleWorker(worker, new RetryCallback<>(worker, listCallback)); |  | ||||||
| 
 |  | ||||||
|             // Increment position in batch
 |  | ||||||
|             startPosition++; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void postBatchData(BatchIdentifier batchIdentifier, List<BatchChunk> batchChunkList, FutureCallback<Boolean> callback) |  | ||||||
|         throws IllegalArgumentException { |  | ||||||
| 
 |  | ||||||
|         postBatchData(batchIdentifier, batchChunkList, 0, callback); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void closeBatch(BatchIdentifier batchIdentifier, Timestamp timestamp, Iterable<Crypto.Signature> signatures, FutureCallback<Boolean> callback) |  | ||||||
|             throws IllegalArgumentException { |  | ||||||
| 
 |  | ||||||
|         if (!(batchIdentifier instanceof SingleServerBatchIdentifier)){ |  | ||||||
|             throw new IllegalArgumentException("Error: batch identifier supplied was not created by this class."); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         SingleServerBatchIdentifier identifier = (SingleServerBatchIdentifier) batchIdentifier; |  | ||||||
| 
 |  | ||||||
|         CloseBatchMessage closeBatchMessage = CloseBatchMessage.newBuilder() |  | ||||||
|                 .setBatchId(identifier.getBatchId().getValue()) |  | ||||||
|                 .setBatchLength(identifier.getLength()) |  | ||||||
|                 .setTimestamp(timestamp) |  | ||||||
|                 .addAllSig(signatures) |  | ||||||
|                 .build(); |  | ||||||
| 
 |  | ||||||
|         // Create worker with redundancy 1 and MAX_RETRIES retries
 |  | ||||||
|         SingleServerCloseBatchWorker worker = |  | ||||||
|                 new SingleServerCloseBatchWorker(dbAddress, closeBatchMessage, MAX_RETRIES); |  | ||||||
| 
 |  | ||||||
|         // Submit worker and create callback
 |  | ||||||
|         scheduleWorker(worker, new RetryCallback<>(worker, callback)); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void getRedundancy(MessageID id, FutureCallback<Float> callback) { |  | ||||||
| 
 |  | ||||||
|         // Create worker with no retries
 |  | ||||||
|         SingleServerGetRedundancyWorker worker = new SingleServerGetRedundancyWorker(dbAddress, id, 1); |  | ||||||
| 
 |  | ||||||
|         // Submit job and create callback
 |  | ||||||
|         scheduleWorker(worker, new RetryCallback<>(worker, callback)); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void readMessages(MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback) { |  | ||||||
| 
 |  | ||||||
|         // Create job with no retries
 |  | ||||||
|         SingleServerReadMessagesWorker worker = new SingleServerReadMessagesWorker(dbAddress, filterList, 1); |  | ||||||
| 
 |  | ||||||
|         // Submit job and create callback
 |  | ||||||
|         scheduleWorker(worker, new RetryCallback<>(worker, callback)); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void readMessage(MessageID msgID, FutureCallback<BulletinBoardMessage> callback) { |  | ||||||
| 
 |  | ||||||
|         // Create job with MAX retries for retrieval of the Bulletin Board Message (which may be a stub)
 |  | ||||||
| 
 |  | ||||||
|         MessageFilterList filterList = MessageFilterList.newBuilder() |  | ||||||
|                 .addFilter(MessageFilter.newBuilder() |  | ||||||
|                         .setType(FilterType.MSG_ID) |  | ||||||
|                         .setId(msgID.getID()) |  | ||||||
|                         .build()) |  | ||||||
|                 .build(); |  | ||||||
| 
 |  | ||||||
|         BatchQuery batchQuery = BatchQuery.newBuilder() |  | ||||||
|                 .setMsgID(msgID) |  | ||||||
|                 .setStartPosition(0) |  | ||||||
|                 .build(); |  | ||||||
| 
 |  | ||||||
|         SingleServerReadMessagesWorker messageWorker = new SingleServerReadMessagesWorker(dbAddress, filterList, MAX_RETRIES); |  | ||||||
| 
 |  | ||||||
|         // Submit jobs with wrapped callbacks
 |  | ||||||
|         scheduleWorker(messageWorker, new RetryCallback<>(messageWorker, new CompleteMessageReadCallback(callback))); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void readBatchData(BulletinBoardMessage stub, FutureCallback<BulletinBoardMessage> callback) throws IllegalArgumentException{ |  | ||||||
| 
 |  | ||||||
|         if (stub.getMsg().getDataTypeCase() != UnsignedBulletinBoardMessage.DataTypeCase.MSGID) { |  | ||||||
|             throw new IllegalArgumentException("Message is not a stub and does not contain the required message ID"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // Create job with MAX retries for retrieval of the Batch Data List
 |  | ||||||
| 
 |  | ||||||
|         BatchQuery batchQuery = BatchQuery.newBuilder() |  | ||||||
|                 .setMsgID(MessageID.newBuilder() |  | ||||||
|                         .setID(stub.getMsg().getMsgId()) |  | ||||||
|                         .build()) |  | ||||||
|                 .setStartPosition(0) |  | ||||||
|                 .build(); |  | ||||||
| 
 |  | ||||||
|         SingleServerReadBatchWorker batchWorker = new SingleServerReadBatchWorker(dbAddress, batchQuery, MAX_RETRIES); |  | ||||||
| 
 |  | ||||||
|         scheduleWorker(batchWorker, new RetryCallback<>(batchWorker, new ReadBatchCallback(stub, callback))); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void querySync(SyncQuery syncQuery, FutureCallback<SyncQueryResponse> callback) { |  | ||||||
| 
 |  | ||||||
|         SingleServerQuerySyncWorker worker = new SingleServerQuerySyncWorker(dbAddress, syncQuery, MAX_RETRIES); |  | ||||||
| 
 |  | ||||||
|         scheduleWorker(worker, new RetryCallback<>(worker, callback)); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void subscribe(MessageFilterList filterList, long startEntry, FutureCallback<List<BulletinBoardMessage>> callback) { |  | ||||||
|         // Remove all existing MIN_ENTRY filters and create new one that starts at 0
 |  | ||||||
| 
 |  | ||||||
|         MessageFilterList.Builder filterListBuilder = filterList.toBuilder(); |  | ||||||
| 
 |  | ||||||
|         Iterator<MessageFilter> iterator = filterListBuilder.getFilterList().iterator(); |  | ||||||
|         while (iterator.hasNext()) { |  | ||||||
|             MessageFilter filter = iterator.next(); |  | ||||||
| 
 |  | ||||||
|             if (filter.getType() == FilterType.MIN_ENTRY){ |  | ||||||
|                 iterator.remove(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         filterListBuilder.addFilter(MessageFilter.newBuilder() |  | ||||||
|                 .setType(FilterType.MIN_ENTRY) |  | ||||||
|                 .setEntry(startEntry) |  | ||||||
|                 .build()); |  | ||||||
| 
 |  | ||||||
|         // Create job with no retries
 |  | ||||||
|         SingleServerReadMessagesWorker worker = new SingleServerReadMessagesWorker(dbAddress, filterListBuilder.build(), MAX_RETRIES); |  | ||||||
| 
 |  | ||||||
|         // Submit job and create callback that retries on failure and handles repeated subscription
 |  | ||||||
|         scheduleWorker(worker, new RetryCallback<>(worker, new SubscriptionCallback(worker, callback))); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void subscribe(MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback) { |  | ||||||
|         subscribe(filterList, 0, callback); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void close() { |  | ||||||
| 
 |  | ||||||
|         executorService.shutdown(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,39 +0,0 @@ | ||||||
| package meerkat.bulletinboard; |  | ||||||
| 
 |  | ||||||
| import meerkat.rest.ProtobufMessageBodyReader; |  | ||||||
| import meerkat.rest.ProtobufMessageBodyWriter; |  | ||||||
| 
 |  | ||||||
| import javax.ws.rs.client.Client; |  | ||||||
| import javax.ws.rs.client.ClientBuilder; |  | ||||||
| import java.util.concurrent.Callable; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 02-Jan-16. |  | ||||||
|  */ |  | ||||||
| public abstract class SingleServerWorker<IN, OUT> extends BulletinClientWorker<IN> implements Callable<OUT>{ |  | ||||||
| 
 |  | ||||||
|     // This resource enabled creation of a single Client per thread.
 |  | ||||||
|     protected static final ThreadLocal<Client> clientLocal = |  | ||||||
|             new ThreadLocal<Client> () { |  | ||||||
|                 @Override protected Client initialValue() { |  | ||||||
|                     Client client; |  | ||||||
|                     client = ClientBuilder.newClient(); |  | ||||||
|                     client.register(ProtobufMessageBodyReader.class); |  | ||||||
|                     client.register(ProtobufMessageBodyWriter.class); |  | ||||||
| 
 |  | ||||||
|                     return client; |  | ||||||
|                 } |  | ||||||
|             }; |  | ||||||
| 
 |  | ||||||
|     protected final String serverAddress; |  | ||||||
| 
 |  | ||||||
|     public SingleServerWorker(String serverAddress, IN payload, int maxRetry) { |  | ||||||
|         super(payload, maxRetry); |  | ||||||
|         this.serverAddress = serverAddress; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public String getServerAddress() { |  | ||||||
|         return serverAddress; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,286 +0,0 @@ | ||||||
| package meerkat.bulletinboard; |  | ||||||
| 
 |  | ||||||
| import com.google.common.util.concurrent.FutureCallback; |  | ||||||
| 
 |  | ||||||
| import com.google.protobuf.Timestamp; |  | ||||||
| import meerkat.bulletinboard.workers.multiserver.*; |  | ||||||
| import meerkat.protobuf.BulletinBoardApi.*; |  | ||||||
| import meerkat.protobuf.Crypto.Signature; |  | ||||||
| import meerkat.protobuf.Voting.*; |  | ||||||
| 
 |  | ||||||
| import java.util.ArrayList; |  | ||||||
| import java.util.List; |  | ||||||
| import java.util.concurrent.ExecutorService; |  | ||||||
| import java.util.concurrent.Executors; |  | ||||||
| import java.util.concurrent.TimeUnit; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 05-Dec-15. |  | ||||||
|  * Thread-based implementation of a Async Bulletin Board Client. |  | ||||||
|  * Features: |  | ||||||
|  * 1. Handles tasks concurrently. |  | ||||||
|  * 2. Retries submitting |  | ||||||
|  */ |  | ||||||
| public class ThreadedBulletinBoardClient extends SimpleBulletinBoardClient implements AsyncBulletinBoardClient { |  | ||||||
| 
 |  | ||||||
|     // Executor service for handling jobs
 |  | ||||||
|     private final static int JOBS_THREAD_NUM = 5; |  | ||||||
|     private ExecutorService executorService; |  | ||||||
| 
 |  | ||||||
|     // Per-server clients
 |  | ||||||
|     private List<SingleServerBulletinBoardClient> clients; |  | ||||||
| 
 |  | ||||||
|     private BulletinBoardDigest batchDigest; |  | ||||||
| 
 |  | ||||||
|     private final static int POST_MESSAGE_RETRY_NUM = 3; |  | ||||||
|     private final static int READ_MESSAGES_RETRY_NUM = 1; |  | ||||||
|     private final static int GET_REDUNDANCY_RETRY_NUM = 1; |  | ||||||
| 
 |  | ||||||
|     private final int SERVER_THREADPOOL_SIZE; |  | ||||||
|     private final long FAIL_DELAY; |  | ||||||
|     private final long SUBSCRIPTION_INTERVAL; |  | ||||||
| 
 |  | ||||||
|     private static final int DEFAULT_SERVER_THREADPOOL_SIZE = 5; |  | ||||||
|     private static final long DEFAULT_FAIL_DELAY = 5000; |  | ||||||
|     private static final long DEFAULT_SUBSCRIPTION_INTERVAL = 10000; |  | ||||||
| 
 |  | ||||||
|     private int minAbsoluteRedundancy; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     public ThreadedBulletinBoardClient(int serverThreadpoolSize, long failDelay, long subscriptionInterval) { |  | ||||||
|         SERVER_THREADPOOL_SIZE = serverThreadpoolSize; |  | ||||||
|         FAIL_DELAY = failDelay; |  | ||||||
|         SUBSCRIPTION_INTERVAL = subscriptionInterval; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public ThreadedBulletinBoardClient() { |  | ||||||
|         this(DEFAULT_SERVER_THREADPOOL_SIZE, DEFAULT_FAIL_DELAY, DEFAULT_SUBSCRIPTION_INTERVAL); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Stores database locations and initializes the web Client |  | ||||||
|      * Stores the required minimum redundancy. |  | ||||||
|      * Starts the Thread Pool. |  | ||||||
|      * @param clientParams contains the required information |  | ||||||
|      */ |  | ||||||
|     @Override |  | ||||||
|     public void init(BulletinBoardClientParams clientParams) { |  | ||||||
| 
 |  | ||||||
|         super.init(clientParams); |  | ||||||
| 
 |  | ||||||
|         batchDigest = new GenericBulletinBoardDigest(digest); |  | ||||||
| 
 |  | ||||||
|         minAbsoluteRedundancy = (int) (clientParams.getMinRedundancy() * (float) clientParams.getBulletinBoardAddressCount()); |  | ||||||
| 
 |  | ||||||
|         executorService = Executors.newFixedThreadPool(JOBS_THREAD_NUM); |  | ||||||
| 
 |  | ||||||
|         clients = new ArrayList<>(clientParams.getBulletinBoardAddressCount()); |  | ||||||
|         for (String address : clientParams.getBulletinBoardAddressList()){ |  | ||||||
| 
 |  | ||||||
|             SingleServerBulletinBoardClient client = |  | ||||||
|                     new SingleServerBulletinBoardClient(SERVER_THREADPOOL_SIZE, FAIL_DELAY, SUBSCRIPTION_INTERVAL); |  | ||||||
| 
 |  | ||||||
|             client.init(BulletinBoardClientParams.newBuilder() |  | ||||||
|                             .addBulletinBoardAddress(address) |  | ||||||
|                             .build()); |  | ||||||
| 
 |  | ||||||
|             clients.add(client); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Post message to all DBs |  | ||||||
|      * Retry failed DBs |  | ||||||
|      * @param msg is the message, |  | ||||||
|      * @return the message ID for later retrieval |  | ||||||
|      */ |  | ||||||
|     @Override |  | ||||||
|     public MessageID postMessage(BulletinBoardMessage msg, FutureCallback<Boolean> callback){ |  | ||||||
| 
 |  | ||||||
|         // Create job
 |  | ||||||
|         MultiServerPostMessageWorker worker = |  | ||||||
|                 new MultiServerPostMessageWorker(clients, minAbsoluteRedundancy, msg, POST_MESSAGE_RETRY_NUM, callback); |  | ||||||
| 
 |  | ||||||
|         // Submit job
 |  | ||||||
|         executorService.submit(worker); |  | ||||||
| 
 |  | ||||||
|         // Calculate the correct message ID and return it
 |  | ||||||
|         batchDigest.reset(); |  | ||||||
|         batchDigest.update(msg.getMsg()); |  | ||||||
|         return batchDigest.digestAsMessageID(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public MessageID postAsBatch(BulletinBoardMessage msg, int chunkSize, FutureCallback<Boolean> callback) { |  | ||||||
| 
 |  | ||||||
|         // Create job
 |  | ||||||
|         MultiServerPostBatchWorker worker = |  | ||||||
|                 new MultiServerPostBatchWorker(clients, minAbsoluteRedundancy, msg, chunkSize, POST_MESSAGE_RETRY_NUM, callback); |  | ||||||
| 
 |  | ||||||
|         // Submit job
 |  | ||||||
|         executorService.submit(worker); |  | ||||||
| 
 |  | ||||||
|         // Calculate the correct message ID and return it
 |  | ||||||
|         batchDigest.reset(); |  | ||||||
|         batchDigest.update(msg); |  | ||||||
|         return batchDigest.digestAsMessageID(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void beginBatch(Iterable<String> tags, FutureCallback<BatchIdentifier> callback) { |  | ||||||
| 
 |  | ||||||
|         // Create job
 |  | ||||||
|         MultiServerBeginBatchWorker worker = |  | ||||||
|                 new MultiServerBeginBatchWorker(clients, minAbsoluteRedundancy, tags, POST_MESSAGE_RETRY_NUM, callback); |  | ||||||
| 
 |  | ||||||
|         // Submit job
 |  | ||||||
|         executorService.submit(worker); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void postBatchData(BatchIdentifier batchIdentifier, List<BatchChunk> batchChunkList, |  | ||||||
|                               int startPosition, FutureCallback<Boolean> callback) throws IllegalArgumentException { |  | ||||||
| 
 |  | ||||||
|         // Cast identifier to usable form
 |  | ||||||
| 
 |  | ||||||
|         if (!(batchIdentifier instanceof MultiServerBatchIdentifier)){ |  | ||||||
|             throw new IllegalArgumentException("Error: batch identifier supplied was not created by this class."); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         MultiServerBatchIdentifier identifier = (MultiServerBatchIdentifier) batchIdentifier; |  | ||||||
| 
 |  | ||||||
|         BatchDataContainer batchDataContainer = new BatchDataContainer(identifier, batchChunkList, startPosition); |  | ||||||
| 
 |  | ||||||
|         // Create job
 |  | ||||||
|         MultiServerPostBatchDataWorker worker = |  | ||||||
|                 new MultiServerPostBatchDataWorker(clients, minAbsoluteRedundancy, batchDataContainer, POST_MESSAGE_RETRY_NUM, callback); |  | ||||||
| 
 |  | ||||||
|         // Submit job
 |  | ||||||
|         executorService.submit(worker); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void postBatchData(BatchIdentifier batchIdentifier, List<BatchChunk> batchChunkList, FutureCallback<Boolean> callback) |  | ||||||
|             throws IllegalArgumentException { |  | ||||||
| 
 |  | ||||||
|         postBatchData(batchIdentifier, batchChunkList, 0, callback); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void closeBatch(BatchIdentifier payload, Timestamp timestamp, Iterable<Signature> signatures, FutureCallback<Boolean> callback) |  | ||||||
|         throws IllegalArgumentException{ |  | ||||||
| 
 |  | ||||||
|         if (!(payload instanceof MultiServerBatchIdentifier)) { |  | ||||||
|             throw new IllegalArgumentException("Error: batch identifier supplied was not created by this class."); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         MultiServerBatchIdentifier identifier = (MultiServerBatchIdentifier) payload; |  | ||||||
| 
 |  | ||||||
|         // Create job
 |  | ||||||
|         MultiServerCloseBatchWorker worker = |  | ||||||
|                 new MultiServerCloseBatchWorker(clients, minAbsoluteRedundancy, identifier, timestamp, signatures, POST_MESSAGE_RETRY_NUM, callback); |  | ||||||
| 
 |  | ||||||
|         // Submit job
 |  | ||||||
|         executorService.submit(worker); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Access each database and search for a given message ID |  | ||||||
|      * Return the number of databases in which the message was found |  | ||||||
|      * Only try once per DB |  | ||||||
|      * Ignore communication exceptions in specific databases |  | ||||||
|      */ |  | ||||||
|     @Override |  | ||||||
|     public void getRedundancy(MessageID id, FutureCallback<Float> callback) { |  | ||||||
| 
 |  | ||||||
|         // Create job
 |  | ||||||
|         MultiServerGetRedundancyWorker worker = |  | ||||||
|                 new MultiServerGetRedundancyWorker(clients, minAbsoluteRedundancy, id, GET_REDUNDANCY_RETRY_NUM, callback); |  | ||||||
| 
 |  | ||||||
|         // Submit job
 |  | ||||||
|         executorService.submit(worker); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Go through the DBs and try to retrieve messages according to the specified filter |  | ||||||
|      * If at the operation is successful for some DB: return the results and stop iterating |  | ||||||
|      * If no operation is successful: return null (NOT blank list) |  | ||||||
|      */ |  | ||||||
|     @Override |  | ||||||
|     public void readMessages(MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback) { |  | ||||||
| 
 |  | ||||||
|         // Create job
 |  | ||||||
|         MultiServerReadMessagesWorker worker = |  | ||||||
|                 new MultiServerReadMessagesWorker(clients, minAbsoluteRedundancy, filterList, READ_MESSAGES_RETRY_NUM, callback); |  | ||||||
| 
 |  | ||||||
|         // Submit job
 |  | ||||||
|         executorService.submit(worker); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void readMessage(MessageID msgID, FutureCallback<BulletinBoardMessage> callback) { |  | ||||||
| 
 |  | ||||||
|         //Create job
 |  | ||||||
|         MultiServerReadMessageWorker worker = |  | ||||||
|                 new MultiServerReadMessageWorker(clients, minAbsoluteRedundancy, msgID, READ_MESSAGES_RETRY_NUM, callback); |  | ||||||
| 
 |  | ||||||
|         // Submit job
 |  | ||||||
|         executorService.submit(worker); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void readBatchData(BulletinBoardMessage stub, FutureCallback<BulletinBoardMessage> callback) throws IllegalArgumentException { |  | ||||||
| 
 |  | ||||||
|         if (stub.getMsg().getDataTypeCase() != UnsignedBulletinBoardMessage.DataTypeCase.MSGID) { |  | ||||||
|             throw new IllegalArgumentException("Message is not a stub and does not contain the required message ID"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // Create job
 |  | ||||||
|         MultiServerReadBatchDataWorker worker = |  | ||||||
|                 new MultiServerReadBatchDataWorker(clients, minAbsoluteRedundancy, stub, READ_MESSAGES_RETRY_NUM, callback); |  | ||||||
| 
 |  | ||||||
|         // Submit job
 |  | ||||||
|         executorService.submit(worker); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * This method is not supported by this class! |  | ||||||
|      * This is because it has no meaning when considering more than one server without knowing which server will be contacted |  | ||||||
|      */ |  | ||||||
|     @Override |  | ||||||
|     public void querySync(SyncQuery syncQuery, FutureCallback<SyncQueryResponse> callback) { |  | ||||||
|         callback.onFailure(new IllegalAccessError("querySync is not supported by this class")); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void close() { |  | ||||||
|         super.close(); |  | ||||||
| 
 |  | ||||||
|         try { |  | ||||||
| 
 |  | ||||||
|             for (SingleServerBulletinBoardClient client : clients){ |  | ||||||
|                 client.close(); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             executorService.shutdown(); |  | ||||||
|             while (! executorService.isShutdown()) { |  | ||||||
|                 executorService.awaitTermination(10, TimeUnit.SECONDS); |  | ||||||
|             } |  | ||||||
|         } catch (InterruptedException e) { |  | ||||||
|             System.err.println(e.getCause() + " " + e.getMessage()); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,276 +0,0 @@ | ||||||
| package meerkat.bulletinboard; |  | ||||||
| 
 |  | ||||||
| import com.google.common.util.concurrent.FutureCallback; |  | ||||||
| import com.google.protobuf.Timestamp; |  | ||||||
| import meerkat.comm.CommunicationException; |  | ||||||
| import meerkat.protobuf.BulletinBoardApi.*; |  | ||||||
| import meerkat.util.BulletinBoardUtils; |  | ||||||
| 
 |  | ||||||
| import static meerkat.protobuf.BulletinBoardApi.FilterType.*; |  | ||||||
| 
 |  | ||||||
| import java.util.*; |  | ||||||
| import java.util.concurrent.Semaphore; |  | ||||||
| import java.util.concurrent.atomic.AtomicBoolean; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 03-Mar-16. |  | ||||||
|  * A multi-server implementation of the {@link BulletinBoardSubscriber} |  | ||||||
|  */ |  | ||||||
| public class ThreadedBulletinBoardSubscriber implements BulletinBoardSubscriber { |  | ||||||
| 
 |  | ||||||
|     protected final Collection<SubscriptionBulletinBoardClient> clients; |  | ||||||
|     protected final BulletinBoardClient localClient; |  | ||||||
| 
 |  | ||||||
|     protected Iterator<SubscriptionBulletinBoardClient> clientIterator; |  | ||||||
|     protected SubscriptionBulletinBoardClient currentClient; |  | ||||||
| 
 |  | ||||||
|     private long lastServerSwitchTime; |  | ||||||
| 
 |  | ||||||
|     private AtomicBoolean isSyncInProgress; |  | ||||||
|     private Semaphore rescheduleSemaphore; |  | ||||||
| 
 |  | ||||||
|     private AtomicBoolean stopped; |  | ||||||
| 
 |  | ||||||
|     private static final Float[] BREAKPOINTS = {0.5f, 0.75f, 0.9f, 0.95f, 0.99f, 0.999f}; |  | ||||||
| 
 |  | ||||||
|     public ThreadedBulletinBoardSubscriber(Collection<SubscriptionBulletinBoardClient> clients, BulletinBoardClient localClient) { |  | ||||||
| 
 |  | ||||||
|         this.clients = clients; |  | ||||||
|         this.localClient = localClient; |  | ||||||
| 
 |  | ||||||
|         lastServerSwitchTime = System.currentTimeMillis(); |  | ||||||
| 
 |  | ||||||
|         clientIterator = clients.iterator(); |  | ||||||
|         currentClient = clientIterator.next(); |  | ||||||
| 
 |  | ||||||
|         isSyncInProgress = new AtomicBoolean(false); |  | ||||||
|         rescheduleSemaphore = new Semaphore(1); |  | ||||||
| 
 |  | ||||||
|         stopped = new AtomicBoolean(false); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Moves to next client and performs resync with it |  | ||||||
|      */ |  | ||||||
|     private void nextClient() { |  | ||||||
| 
 |  | ||||||
|         try { |  | ||||||
| 
 |  | ||||||
|             rescheduleSemaphore.acquire(); |  | ||||||
| 
 |  | ||||||
|             if (!clientIterator.hasNext()){ |  | ||||||
|                 clientIterator = clients.iterator(); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             currentClient = clientIterator.next(); |  | ||||||
| 
 |  | ||||||
|             lastServerSwitchTime = System.currentTimeMillis(); |  | ||||||
| 
 |  | ||||||
|             isSyncInProgress.set(false); |  | ||||||
| 
 |  | ||||||
|             rescheduleSemaphore.release(); |  | ||||||
| 
 |  | ||||||
|         } catch (InterruptedException e) { |  | ||||||
|             // TODO: log
 |  | ||||||
|             // Do not change client
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private abstract class SubscriberCallback<T> implements FutureCallback<T> { |  | ||||||
| 
 |  | ||||||
|         protected final MessageFilterList filterList; |  | ||||||
|         protected final FutureCallback<List<BulletinBoardMessage>> callback; |  | ||||||
|         private final long invocationTime; |  | ||||||
| 
 |  | ||||||
|         public SubscriberCallback(MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback) { |  | ||||||
| 
 |  | ||||||
|             this.filterList = filterList; |  | ||||||
|             this.callback = callback; |  | ||||||
|             this.invocationTime = System.currentTimeMillis(); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         /** |  | ||||||
|          * Handles resyncing process for the given subscription after a server is switched |  | ||||||
|          * Specifically: generates a sync query from the local database and uses it to query the current server |  | ||||||
|          */ |  | ||||||
|         private void reSync() { |  | ||||||
| 
 |  | ||||||
|             SyncQuery syncQuery = null; |  | ||||||
|             try { |  | ||||||
| 
 |  | ||||||
|                 syncQuery = localClient.generateSyncQuery(GenerateSyncQueryParams.newBuilder() |  | ||||||
|                         .setFilterList(filterList) |  | ||||||
|                         .addAllBreakpointList(Arrays.asList(BREAKPOINTS)) |  | ||||||
|                         .build()); |  | ||||||
| 
 |  | ||||||
|             } catch (CommunicationException e) { |  | ||||||
| 
 |  | ||||||
|                 // Handle failure in standard way
 |  | ||||||
|                 onFailure(e); |  | ||||||
| 
 |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             currentClient.querySync(syncQuery, new SyncQueryCallback(filterList, callback)); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         /** |  | ||||||
|          * Reschedules the subscription |  | ||||||
|          */ |  | ||||||
|         private void reschedule() { |  | ||||||
| 
 |  | ||||||
|             try { |  | ||||||
| 
 |  | ||||||
|                 rescheduleSemaphore.acquire(); |  | ||||||
| 
 |  | ||||||
|                 reSync(); |  | ||||||
| 
 |  | ||||||
|                 rescheduleSemaphore.release(); |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|             } catch (InterruptedException e) { |  | ||||||
| 
 |  | ||||||
|                 //TODO: log
 |  | ||||||
| 
 |  | ||||||
|                 if (callback != null) |  | ||||||
|                     callback.onFailure(e); // Hard error: Cannot guarantee subscription safety
 |  | ||||||
| 
 |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onFailure(Throwable t) { |  | ||||||
| 
 |  | ||||||
|             // If server failure is not already known: switch to next client and resync
 |  | ||||||
|             if (invocationTime > lastServerSwitchTime){ |  | ||||||
| 
 |  | ||||||
|                 // Make sure only what thread switches the client
 |  | ||||||
|                 if (isSyncInProgress.compareAndSet(false, true)){ |  | ||||||
|                     nextClient(); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             reschedule(); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Provides handling logic for resync query callback operation |  | ||||||
|      * Receives a SyncQueryResponse and reads the missing data (starting from the received timestamp) if needed |  | ||||||
|      */ |  | ||||||
|     protected class SyncQueryCallback extends SubscriberCallback<SyncQueryResponse> { |  | ||||||
| 
 |  | ||||||
|         public SyncQueryCallback (MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback) { |  | ||||||
| 
 |  | ||||||
|             super(filterList, callback); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onSuccess(SyncQueryResponse result) { |  | ||||||
| 
 |  | ||||||
|             final Timestamp DEFAULT_TIME = BulletinBoardUtils.toTimestampProto(946728000); // Year 2000
 |  | ||||||
| 
 |  | ||||||
|             // Read required messages according to received Timestamp
 |  | ||||||
| 
 |  | ||||||
|             Timestamp syncTimestamp; |  | ||||||
| 
 |  | ||||||
|             if (result.hasLastTimeOfSync()) { |  | ||||||
|                 syncTimestamp = result.getLastTimeOfSync();                         // Use returned time of sync
 |  | ||||||
|             } else { |  | ||||||
|                 syncTimestamp = DEFAULT_TIME;                                       // Get all messages
 |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             MessageFilterList timestampedFilterList = filterList.toBuilder() |  | ||||||
|                     .removeFilter(filterList.getFilterCount()-1)                    // Remove MIN_ENTRY filter
 |  | ||||||
|                     .addFilter(MessageFilter.newBuilder()                           // Add timestamp filter
 |  | ||||||
|                             .setType(AFTER_TIME) |  | ||||||
|                             .setTimestamp(syncTimestamp) |  | ||||||
|                             .build()) |  | ||||||
|                     .build(); |  | ||||||
| 
 |  | ||||||
|             currentClient.readMessages(timestampedFilterList, new ReSyncCallback(filterList, callback, result.getLastEntryNum())); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Provides handling logic for callback of resyncing process |  | ||||||
|      * Receives the missing messages, handles them and resubscribes |  | ||||||
|      */ |  | ||||||
|     protected class ReSyncCallback extends SubscriberCallback<List<BulletinBoardMessage>> { |  | ||||||
| 
 |  | ||||||
|         private long minEntry; |  | ||||||
| 
 |  | ||||||
|         public ReSyncCallback (MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback, long minEntry) { |  | ||||||
| 
 |  | ||||||
|             super(filterList, callback); |  | ||||||
| 
 |  | ||||||
|             this.minEntry = minEntry; |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onSuccess(List<BulletinBoardMessage> result) { |  | ||||||
| 
 |  | ||||||
|             // Propagate result to caller
 |  | ||||||
|             if (callback != null) |  | ||||||
|                 callback.onSuccess(result); |  | ||||||
| 
 |  | ||||||
|             // Renew subscription
 |  | ||||||
| 
 |  | ||||||
|             MessageFilterList newFilterList = filterList.toBuilder() |  | ||||||
|                     .removeFilter(filterList.getFilterCount()-1)        // Remove current MIN_ENTRY filter
 |  | ||||||
|                     .addFilter(MessageFilter.newBuilder()               // Add new MIN_ENTRY filter for current server
 |  | ||||||
|                             .setType(MIN_ENTRY) |  | ||||||
|                             .setEntry(minEntry) |  | ||||||
|                             .build()) |  | ||||||
|                     .build(); |  | ||||||
| 
 |  | ||||||
|             currentClient.subscribe(newFilterList, callback); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Provides the handling logic for results and failures of main subscription (while there are no errors) |  | ||||||
|      */ |  | ||||||
|     protected class SubscriptionCallback extends SubscriberCallback<List<BulletinBoardMessage>> { |  | ||||||
| 
 |  | ||||||
|         public SubscriptionCallback(MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback){ |  | ||||||
|             super(filterList, callback); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onSuccess(List<BulletinBoardMessage> result) { |  | ||||||
| 
 |  | ||||||
|             // Propagate result to caller
 |  | ||||||
|             if (callback != null) |  | ||||||
|                 callback.onSuccess(result); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void subscribe(MessageFilterList filterList, long startEntry, FutureCallback<List<BulletinBoardMessage>> callback) { |  | ||||||
| 
 |  | ||||||
|         currentClient.subscribe(filterList, startEntry, new SubscriptionCallback(filterList, callback)); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void subscribe(MessageFilterList filterList, FutureCallback<List<BulletinBoardMessage>> callback) { |  | ||||||
|         subscribe(filterList, 0, callback); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,96 +0,0 @@ | ||||||
| package meerkat.bulletinboard.workers.multiserver; |  | ||||||
| 
 |  | ||||||
| import com.google.common.util.concurrent.FutureCallback; |  | ||||||
| import meerkat.bulletinboard.MultiServerBatchIdentifier; |  | ||||||
| import meerkat.bulletinboard.MultiServerWorker; |  | ||||||
| import meerkat.bulletinboard.SingleServerBulletinBoardClient; |  | ||||||
| import meerkat.bulletinboard.AsyncBulletinBoardClient.BatchIdentifier; |  | ||||||
| import meerkat.comm.CommunicationException; |  | ||||||
| 
 |  | ||||||
| import java.util.List; |  | ||||||
| import java.util.concurrent.atomic.AtomicInteger; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 27-Dec-15. |  | ||||||
|  */ |  | ||||||
| public class MultiServerBeginBatchWorker extends MultiServerWorker<Iterable<String>, BatchIdentifier> { |  | ||||||
| 
 |  | ||||||
|     private BatchIdentifier[] identifiers; |  | ||||||
|     private AtomicInteger remainingServers; |  | ||||||
| 
 |  | ||||||
|     public MultiServerBeginBatchWorker(List<SingleServerBulletinBoardClient> clients, |  | ||||||
|                                        int minServers, Iterable<String> payload, int maxRetry, |  | ||||||
|                                        FutureCallback<BatchIdentifier> futureCallback) { |  | ||||||
| 
 |  | ||||||
|         super(clients, minServers, payload, maxRetry, futureCallback); |  | ||||||
| 
 |  | ||||||
|         identifiers = new BatchIdentifier[clients.size()]; |  | ||||||
| 
 |  | ||||||
|         for (int i = 0 ; i < identifiers.length ; i++) { |  | ||||||
|             identifiers[i] = null; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         remainingServers = new AtomicInteger(clients.size()); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private class BeginBatchCallback implements FutureCallback<BatchIdentifier> { |  | ||||||
| 
 |  | ||||||
|         private final int clientNum; |  | ||||||
| 
 |  | ||||||
|         public BeginBatchCallback(int clientNum) { |  | ||||||
|             this.clientNum = clientNum; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         private void finishPost() { |  | ||||||
| 
 |  | ||||||
|             if (remainingServers.decrementAndGet() <= 0){ |  | ||||||
| 
 |  | ||||||
|                 if (minServers.decrementAndGet() <= 0) { |  | ||||||
|                     MultiServerBeginBatchWorker.this.onSuccess(new MultiServerBatchIdentifier(identifiers)); |  | ||||||
|                 } else { |  | ||||||
|                     MultiServerBeginBatchWorker.this.onFailure(new CommunicationException("Could not open batch in enough servers")); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onSuccess(BatchIdentifier result) { |  | ||||||
| 
 |  | ||||||
|             identifiers[clientNum] = result; |  | ||||||
|             finishPost(); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onFailure(Throwable t) { |  | ||||||
|             finishPost(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void onSuccess(BatchIdentifier result) { |  | ||||||
|         succeed(result); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void onFailure(Throwable t) { |  | ||||||
|         fail(t); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void run() { |  | ||||||
| 
 |  | ||||||
|         int clientNum = 0; |  | ||||||
| 
 |  | ||||||
|         for (SingleServerBulletinBoardClient client : clients){ |  | ||||||
| 
 |  | ||||||
|             client.beginBatch(payload, new BeginBatchCallback(clientNum)); |  | ||||||
| 
 |  | ||||||
|             clientNum++; |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,84 +0,0 @@ | ||||||
| package meerkat.bulletinboard.workers.multiserver; |  | ||||||
| 
 |  | ||||||
| import com.google.common.util.concurrent.FutureCallback; |  | ||||||
| import com.google.protobuf.Timestamp; |  | ||||||
| import meerkat.bulletinboard.AsyncBulletinBoardClient.BatchIdentifier; |  | ||||||
| import meerkat.bulletinboard.BatchDataContainer; |  | ||||||
| import meerkat.bulletinboard.MultiServerBatchIdentifier; |  | ||||||
| import meerkat.bulletinboard.MultiServerWorker; |  | ||||||
| import meerkat.bulletinboard.SingleServerBulletinBoardClient; |  | ||||||
| import meerkat.crypto.DigitalSignature; |  | ||||||
| import meerkat.protobuf.Crypto; |  | ||||||
| import meerkat.protobuf.Crypto.Signature; |  | ||||||
| 
 |  | ||||||
| import java.util.Iterator; |  | ||||||
| import java.util.List; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 27-Dec-15. |  | ||||||
|  */ |  | ||||||
| public class MultiServerCloseBatchWorker extends MultiServerWorker<MultiServerBatchIdentifier, Boolean> { |  | ||||||
| 
 |  | ||||||
|     private final Timestamp timestamp; |  | ||||||
|     private final Iterable<Crypto.Signature> signatures; |  | ||||||
| 
 |  | ||||||
|     public MultiServerCloseBatchWorker(List<SingleServerBulletinBoardClient> clients, |  | ||||||
|                                        int minServers, MultiServerBatchIdentifier payload, Timestamp timestamp, Iterable<Crypto.Signature> signatures, |  | ||||||
|                                        int maxRetry, FutureCallback<Boolean> futureCallback) { |  | ||||||
| 
 |  | ||||||
|         super(clients, minServers, payload, maxRetry, futureCallback); |  | ||||||
| 
 |  | ||||||
|         this.timestamp = timestamp; |  | ||||||
|         this.signatures = signatures; |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void run() { |  | ||||||
| 
 |  | ||||||
|         Iterator<BatchIdentifier> identifierIterator = payload.getIdentifiers().iterator(); |  | ||||||
| 
 |  | ||||||
|         // Iterate through client
 |  | ||||||
| 
 |  | ||||||
|         for (SingleServerBulletinBoardClient client : clients) { |  | ||||||
| 
 |  | ||||||
|             if (identifierIterator.hasNext()) { |  | ||||||
| 
 |  | ||||||
|                 // Fetch the batch identifier supplied by the specific client (may be null if batch open failed on client
 |  | ||||||
| 
 |  | ||||||
|                 BatchIdentifier identifier = identifierIterator.next(); |  | ||||||
| 
 |  | ||||||
|                 if (identifier != null) { |  | ||||||
| 
 |  | ||||||
|                     // Post the data with the matching identifier to the client
 |  | ||||||
|                     client.closeBatch(identifier, timestamp, signatures, this); |  | ||||||
| 
 |  | ||||||
|                 } else { |  | ||||||
| 
 |  | ||||||
|                     // Count servers with no batch identifier as failed
 |  | ||||||
|                     maxFailedServers.decrementAndGet(); |  | ||||||
| 
 |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void onSuccess(Boolean result) { |  | ||||||
|         if (minServers.decrementAndGet() <= 0){ |  | ||||||
|             succeed(result); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void onFailure(Throwable t) { |  | ||||||
|         if (maxFailedServers.decrementAndGet() <= 0){ |  | ||||||
|             fail(t); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,62 +0,0 @@ | ||||||
| package meerkat.bulletinboard.workers.multiserver; |  | ||||||
| 
 |  | ||||||
| import com.google.common.util.concurrent.FutureCallback; |  | ||||||
| import com.google.common.util.concurrent.FutureCallback; |  | ||||||
| import meerkat.bulletinboard.MultiServerWorker; |  | ||||||
| import meerkat.bulletinboard.SingleServerBulletinBoardClient; |  | ||||||
| import meerkat.comm.CommunicationException; |  | ||||||
| 
 |  | ||||||
| import javax.ws.rs.client.WebTarget; |  | ||||||
| import javax.ws.rs.core.Response; |  | ||||||
| import java.util.Iterator; |  | ||||||
| import java.util.List; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 27-Dec-15. |  | ||||||
|  */ |  | ||||||
| public abstract class MultiServerGenericPostWorker<T> extends MultiServerWorker<T, Boolean> { |  | ||||||
| 
 |  | ||||||
|     public MultiServerGenericPostWorker(List<SingleServerBulletinBoardClient> clients, |  | ||||||
|                                         int minServers, T payload, int maxRetry, |  | ||||||
|                                         FutureCallback<Boolean> futureCallback) { |  | ||||||
| 
 |  | ||||||
|         super(clients, minServers, payload, maxRetry, futureCallback); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     protected abstract void doPost(SingleServerBulletinBoardClient client, T payload); |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * This method carries out the actual communication with the servers via HTTP Post |  | ||||||
|      * It accesses the servers one by one and tries to post the payload to each in turn |  | ||||||
|      * The method will only iterate once through the server list |  | ||||||
|      * Successful post to a server results in removing the server from the list |  | ||||||
|      */ |  | ||||||
|     public void run() { |  | ||||||
| 
 |  | ||||||
|         // Iterate through servers
 |  | ||||||
|         for (SingleServerBulletinBoardClient client : clients) { |  | ||||||
| 
 |  | ||||||
|             // Send request to Server
 |  | ||||||
|             doPost(client, payload); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void onSuccess(Boolean result) { |  | ||||||
|         if (result){ |  | ||||||
|             if (minServers.decrementAndGet() <= 0){ |  | ||||||
|                 succeed(Boolean.TRUE); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void onFailure(Throwable t) { |  | ||||||
|         if (maxFailedServers.decrementAndGet() < 0){ |  | ||||||
|             fail(t); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,69 +0,0 @@ | ||||||
| package meerkat.bulletinboard.workers.multiserver; |  | ||||||
| 
 |  | ||||||
| import com.google.common.util.concurrent.FutureCallback; |  | ||||||
| import meerkat.bulletinboard.MultiServerWorker; |  | ||||||
| import meerkat.bulletinboard.SingleServerBulletinBoardClient; |  | ||||||
| import meerkat.comm.CommunicationException; |  | ||||||
| 
 |  | ||||||
| import java.util.Iterator; |  | ||||||
| import java.util.List; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 27-Dec-15. |  | ||||||
|  */ |  | ||||||
| public abstract class MultiServerGenericReadWorker<IN, OUT> extends MultiServerWorker<IN, OUT>{ |  | ||||||
| 
 |  | ||||||
|     private Iterator<SingleServerBulletinBoardClient> clientIterator; |  | ||||||
| 
 |  | ||||||
|     private String errorString; |  | ||||||
| 
 |  | ||||||
|     public MultiServerGenericReadWorker(List<SingleServerBulletinBoardClient> clients, |  | ||||||
|                                         int minServers, IN payload, int maxRetry, |  | ||||||
|                                         FutureCallback<OUT> futureCallback) { |  | ||||||
| 
 |  | ||||||
|         super(clients, true, minServers, payload, maxRetry, futureCallback); // Shuffle clients on creation to balance load
 |  | ||||||
| 
 |  | ||||||
|         clientIterator = clients.iterator(); |  | ||||||
|         errorString = ""; |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     protected abstract void doRead(IN payload, SingleServerBulletinBoardClient client); |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * This method carries out the actual communication with the servers via HTTP Post |  | ||||||
|      * It accesses the servers in a random order until one answers it |  | ||||||
|      * Successful retrieval from any server terminates the method and returns the received values; The list is not changed |  | ||||||
|      */ |  | ||||||
|     public void run(){ |  | ||||||
| 
 |  | ||||||
|         // Iterate through servers
 |  | ||||||
| 
 |  | ||||||
|         if (clientIterator.hasNext()) { |  | ||||||
| 
 |  | ||||||
|             // Get next server
 |  | ||||||
|             SingleServerBulletinBoardClient client = clientIterator.next(); |  | ||||||
| 
 |  | ||||||
|             // Retrieve answer from server
 |  | ||||||
|             doRead(payload, client); |  | ||||||
| 
 |  | ||||||
|         } else { |  | ||||||
|             fail(new CommunicationException("Could not contact any server. Errors follow:\n" + errorString)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void onSuccess(OUT msg) { |  | ||||||
|         succeed(msg); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void onFailure(Throwable t) { |  | ||||||
|         //TODO: log
 |  | ||||||
|         errorString += t.getCause() + " " + t.getMessage() + "\n"; |  | ||||||
|         run(); // Retry with next server
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,67 +0,0 @@ | ||||||
| package meerkat.bulletinboard.workers.multiserver; |  | ||||||
| 
 |  | ||||||
| import com.google.common.util.concurrent.FutureCallback; |  | ||||||
| import meerkat.bulletinboard.MultiServerWorker; |  | ||||||
| import meerkat.bulletinboard.SingleServerBulletinBoardClient; |  | ||||||
| import meerkat.comm.CommunicationException; |  | ||||||
| import meerkat.protobuf.BulletinBoardApi.*; |  | ||||||
| 
 |  | ||||||
| import java.util.Iterator; |  | ||||||
| import java.util.List; |  | ||||||
| import java.util.concurrent.atomic.AtomicInteger; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 27-Dec-15. |  | ||||||
|  */ |  | ||||||
| public class MultiServerGetRedundancyWorker extends MultiServerWorker<MessageID, Float> { |  | ||||||
| 
 |  | ||||||
|     private AtomicInteger serversContainingMessage; |  | ||||||
|     private AtomicInteger totalContactedServers; |  | ||||||
| 
 |  | ||||||
|     public MultiServerGetRedundancyWorker(List<SingleServerBulletinBoardClient> clients, |  | ||||||
|                                          int minServers, MessageID payload, int maxRetry, |  | ||||||
|                                          FutureCallback<Float> futureCallback) { |  | ||||||
| 
 |  | ||||||
|         super(clients, minServers, payload, maxRetry, futureCallback); // Shuffle clients on creation to balance load
 |  | ||||||
| 
 |  | ||||||
|         serversContainingMessage = new AtomicInteger(0); |  | ||||||
|         totalContactedServers = new AtomicInteger(0); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * This method carries out the actual communication with the servers via HTTP Post |  | ||||||
|      * It accesses the servers in a random order until one answers it |  | ||||||
|      * Successful retrieval from any server terminates the method and returns the received values; The list is not changed |  | ||||||
|      */ |  | ||||||
|     public void run(){ |  | ||||||
| 
 |  | ||||||
|         // Iterate through clients
 |  | ||||||
|         for (SingleServerBulletinBoardClient client : clients) { |  | ||||||
| 
 |  | ||||||
|             // Send request to client
 |  | ||||||
|             client.getRedundancy(payload,this); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void onSuccess(Float result) { |  | ||||||
| 
 |  | ||||||
|         if (result > 0.5) { |  | ||||||
|             serversContainingMessage.incrementAndGet(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (totalContactedServers.incrementAndGet() >= getClientNumber()){ |  | ||||||
|             succeed(((float) serversContainingMessage.get()) / ((float) getClientNumber())); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void onFailure(Throwable t) { |  | ||||||
|         onSuccess(0.0f); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,72 +0,0 @@ | ||||||
| package meerkat.bulletinboard.workers.multiserver; |  | ||||||
| 
 |  | ||||||
| import com.google.common.util.concurrent.FutureCallback; |  | ||||||
| import meerkat.bulletinboard.AsyncBulletinBoardClient.BatchIdentifier; |  | ||||||
| import meerkat.bulletinboard.MultiServerWorker; |  | ||||||
| import meerkat.bulletinboard.SingleServerBulletinBoardClient; |  | ||||||
| import meerkat.bulletinboard.BatchDataContainer; |  | ||||||
| 
 |  | ||||||
| import java.util.Iterator; |  | ||||||
| import java.util.List; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 27-Dec-15. |  | ||||||
|  */ |  | ||||||
| public class MultiServerPostBatchDataWorker extends MultiServerWorker<BatchDataContainer, Boolean> { |  | ||||||
| 
 |  | ||||||
|     public MultiServerPostBatchDataWorker(List<SingleServerBulletinBoardClient> clients, |  | ||||||
|                                           int minServers, BatchDataContainer payload, int maxRetry, |  | ||||||
|                                           FutureCallback<Boolean> futureCallback) { |  | ||||||
| 
 |  | ||||||
|         super(clients, minServers, payload, maxRetry, futureCallback); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void run() { |  | ||||||
| 
 |  | ||||||
|         Iterator<BatchIdentifier> identifierIterator = payload.batchId.getIdentifiers().iterator(); |  | ||||||
| 
 |  | ||||||
|         // Iterate through client
 |  | ||||||
| 
 |  | ||||||
|         for (SingleServerBulletinBoardClient client : clients) { |  | ||||||
| 
 |  | ||||||
|             if (identifierIterator.hasNext()) { |  | ||||||
| 
 |  | ||||||
|                 // Fetch the batch identifier supplied by the specific client (may be null if batch open failed on client
 |  | ||||||
| 
 |  | ||||||
|                 BatchIdentifier identifier = identifierIterator.next(); |  | ||||||
| 
 |  | ||||||
|                 if (identifier != null) { |  | ||||||
| 
 |  | ||||||
|                     // Post the data with the matching identifier to the client
 |  | ||||||
|                     client.postBatchData(identifier, payload.batchChunkList, payload.startPosition, this); |  | ||||||
| 
 |  | ||||||
|                 } else { |  | ||||||
| 
 |  | ||||||
|                     // Count servers with no batch identifier as failed
 |  | ||||||
|                     maxFailedServers.decrementAndGet(); |  | ||||||
| 
 |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void onSuccess(Boolean result) { |  | ||||||
|         if (minServers.decrementAndGet() <= 0){ |  | ||||||
|             succeed(result); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void onFailure(Throwable t) { |  | ||||||
|         if (maxFailedServers.decrementAndGet() <= 0){ |  | ||||||
|             fail(t); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,34 +0,0 @@ | ||||||
| package meerkat.bulletinboard.workers.multiserver; |  | ||||||
| 
 |  | ||||||
| import com.google.common.util.concurrent.FutureCallback; |  | ||||||
| import meerkat.bulletinboard.SingleServerBulletinBoardClient; |  | ||||||
| import meerkat.protobuf.BulletinBoardApi.BulletinBoardMessage; |  | ||||||
| 
 |  | ||||||
| import java.util.List; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 27-Dec-15. |  | ||||||
|  */ |  | ||||||
| public class MultiServerPostBatchWorker extends MultiServerGenericPostWorker<BulletinBoardMessage> { |  | ||||||
| 
 |  | ||||||
|     private final int chunkSize; |  | ||||||
| 
 |  | ||||||
|     public MultiServerPostBatchWorker(List<SingleServerBulletinBoardClient> clients, |  | ||||||
|                                       int minServers, BulletinBoardMessage payload, int chunkSize, int maxRetry, |  | ||||||
|                                       FutureCallback<Boolean> futureCallback) { |  | ||||||
| 
 |  | ||||||
|         super(clients, minServers, payload, maxRetry, futureCallback); |  | ||||||
| 
 |  | ||||||
|         this.chunkSize = chunkSize; |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     protected void doPost(SingleServerBulletinBoardClient client, BulletinBoardMessage payload) { |  | ||||||
| 
 |  | ||||||
|         client.postAsBatch(payload, chunkSize, this); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,28 +0,0 @@ | ||||||
| package meerkat.bulletinboard.workers.multiserver; |  | ||||||
| 
 |  | ||||||
| import com.google.common.util.concurrent.FutureCallback; |  | ||||||
| import meerkat.bulletinboard.SingleServerBulletinBoardClient; |  | ||||||
| import meerkat.protobuf.BulletinBoardApi.*; |  | ||||||
| 
 |  | ||||||
| import java.util.List; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 27-Dec-15. |  | ||||||
|  */ |  | ||||||
| public class MultiServerPostMessageWorker extends MultiServerGenericPostWorker<BulletinBoardMessage> { |  | ||||||
| 
 |  | ||||||
|     public MultiServerPostMessageWorker(List<SingleServerBulletinBoardClient> clients, |  | ||||||
|                                         int minServers, BulletinBoardMessage payload, int maxRetry, |  | ||||||
|                                         FutureCallback<Boolean> futureCallback) { |  | ||||||
| 
 |  | ||||||
|         super(clients, minServers, payload, maxRetry, futureCallback); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     protected void doPost(SingleServerBulletinBoardClient client, BulletinBoardMessage payload) { |  | ||||||
|         client.postMessage(payload, this); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,29 +0,0 @@ | ||||||
| package meerkat.bulletinboard.workers.multiserver; |  | ||||||
| 
 |  | ||||||
| import com.google.common.util.concurrent.FutureCallback; |  | ||||||
| import meerkat.bulletinboard.SingleServerBulletinBoardClient; |  | ||||||
| import meerkat.protobuf.BulletinBoardApi.*; |  | ||||||
| 
 |  | ||||||
| import java.util.List; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 27-Dec-15. |  | ||||||
|  */ |  | ||||||
| public class MultiServerReadBatchDataWorker extends MultiServerGenericReadWorker<BulletinBoardMessage, BulletinBoardMessage> { |  | ||||||
| 
 |  | ||||||
|     public MultiServerReadBatchDataWorker(List<SingleServerBulletinBoardClient> clients, |  | ||||||
|                                           int minServers, BulletinBoardMessage payload, int maxRetry, |  | ||||||
|                                           FutureCallback<BulletinBoardMessage> futureCallback) { |  | ||||||
| 
 |  | ||||||
|         super(clients, minServers, payload, maxRetry, futureCallback); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     protected void doRead(BulletinBoardMessage payload, SingleServerBulletinBoardClient client) { |  | ||||||
|         client.readBatchData(payload, this); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,30 +0,0 @@ | ||||||
| package meerkat.bulletinboard.workers.multiserver; |  | ||||||
| 
 |  | ||||||
| import com.google.common.util.concurrent.FutureCallback; |  | ||||||
| import meerkat.bulletinboard.SingleServerBulletinBoardClient; |  | ||||||
| import meerkat.protobuf.BulletinBoardApi.BulletinBoardMessage; |  | ||||||
| import meerkat.protobuf.BulletinBoardApi.MessageID; |  | ||||||
| 
 |  | ||||||
| import java.util.List; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 27-Dec-15. |  | ||||||
|  */ |  | ||||||
| public class MultiServerReadMessageWorker extends MultiServerGenericReadWorker<MessageID, BulletinBoardMessage> { |  | ||||||
| 
 |  | ||||||
|     public MultiServerReadMessageWorker(List<SingleServerBulletinBoardClient> clients, |  | ||||||
|                                         int minServers, MessageID payload, int maxRetry, |  | ||||||
|                                         FutureCallback<BulletinBoardMessage> futureCallback) { |  | ||||||
| 
 |  | ||||||
|         super(clients, minServers, payload, maxRetry, futureCallback); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     protected void doRead(MessageID payload, SingleServerBulletinBoardClient client) { |  | ||||||
|         client.readMessage(payload, this); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,29 +0,0 @@ | ||||||
| package meerkat.bulletinboard.workers.multiserver; |  | ||||||
| 
 |  | ||||||
| import com.google.common.util.concurrent.FutureCallback; |  | ||||||
| import meerkat.bulletinboard.SingleServerBulletinBoardClient; |  | ||||||
| import meerkat.protobuf.BulletinBoardApi.*; |  | ||||||
| 
 |  | ||||||
| import java.util.List; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 27-Dec-15. |  | ||||||
|  */ |  | ||||||
| public class MultiServerReadMessagesWorker extends MultiServerGenericReadWorker<MessageFilterList,List<BulletinBoardMessage>>{ |  | ||||||
| 
 |  | ||||||
|     public MultiServerReadMessagesWorker(List<SingleServerBulletinBoardClient> clients, |  | ||||||
|                                         int minServers, MessageFilterList payload, int maxRetry, |  | ||||||
|                                         FutureCallback<List<BulletinBoardMessage>> futureCallback) { |  | ||||||
| 
 |  | ||||||
|         super(clients, minServers, payload, maxRetry, futureCallback); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     protected void doRead(MessageFilterList payload, SingleServerBulletinBoardClient client) { |  | ||||||
|         client.readMessages(payload, this); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,53 +0,0 @@ | ||||||
| package meerkat.bulletinboard.workers.singleserver; |  | ||||||
| 
 |  | ||||||
| import com.google.protobuf.Int64Value; |  | ||||||
| import meerkat.bulletinboard.SingleServerWorker; |  | ||||||
| import meerkat.comm.CommunicationException; |  | ||||||
| import meerkat.protobuf.BulletinBoardApi.BeginBatchMessage; |  | ||||||
| import meerkat.rest.Constants; |  | ||||||
| 
 |  | ||||||
| import javax.ws.rs.ProcessingException; |  | ||||||
| import javax.ws.rs.client.Client; |  | ||||||
| import javax.ws.rs.client.Entity; |  | ||||||
| import javax.ws.rs.client.WebTarget; |  | ||||||
| import javax.ws.rs.core.Response; |  | ||||||
| 
 |  | ||||||
| import static meerkat.bulletinboard.BulletinBoardConstants.BEGIN_BATCH_PATH; |  | ||||||
| import static meerkat.bulletinboard.BulletinBoardConstants.BULLETIN_BOARD_SERVER_PATH; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 27-Dec-15. |  | ||||||
|  * Tries to contact server once and perform a post operation |  | ||||||
|  */ |  | ||||||
| public class SingleServerBeginBatchWorker extends SingleServerWorker<BeginBatchMessage,Int64Value> { |  | ||||||
| 
 |  | ||||||
|     public SingleServerBeginBatchWorker(String serverAddress, BeginBatchMessage payload, int maxRetry) { |  | ||||||
|         super(serverAddress, payload, maxRetry); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public Int64Value call() throws Exception { |  | ||||||
|         Client client = clientLocal.get(); |  | ||||||
| 
 |  | ||||||
|         WebTarget webTarget = client.target(serverAddress).path(BULLETIN_BOARD_SERVER_PATH).path(BEGIN_BATCH_PATH); |  | ||||||
|         Response response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post( |  | ||||||
|                 Entity.entity(payload, Constants.MEDIATYPE_PROTOBUF)); |  | ||||||
| 
 |  | ||||||
|         try { |  | ||||||
| 
 |  | ||||||
|             Int64Value result =  response.readEntity(Int64Value.class); |  | ||||||
|             return result; |  | ||||||
| 
 |  | ||||||
|         } catch (ProcessingException | IllegalStateException e) { |  | ||||||
| 
 |  | ||||||
|             // Post to this server failed
 |  | ||||||
|             throw new CommunicationException("Could not contact the server. Original error: " + e.getMessage()); |  | ||||||
| 
 |  | ||||||
|         } catch (Exception e) { |  | ||||||
|             throw new CommunicationException("Could not contact the server. Original error: " + e.getMessage()); |  | ||||||
|         } |  | ||||||
|         finally { |  | ||||||
|             response.close(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,17 +0,0 @@ | ||||||
| package meerkat.bulletinboard.workers.singleserver; |  | ||||||
| 
 |  | ||||||
| import meerkat.protobuf.BulletinBoardApi.CloseBatchMessage; |  | ||||||
| 
 |  | ||||||
| import static meerkat.bulletinboard.BulletinBoardConstants.CLOSE_BATCH_PATH; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 27-Dec-15. |  | ||||||
|  * Tries to contact server once and perform a stop batch operation |  | ||||||
|  */ |  | ||||||
| public class SingleServerCloseBatchWorker extends SingleServerGenericPostWorker<CloseBatchMessage> { |  | ||||||
| 
 |  | ||||||
|     public SingleServerCloseBatchWorker(String serverAddress, CloseBatchMessage payload, int maxRetry) { |  | ||||||
|         super(serverAddress, CLOSE_BATCH_PATH, payload, maxRetry); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,55 +0,0 @@ | ||||||
| package meerkat.bulletinboard.workers.singleserver; |  | ||||||
| 
 |  | ||||||
| import com.google.protobuf.Int64Value; |  | ||||||
| import meerkat.bulletinboard.SingleServerWorker; |  | ||||||
| import meerkat.comm.CommunicationException; |  | ||||||
| import meerkat.protobuf.BulletinBoardApi.SyncQuery; |  | ||||||
| import meerkat.protobuf.BulletinBoardApi.GenerateSyncQueryParams; |  | ||||||
| import meerkat.rest.Constants; |  | ||||||
| 
 |  | ||||||
| import javax.ws.rs.ProcessingException; |  | ||||||
| import javax.ws.rs.client.Client; |  | ||||||
| import javax.ws.rs.client.Entity; |  | ||||||
| import javax.ws.rs.client.WebTarget; |  | ||||||
| import javax.ws.rs.core.Response; |  | ||||||
| 
 |  | ||||||
| import static meerkat.bulletinboard.BulletinBoardConstants.BULLETIN_BOARD_SERVER_PATH; |  | ||||||
| import static meerkat.bulletinboard.BulletinBoardConstants.GENERATE_SYNC_QUERY_PATH; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 27-Dec-15. |  | ||||||
|  * Tries to contact server once and perform a Sync Query Generation operation |  | ||||||
|  */ |  | ||||||
| public class SingleServerGenerateSyncQueryWorker extends SingleServerWorker<GenerateSyncQueryParams,SyncQuery> { |  | ||||||
| 
 |  | ||||||
|     public SingleServerGenerateSyncQueryWorker(String serverAddress, GenerateSyncQueryParams payload, int maxRetry) { |  | ||||||
|         super(serverAddress, payload, maxRetry); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public SyncQuery call() throws Exception { |  | ||||||
| 
 |  | ||||||
|         Client client = clientLocal.get(); |  | ||||||
| 
 |  | ||||||
|         WebTarget webTarget = client.target(serverAddress).path(BULLETIN_BOARD_SERVER_PATH).path(GENERATE_SYNC_QUERY_PATH); |  | ||||||
| 
 |  | ||||||
|         Response response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(payload, Constants.MEDIATYPE_PROTOBUF)); |  | ||||||
| 
 |  | ||||||
|         try { |  | ||||||
| 
 |  | ||||||
|             SyncQuery result = response.readEntity(SyncQuery.class); |  | ||||||
|             return result; |  | ||||||
| 
 |  | ||||||
|         } catch (ProcessingException | IllegalStateException e) { |  | ||||||
| 
 |  | ||||||
|             // Post to this server failed
 |  | ||||||
|             throw new CommunicationException("Could not contact the server. Original error: " + e.getMessage()); |  | ||||||
| 
 |  | ||||||
|         } catch (Exception e) { |  | ||||||
|             throw new CommunicationException("Could not contact the server. Original error: " + e.getMessage()); |  | ||||||
|         } |  | ||||||
|         finally { |  | ||||||
|             response.close(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,63 +0,0 @@ | ||||||
| package meerkat.bulletinboard.workers.singleserver; |  | ||||||
| 
 |  | ||||||
| import com.google.protobuf.BoolValue; |  | ||||||
| import meerkat.bulletinboard.SingleServerWorker; |  | ||||||
| import meerkat.comm.CommunicationException; |  | ||||||
| import meerkat.protobuf.Comm.*; |  | ||||||
| import meerkat.rest.Constants; |  | ||||||
| 
 |  | ||||||
| import javax.ws.rs.ProcessingException; |  | ||||||
| import javax.ws.rs.client.Client; |  | ||||||
| import javax.ws.rs.client.Entity; |  | ||||||
| import javax.ws.rs.client.WebTarget; |  | ||||||
| import javax.ws.rs.core.Response; |  | ||||||
| 
 |  | ||||||
| import static meerkat.bulletinboard.BulletinBoardConstants.BULLETIN_BOARD_SERVER_PATH; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 27-Dec-15. |  | ||||||
|  * Tries to contact server once and perform a post operation |  | ||||||
|  */ |  | ||||||
| public class SingleServerGenericPostWorker<T> extends SingleServerWorker<T, Boolean> { |  | ||||||
| 
 |  | ||||||
|     private final String subPath; |  | ||||||
| 
 |  | ||||||
|     public SingleServerGenericPostWorker(String serverAddress, String subPath, T payload, int maxRetry) { |  | ||||||
|         super(serverAddress, payload, maxRetry); |  | ||||||
|         this.subPath = subPath; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * This method carries out the actual communication with the server via HTTP Post |  | ||||||
|      * It accesses the server and tries to post the payload to it |  | ||||||
|      * Successful post to a server results |  | ||||||
|      * @return TRUE if the operation is successful |  | ||||||
|      * @throws CommunicationException if the operation is unsuccessful |  | ||||||
|      */ |  | ||||||
|     public Boolean call() throws CommunicationException{ |  | ||||||
| 
 |  | ||||||
|         Client client = clientLocal.get(); |  | ||||||
| 
 |  | ||||||
|         WebTarget webTarget = client.target(serverAddress).path(BULLETIN_BOARD_SERVER_PATH).path(subPath); |  | ||||||
|         Response response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post( |  | ||||||
|                 Entity.entity(payload, Constants.MEDIATYPE_PROTOBUF)); |  | ||||||
| 
 |  | ||||||
|         try { |  | ||||||
| 
 |  | ||||||
|             // If a BoolValue entity is returned: the post was successful
 |  | ||||||
|             response.readEntity(BoolValue.class); |  | ||||||
|             return Boolean.TRUE; |  | ||||||
| 
 |  | ||||||
|         } catch (ProcessingException | IllegalStateException e) { |  | ||||||
| 
 |  | ||||||
|             // Post to this server failed
 |  | ||||||
|             throw new CommunicationException("Could not contact the server"); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
|         finally { |  | ||||||
|             response.close(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,85 +0,0 @@ | ||||||
| package meerkat.bulletinboard.workers.singleserver; |  | ||||||
| 
 |  | ||||||
| import meerkat.bulletinboard.SingleServerWorker; |  | ||||||
| import meerkat.comm.CommunicationException; |  | ||||||
| import meerkat.comm.MessageInputStream; |  | ||||||
| import meerkat.protobuf.BulletinBoardApi.*; |  | ||||||
| import meerkat.rest.Constants; |  | ||||||
| 
 |  | ||||||
| import javax.ws.rs.client.Client; |  | ||||||
| import javax.ws.rs.client.Entity; |  | ||||||
| import javax.ws.rs.client.WebTarget; |  | ||||||
| import javax.ws.rs.core.Response; |  | ||||||
| 
 |  | ||||||
| import java.io.IOException; |  | ||||||
| import java.io.InputStream; |  | ||||||
| 
 |  | ||||||
| import static meerkat.bulletinboard.BulletinBoardConstants.BULLETIN_BOARD_SERVER_PATH; |  | ||||||
| import static meerkat.bulletinboard.BulletinBoardConstants.READ_MESSAGES_PATH; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 27-Dec-15. |  | ||||||
|  */ |  | ||||||
| public class SingleServerGetRedundancyWorker extends SingleServerWorker<MessageID, Float> { |  | ||||||
| 
 |  | ||||||
|     public SingleServerGetRedundancyWorker(String serverAddress, MessageID payload, int maxRetry) { |  | ||||||
|         super(serverAddress, payload, maxRetry); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * This method carries out the actual communication with the server via HTTP Post |  | ||||||
|      * It queries the server for a message with the given ID |  | ||||||
|      * @return TRUE if the message exists in the server and FALSE otherwise |  | ||||||
|      * @throws CommunicationException if the server does not return a valid answer |  | ||||||
|      */ |  | ||||||
|     public Float call() throws CommunicationException{ |  | ||||||
| 
 |  | ||||||
|         Client client = clientLocal.get(); |  | ||||||
| 
 |  | ||||||
|         WebTarget webTarget; |  | ||||||
|         Response response; |  | ||||||
| 
 |  | ||||||
|         MessageFilterList msgFilterList = MessageFilterList.newBuilder() |  | ||||||
|                 .addFilter(MessageFilter.newBuilder() |  | ||||||
|                         .setType(FilterType.MSG_ID) |  | ||||||
|                         .setId(payload.getID()) |  | ||||||
|                         .build() |  | ||||||
|                 ).build(); |  | ||||||
| 
 |  | ||||||
|         // Send request to Server
 |  | ||||||
| 
 |  | ||||||
|         // Send request to Server
 |  | ||||||
|         webTarget = client.target(serverAddress).path(BULLETIN_BOARD_SERVER_PATH).path(READ_MESSAGES_PATH); |  | ||||||
|         InputStream in = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(msgFilterList, Constants.MEDIATYPE_PROTOBUF), InputStream.class); |  | ||||||
| 
 |  | ||||||
|         MessageInputStream<BulletinBoardMessage> inputStream = null; |  | ||||||
| 
 |  | ||||||
|         // Retrieve answer
 |  | ||||||
| 
 |  | ||||||
|         try { |  | ||||||
| 
 |  | ||||||
|             inputStream = MessageInputStream.MessageInputStreamFactory.createMessageInputStream(in, BulletinBoardMessage.class); |  | ||||||
| 
 |  | ||||||
|             if (inputStream.asList().size() > 0){ |  | ||||||
|                 // Message exists in the server
 |  | ||||||
|                 return 1.0f; |  | ||||||
|             } |  | ||||||
|             else { |  | ||||||
|                 // Message does not exist in the server
 |  | ||||||
|                 return 0.0f; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|         } catch (Exception e) { |  | ||||||
| 
 |  | ||||||
|             // Read failed
 |  | ||||||
|             throw new CommunicationException("Server access failed"); |  | ||||||
| 
 |  | ||||||
|         } finally { |  | ||||||
|             try { |  | ||||||
|                 inputStream.close(); |  | ||||||
|             } catch (IOException ignored) {} |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,17 +0,0 @@ | ||||||
| package meerkat.bulletinboard.workers.singleserver; |  | ||||||
| 
 |  | ||||||
| import meerkat.protobuf.BulletinBoardApi.BatchMessage; |  | ||||||
| 
 |  | ||||||
| import static meerkat.bulletinboard.BulletinBoardConstants.POST_BATCH_PATH; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 27-Dec-15. |  | ||||||
|  * Tries to contact server once and perform a post batch operation |  | ||||||
|  */ |  | ||||||
| public class SingleServerPostBatchWorker extends SingleServerGenericPostWorker<BatchMessage> { |  | ||||||
| 
 |  | ||||||
|     public SingleServerPostBatchWorker(String serverAddress, BatchMessage payload, int maxRetry) { |  | ||||||
|         super(serverAddress, POST_BATCH_PATH, payload, maxRetry); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,17 +0,0 @@ | ||||||
| package meerkat.bulletinboard.workers.singleserver; |  | ||||||
| 
 |  | ||||||
| import meerkat.protobuf.BulletinBoardApi.BulletinBoardMessage; |  | ||||||
| 
 |  | ||||||
| import static meerkat.bulletinboard.BulletinBoardConstants.POST_MESSAGE_PATH; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 27-Dec-15. |  | ||||||
|  * Tries to contact server once and perform a post operation |  | ||||||
|  */ |  | ||||||
| public class SingleServerPostMessageWorker extends SingleServerGenericPostWorker<BulletinBoardMessage> { |  | ||||||
| 
 |  | ||||||
|     public SingleServerPostMessageWorker(String serverAddress, BulletinBoardMessage payload, int maxRetry) { |  | ||||||
|         super(serverAddress, POST_MESSAGE_PATH, payload, maxRetry); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,59 +0,0 @@ | ||||||
| package meerkat.bulletinboard.workers.singleserver; |  | ||||||
| 
 |  | ||||||
| import meerkat.bulletinboard.SingleServerWorker; |  | ||||||
| import meerkat.comm.CommunicationException; |  | ||||||
| import meerkat.protobuf.BulletinBoardApi.SyncQuery; |  | ||||||
| import meerkat.protobuf.BulletinBoardApi.SyncQueryResponse; |  | ||||||
| import meerkat.rest.Constants; |  | ||||||
| 
 |  | ||||||
| import javax.ws.rs.ProcessingException; |  | ||||||
| import javax.ws.rs.client.Client; |  | ||||||
| import javax.ws.rs.client.Entity; |  | ||||||
| import javax.ws.rs.client.WebTarget; |  | ||||||
| import javax.ws.rs.core.Response; |  | ||||||
| 
 |  | ||||||
| import static meerkat.bulletinboard.BulletinBoardConstants.BULLETIN_BOARD_SERVER_PATH; |  | ||||||
| import static meerkat.bulletinboard.BulletinBoardConstants.SYNC_QUERY_PATH; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 27-Dec-15. |  | ||||||
|  * Tries to contact server once and perform a post operation |  | ||||||
|  */ |  | ||||||
| public class SingleServerQuerySyncWorker extends SingleServerWorker<SyncQuery, SyncQueryResponse> { |  | ||||||
| 
 |  | ||||||
|     public SingleServerQuerySyncWorker(String serverAddress, SyncQuery payload, int maxRetry) { |  | ||||||
|         super(serverAddress, payload, maxRetry); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public SyncQueryResponse call() throws Exception { |  | ||||||
| 
 |  | ||||||
|         Client client = clientLocal.get(); |  | ||||||
| 
 |  | ||||||
|         WebTarget webTarget; |  | ||||||
|         Response response; |  | ||||||
| 
 |  | ||||||
|         // Send request to Server
 |  | ||||||
| 
 |  | ||||||
|         webTarget = client.target(serverAddress).path(BULLETIN_BOARD_SERVER_PATH).path(SYNC_QUERY_PATH); |  | ||||||
|         response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(payload, Constants.MEDIATYPE_PROTOBUF)); |  | ||||||
| 
 |  | ||||||
|         // Retrieve answer
 |  | ||||||
| 
 |  | ||||||
|         try { |  | ||||||
| 
 |  | ||||||
|             // If a BulletinBoardMessageList is returned: the read was successful
 |  | ||||||
|             return response.readEntity(SyncQueryResponse.class); |  | ||||||
| 
 |  | ||||||
|         } catch (ProcessingException | IllegalStateException e) { |  | ||||||
| 
 |  | ||||||
|             // Read failed
 |  | ||||||
|             throw new CommunicationException("Server access failed"); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
|         finally { |  | ||||||
|             response.close(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,71 +0,0 @@ | ||||||
| package meerkat.bulletinboard.workers.singleserver; |  | ||||||
| 
 |  | ||||||
| import meerkat.bulletinboard.SingleServerWorker; |  | ||||||
| import meerkat.comm.CommunicationException; |  | ||||||
| import meerkat.comm.MessageInputStream; |  | ||||||
| import meerkat.protobuf.BulletinBoardApi.*; |  | ||||||
| import meerkat.rest.Constants; |  | ||||||
| 
 |  | ||||||
| import javax.ws.rs.client.Client; |  | ||||||
| import javax.ws.rs.client.Entity; |  | ||||||
| import javax.ws.rs.client.WebTarget; |  | ||||||
| import java.io.IOException; |  | ||||||
| import java.io.InputStream; |  | ||||||
| import java.lang.reflect.InvocationTargetException; |  | ||||||
| import java.util.List; |  | ||||||
| 
 |  | ||||||
| import static meerkat.bulletinboard.BulletinBoardConstants.BULLETIN_BOARD_SERVER_PATH; |  | ||||||
| import static meerkat.bulletinboard.BulletinBoardConstants.READ_BATCH_PATH; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 27-Dec-15. |  | ||||||
|  */ |  | ||||||
| public class SingleServerReadBatchWorker extends SingleServerWorker<BatchQuery, List<BatchChunk>> { |  | ||||||
| 
 |  | ||||||
|     public SingleServerReadBatchWorker(String serverAddress, BatchQuery payload, int maxRetry) { |  | ||||||
|         super(serverAddress, payload, maxRetry); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * This method carries out the actual communication with the server via HTTP Post |  | ||||||
|      * Upon successful retrieval from the server the method returns the received values |  | ||||||
|      * @return the complete batch as read from the server |  | ||||||
|      * @throws CommunicationException if the server's response is invalid |  | ||||||
|      */ |  | ||||||
|     public List<BatchChunk> call() throws CommunicationException{ |  | ||||||
| 
 |  | ||||||
|         Client client = clientLocal.get(); |  | ||||||
| 
 |  | ||||||
|         WebTarget webTarget; |  | ||||||
| 
 |  | ||||||
|         // Get the batch data
 |  | ||||||
| 
 |  | ||||||
|         webTarget = client.target(serverAddress).path(BULLETIN_BOARD_SERVER_PATH).path(READ_BATCH_PATH); |  | ||||||
|         InputStream in = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(payload, Constants.MEDIATYPE_PROTOBUF), InputStream.class); |  | ||||||
| 
 |  | ||||||
|         MessageInputStream<BatchChunk> inputStream = null; |  | ||||||
| 
 |  | ||||||
|         try { |  | ||||||
| 
 |  | ||||||
|             inputStream = MessageInputStream.MessageInputStreamFactory.createMessageInputStream(in, BatchChunk.class); |  | ||||||
| 
 |  | ||||||
|             return inputStream.asList(); |  | ||||||
| 
 |  | ||||||
|         } catch (IOException | InvocationTargetException e) { |  | ||||||
| 
 |  | ||||||
|             // Read failed
 |  | ||||||
|             throw new CommunicationException("Could not contact the server or server returned illegal result"); |  | ||||||
| 
 |  | ||||||
|         } catch (NoSuchMethodException | IllegalAccessException e) { |  | ||||||
| 
 |  | ||||||
|             throw new CommunicationException("MessageInputStream error"); |  | ||||||
| 
 |  | ||||||
|         } finally { |  | ||||||
|             try { |  | ||||||
|                 inputStream.close(); |  | ||||||
|             } catch (IOException ignored) {} |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,76 +0,0 @@ | ||||||
| package meerkat.bulletinboard.workers.singleserver; |  | ||||||
| 
 |  | ||||||
| import meerkat.bulletinboard.SingleServerWorker; |  | ||||||
| import meerkat.comm.CommunicationException; |  | ||||||
| import meerkat.comm.MessageInputStream; |  | ||||||
| import meerkat.protobuf.BulletinBoardApi; |  | ||||||
| import meerkat.protobuf.BulletinBoardApi.BulletinBoardMessageList; |  | ||||||
| import meerkat.protobuf.BulletinBoardApi.MessageFilterList; |  | ||||||
| import meerkat.protobuf.BulletinBoardApi.BulletinBoardMessage; |  | ||||||
| import meerkat.rest.Constants; |  | ||||||
| 
 |  | ||||||
| import javax.ws.rs.ProcessingException; |  | ||||||
| import javax.ws.rs.client.Client; |  | ||||||
| import javax.ws.rs.client.Entity; |  | ||||||
| import javax.ws.rs.client.WebTarget; |  | ||||||
| import javax.ws.rs.core.Response; |  | ||||||
| 
 |  | ||||||
| import java.io.IOException; |  | ||||||
| import java.io.InputStream; |  | ||||||
| import java.lang.reflect.InvocationTargetException; |  | ||||||
| import java.util.List; |  | ||||||
| 
 |  | ||||||
| import static meerkat.bulletinboard.BulletinBoardConstants.BULLETIN_BOARD_SERVER_PATH; |  | ||||||
| import static meerkat.bulletinboard.BulletinBoardConstants.READ_MESSAGES_PATH; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 27-Dec-15. |  | ||||||
|  */ |  | ||||||
| public class SingleServerReadMessagesWorker extends SingleServerWorker<MessageFilterList, List<BulletinBoardMessage>> { |  | ||||||
| 
 |  | ||||||
|     public SingleServerReadMessagesWorker(String serverAddress, MessageFilterList payload, int maxRetry) { |  | ||||||
|         super(serverAddress, payload, maxRetry); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * This method carries out the actual communication with the server via HTTP Post |  | ||||||
|      * Upon successful retrieval from the server the method returns the received values |  | ||||||
|      * @return The list of messages returned by the server |  | ||||||
|      * @throws CommunicationException if the server's response is invalid |  | ||||||
|      */ |  | ||||||
|     public List<BulletinBoardMessage> call() throws CommunicationException{ |  | ||||||
| 
 |  | ||||||
|         Client client = clientLocal.get(); |  | ||||||
| 
 |  | ||||||
|         WebTarget webTarget; |  | ||||||
| 
 |  | ||||||
|         // Send request to Server
 |  | ||||||
|         webTarget = client.target(serverAddress).path(BULLETIN_BOARD_SERVER_PATH).path(READ_MESSAGES_PATH); |  | ||||||
|         InputStream in = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(payload, Constants.MEDIATYPE_PROTOBUF), InputStream.class); |  | ||||||
| 
 |  | ||||||
|         MessageInputStream<BulletinBoardMessage> inputStream = null; |  | ||||||
| 
 |  | ||||||
|         try { |  | ||||||
| 
 |  | ||||||
|             inputStream = MessageInputStream.MessageInputStreamFactory.createMessageInputStream(in, BulletinBoardMessage.class); |  | ||||||
| 
 |  | ||||||
|             return inputStream.asList(); |  | ||||||
| 
 |  | ||||||
|         } catch (IOException | InvocationTargetException e) { |  | ||||||
| 
 |  | ||||||
|             // Read failed
 |  | ||||||
|             throw new CommunicationException("Could not contact the server or server returned illegal result"); |  | ||||||
| 
 |  | ||||||
|         } catch (NoSuchMethodException | IllegalAccessException e) { |  | ||||||
| 
 |  | ||||||
|             throw new CommunicationException("MessageInputStream error"); |  | ||||||
| 
 |  | ||||||
|         } finally { |  | ||||||
|             try { |  | ||||||
|                 inputStream.close(); |  | ||||||
|             } catch (IOException ignored) {} |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,318 +0,0 @@ | ||||||
| package meerkat.bulletinboard; |  | ||||||
| 
 |  | ||||||
| import com.google.common.util.concurrent.FutureCallback; |  | ||||||
| import com.google.protobuf.*; |  | ||||||
| import com.google.protobuf.Timestamp; |  | ||||||
| 
 |  | ||||||
| import static meerkat.bulletinboard.BulletinBoardSynchronizer.SyncStatus; |  | ||||||
| 
 |  | ||||||
| import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer; |  | ||||||
| import meerkat.bulletinboard.sqlserver.H2QueryProvider; |  | ||||||
| 
 |  | ||||||
| import meerkat.comm.CommunicationException; |  | ||||||
| import meerkat.crypto.concrete.ECDSASignature; |  | ||||||
| import meerkat.protobuf.BulletinBoardApi.*; |  | ||||||
| import meerkat.util.BulletinBoardMessageComparator; |  | ||||||
| import meerkat.util.BulletinBoardMessageGenerator; |  | ||||||
| import org.junit.*; |  | ||||||
| 
 |  | ||||||
| import java.io.IOException; |  | ||||||
| import java.io.InputStream; |  | ||||||
| import java.security.*; |  | ||||||
| import java.security.cert.CertificateException; |  | ||||||
| import java.util.LinkedList; |  | ||||||
| import java.util.List; |  | ||||||
| import java.util.Random; |  | ||||||
| import java.util.concurrent.Semaphore; |  | ||||||
| import java.util.concurrent.TimeUnit; |  | ||||||
| import java.util.concurrent.TimeoutException; |  | ||||||
| import java.util.concurrent.atomic.AtomicBoolean; |  | ||||||
| 
 |  | ||||||
| import static org.hamcrest.MatcherAssert.assertThat; |  | ||||||
| import static org.junit.Assert.fail; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel on 6/1/2016. |  | ||||||
|  */ |  | ||||||
| public class BulletinBoardSynchronizerTest { |  | ||||||
| 
 |  | ||||||
|     private static final String REMOTE_SERVER_ADDRESS = "remoteDB"; |  | ||||||
|     private static final String LOCAL_SERVER_ADDRESS = "localDB"; |  | ||||||
|     private static int testCount; |  | ||||||
| 
 |  | ||||||
|     private static final int THREAD_NUM = 3; |  | ||||||
|     private static final int SUBSCRIPTION_INTERVAL = 1000; |  | ||||||
| 
 |  | ||||||
|     private static final int SYNC_SLEEP_INTERVAL = 500; |  | ||||||
|     private static final int SYNC_WAIT_CAP = 1000; |  | ||||||
| 
 |  | ||||||
|     private DeletableSubscriptionBulletinBoardClient localClient; |  | ||||||
|     private AsyncBulletinBoardClient remoteClient; |  | ||||||
| 
 |  | ||||||
|     private BulletinBoardSynchronizer synchronizer; |  | ||||||
| 
 |  | ||||||
|     private static BulletinBoardMessageGenerator messageGenerator; |  | ||||||
|     private static BulletinBoardMessageComparator messageComparator; |  | ||||||
| 
 |  | ||||||
|     private static String KEYFILE_EXAMPLE = "/certs/enduser-certs/user1-key-with-password-secret.p12"; |  | ||||||
|     private static String KEYFILE_PASSWORD1 = "secret"; |  | ||||||
|     private static String CERT1_PEM_EXAMPLE = "/certs/enduser-certs/user1.crt"; |  | ||||||
| 
 |  | ||||||
|     private static BulletinBoardSignature[] signers; |  | ||||||
|     private static ByteString[] signerIDs; |  | ||||||
| 
 |  | ||||||
|     private Semaphore semaphore; |  | ||||||
|     private List<Throwable> thrown; |  | ||||||
| 
 |  | ||||||
|     @BeforeClass |  | ||||||
|     public static void build() { |  | ||||||
| 
 |  | ||||||
|         messageGenerator = new BulletinBoardMessageGenerator(new Random(0)); |  | ||||||
|         messageComparator = new BulletinBoardMessageComparator(); |  | ||||||
| 
 |  | ||||||
|         signers = new BulletinBoardSignature[1]; |  | ||||||
|         signerIDs = new ByteString[1]; |  | ||||||
| 
 |  | ||||||
|         signers[0] = new GenericBulletinBoardSignature(new ECDSASignature()); |  | ||||||
|         signerIDs[0] = signers[0].getSignerID(); |  | ||||||
| 
 |  | ||||||
|         InputStream keyStream = BulletinBoardSynchronizerTest.class.getResourceAsStream(KEYFILE_EXAMPLE); |  | ||||||
|         char[] password = KEYFILE_PASSWORD1.toCharArray(); |  | ||||||
| 
 |  | ||||||
|         try { |  | ||||||
| 
 |  | ||||||
|             KeyStore.Builder keyStoreBuilder = signers[0].getPKCS12KeyStoreBuilder(keyStream, password); |  | ||||||
| 
 |  | ||||||
|             signers[0].loadSigningCertificate(keyStoreBuilder); |  | ||||||
| 
 |  | ||||||
|             signers[0].loadVerificationCertificates(BulletinBoardSynchronizerTest.class.getResourceAsStream(CERT1_PEM_EXAMPLE)); |  | ||||||
| 
 |  | ||||||
|         } catch (IOException e) { |  | ||||||
|             System.err.println("Failed reading from signature file " + e.getMessage()); |  | ||||||
|             fail("Failed reading from signature file " + e.getMessage()); |  | ||||||
|         } catch (CertificateException e) { |  | ||||||
|             System.err.println("Failed reading certificate " + e.getMessage()); |  | ||||||
|             fail("Failed reading certificate " + e.getMessage()); |  | ||||||
|         } catch (KeyStoreException e) { |  | ||||||
|             System.err.println("Failed reading keystore " + e.getMessage()); |  | ||||||
|             fail("Failed reading keystore " + e.getMessage()); |  | ||||||
|         } catch (NoSuchAlgorithmException e) { |  | ||||||
|             System.err.println("Couldn't find signing algorithm " + e.getMessage()); |  | ||||||
|             fail("Couldn't find signing algorithm " + e.getMessage()); |  | ||||||
|         } catch (UnrecoverableKeyException e) { |  | ||||||
|             System.err.println("Couldn't find signing key " + e.getMessage()); |  | ||||||
|             fail("Couldn't find signing key " + e.getMessage()); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         signerIDs[0] = signers[0].getSignerID(); |  | ||||||
| 
 |  | ||||||
|         testCount = 0; |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Before |  | ||||||
|     public void init() throws CommunicationException { |  | ||||||
| 
 |  | ||||||
|         DeletableBulletinBoardServer remoteServer = new BulletinBoardSQLServer(new H2QueryProvider(REMOTE_SERVER_ADDRESS + testCount)); |  | ||||||
|         remoteServer.init(); |  | ||||||
| 
 |  | ||||||
|         remoteClient = new LocalBulletinBoardClient( |  | ||||||
|                 remoteServer, |  | ||||||
|                 THREAD_NUM, |  | ||||||
|                 SUBSCRIPTION_INTERVAL); |  | ||||||
| 
 |  | ||||||
|         DeletableBulletinBoardServer localServer = new BulletinBoardSQLServer(new H2QueryProvider(LOCAL_SERVER_ADDRESS + testCount)); |  | ||||||
|         localServer.init(); |  | ||||||
| 
 |  | ||||||
|         localClient = new LocalBulletinBoardClient( |  | ||||||
|                 localServer, |  | ||||||
|                 THREAD_NUM, |  | ||||||
|                 SUBSCRIPTION_INTERVAL); |  | ||||||
| 
 |  | ||||||
|         synchronizer = new SimpleBulletinBoardSynchronizer(SYNC_SLEEP_INTERVAL, SYNC_WAIT_CAP); |  | ||||||
|         synchronizer.init(localClient, remoteClient); |  | ||||||
| 
 |  | ||||||
|         semaphore = new Semaphore(0); |  | ||||||
|         thrown = new LinkedList<>(); |  | ||||||
| 
 |  | ||||||
|         testCount++; |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private class SyncStatusCallback implements FutureCallback<SyncStatus> { |  | ||||||
| 
 |  | ||||||
|         private final SyncStatus statusToWaitFor; |  | ||||||
|         private AtomicBoolean stillWaiting; |  | ||||||
| 
 |  | ||||||
|         public SyncStatusCallback(SyncStatus statusToWaitFor) { |  | ||||||
|             this.statusToWaitFor = statusToWaitFor; |  | ||||||
|             stillWaiting = new AtomicBoolean(true); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onSuccess(SyncStatus result) { |  | ||||||
| 
 |  | ||||||
|             if (result == statusToWaitFor && stillWaiting.compareAndSet(true, false)){ |  | ||||||
|                 semaphore.release(); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onFailure(Throwable t) { |  | ||||||
|             thrown.add(t); |  | ||||||
|             if (stillWaiting.compareAndSet(true,false)) { |  | ||||||
|                 semaphore.release(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private class MessageCountCallback implements FutureCallback<Integer> { |  | ||||||
| 
 |  | ||||||
|         private int[] expectedCounts; |  | ||||||
|         private int currentIteration; |  | ||||||
| 
 |  | ||||||
|         public MessageCountCallback(int[] expectedCounts) { |  | ||||||
|             this.expectedCounts = expectedCounts; |  | ||||||
|             this.currentIteration = 0; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onSuccess(Integer result) { |  | ||||||
| 
 |  | ||||||
|             if (currentIteration < expectedCounts.length){ |  | ||||||
|                 if (result != expectedCounts[currentIteration]){ |  | ||||||
|                     onFailure(new AssertionError("Wrong message count. Expected " + expectedCounts[currentIteration] + " but received " + result)); |  | ||||||
|                     currentIteration = expectedCounts.length; |  | ||||||
|                     return; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             currentIteration++; |  | ||||||
| 
 |  | ||||||
|             if (currentIteration == expectedCounts.length) |  | ||||||
|                 semaphore.release(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onFailure(Throwable t) { |  | ||||||
|             thrown.add(t); |  | ||||||
|             semaphore.release(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Test |  | ||||||
|     public void testSync() throws SignatureException, CommunicationException, InterruptedException { |  | ||||||
| 
 |  | ||||||
|         Timestamp timestamp = Timestamp.newBuilder() |  | ||||||
|                 .setSeconds(15252162) |  | ||||||
|                 .setNanos(85914) |  | ||||||
|                 .build(); |  | ||||||
| 
 |  | ||||||
|         BulletinBoardMessage msg = messageGenerator.generateRandomMessage(signers, timestamp, 10, 10); |  | ||||||
| 
 |  | ||||||
|         MessageID msgID = localClient.postMessage(msg); |  | ||||||
| 
 |  | ||||||
|         timestamp = Timestamp.newBuilder() |  | ||||||
|                 .setSeconds(51511653) |  | ||||||
|                 .setNanos(3625) |  | ||||||
|                 .build(); |  | ||||||
| 
 |  | ||||||
|         BulletinBoardMessage batchMessage = messageGenerator.generateRandomMessage(signers,timestamp,  100, 10); |  | ||||||
| 
 |  | ||||||
|         MessageID batchMsgID = localClient.postAsBatch(batchMessage, 10); |  | ||||||
| 
 |  | ||||||
|         BulletinBoardMessage test = localClient.readMessage(batchMsgID); |  | ||||||
| 
 |  | ||||||
|         BulletinBoardMessage stub = localClient.readMessages(MessageFilterList.newBuilder() |  | ||||||
|                 .addFilter(MessageFilter.newBuilder() |  | ||||||
|                         .setType(FilterType.MSG_ID) |  | ||||||
|                         .setId(batchMsgID.getID()) |  | ||||||
|                         .build()) |  | ||||||
|                 .build()).get(0); |  | ||||||
| 
 |  | ||||||
|         BulletinBoardMessage test2 = localClient.readBatchData(stub); |  | ||||||
| 
 |  | ||||||
|         synchronizer.subscribeToSyncStatus(new SyncStatusCallback(SyncStatus.SYNCHRONIZED)); |  | ||||||
| 
 |  | ||||||
|         int[] expectedCounts = {2,0}; |  | ||||||
|         synchronizer.subscribeToRemainingMessagesCount(new MessageCountCallback(expectedCounts)); |  | ||||||
| 
 |  | ||||||
|         Thread syncThread = new Thread(synchronizer); |  | ||||||
|         syncThread.start(); |  | ||||||
| 
 |  | ||||||
|         if (!semaphore.tryAcquire(2, 4000, TimeUnit.MILLISECONDS)) { |  | ||||||
|             thrown.add(new TimeoutException("Timeout occurred while waiting for synchronizer to sync.")); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         synchronizer.stop(); |  | ||||||
|         syncThread.join(); |  | ||||||
| 
 |  | ||||||
|         if (thrown.size() > 0) { |  | ||||||
|             for (Throwable t : thrown) |  | ||||||
|                 System.err.println(t.getMessage()); |  | ||||||
|             assertThat("Exception thrown by Synchronizer: " + thrown.get(0).getMessage(), false); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         List<BulletinBoardMessage> msgList = remoteClient.readMessages(MessageFilterList.newBuilder() |  | ||||||
|                 .addFilter(MessageFilter.newBuilder() |  | ||||||
|                         .setType(FilterType.MSG_ID) |  | ||||||
|                         .setId(msgID.getID()) |  | ||||||
|                         .build()) |  | ||||||
|                 .build()); |  | ||||||
| 
 |  | ||||||
|         assertThat("Wrong number of messages returned.", msgList.size() == 1); |  | ||||||
|         assertThat("Returned message is not equal to original one", messageComparator.compare(msgList.get(0),msg) == 0); |  | ||||||
| 
 |  | ||||||
|         BulletinBoardMessage returnedBatchMsg = remoteClient.readMessage(batchMsgID); |  | ||||||
| 
 |  | ||||||
|         assertThat("Returned batch does not equal original one.", messageComparator.compare(returnedBatchMsg, batchMessage) == 0); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Test |  | ||||||
|     public void testServerError() throws SignatureException, CommunicationException, InterruptedException { |  | ||||||
| 
 |  | ||||||
|         Timestamp timestamp = Timestamp.newBuilder() |  | ||||||
|                 .setSeconds(945736256) |  | ||||||
|                 .setNanos(276788) |  | ||||||
|                 .build(); |  | ||||||
| 
 |  | ||||||
|         BulletinBoardMessage msg = messageGenerator.generateRandomMessage(signers, timestamp, 10, 10); |  | ||||||
| 
 |  | ||||||
|         remoteClient.close(); |  | ||||||
| 
 |  | ||||||
|         synchronizer.subscribeToSyncStatus(new SyncStatusCallback(SyncStatus.SERVER_ERROR)); |  | ||||||
| 
 |  | ||||||
|         localClient.postMessage(msg); |  | ||||||
| 
 |  | ||||||
|         Thread thread = new Thread(synchronizer); |  | ||||||
| 
 |  | ||||||
|         thread.start(); |  | ||||||
| 
 |  | ||||||
|         if (!semaphore.tryAcquire(4000, TimeUnit.MILLISECONDS)) { |  | ||||||
|             thrown.add(new TimeoutException("Timeout occurred while waiting for synchronizer to sync.")); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         synchronizer.stop(); |  | ||||||
|         thread.join(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @After |  | ||||||
|     public void close() { |  | ||||||
| 
 |  | ||||||
|         if (thrown.size() > 0) { |  | ||||||
|             for (Throwable t : thrown) { |  | ||||||
|                 System.err.println(t.getMessage()); |  | ||||||
|             } |  | ||||||
|             assertThat("Exception thrown by Synchronizer: " + thrown.get(0).getMessage(), false); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         synchronizer.stop(); |  | ||||||
|         localClient.close(); |  | ||||||
|         remoteClient.close(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,111 +0,0 @@ | ||||||
| package meerkat.bulletinboard; |  | ||||||
| 
 |  | ||||||
| import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer; |  | ||||||
| import meerkat.bulletinboard.sqlserver.H2QueryProvider; |  | ||||||
| import meerkat.comm.CommunicationException; |  | ||||||
| import org.junit.After; |  | ||||||
| import org.junit.Before; |  | ||||||
| import org.junit.Test; |  | ||||||
| 
 |  | ||||||
| import java.security.SignatureException; |  | ||||||
| import java.util.LinkedList; |  | ||||||
| import java.util.List; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel on 6/27/2016. |  | ||||||
|  */ |  | ||||||
| public class CachedBulletinBoardClientTest { |  | ||||||
| 
 |  | ||||||
|     private static final int THREAD_NUM = 3; |  | ||||||
| 
 |  | ||||||
|     private static final String LOCAL_DB_NAME = "localDB"; |  | ||||||
|     private static final String REMOTE_DB_NAME = "remoteDB"; |  | ||||||
|     private static final String QUEUE_DB_NAME = "queueDB"; |  | ||||||
| 
 |  | ||||||
|     private static final int SUBSRCIPTION_DELAY = 500; |  | ||||||
|     private static final int SYNC_DELAY = 500; |  | ||||||
| 
 |  | ||||||
|     // Testers
 |  | ||||||
|     private CachedBulletinBoardClient cachedClient; |  | ||||||
|     private GenericBulletinBoardClientTester clientTest; |  | ||||||
|     private GenericSubscriptionClientTester subscriptionTester; |  | ||||||
| 
 |  | ||||||
|     public CachedBulletinBoardClientTest() throws CommunicationException { |  | ||||||
| 
 |  | ||||||
|         DeletableBulletinBoardServer localServer = new BulletinBoardSQLServer(new H2QueryProvider(LOCAL_DB_NAME)); |  | ||||||
|         localServer.init(); |  | ||||||
|         LocalBulletinBoardClient localClient = new LocalBulletinBoardClient(localServer, THREAD_NUM, SUBSRCIPTION_DELAY); |  | ||||||
| 
 |  | ||||||
|         DeletableBulletinBoardServer remoteServer = new BulletinBoardSQLServer(new H2QueryProvider(REMOTE_DB_NAME)); |  | ||||||
|         remoteServer.init(); |  | ||||||
|         LocalBulletinBoardClient remoteClient = new LocalBulletinBoardClient(remoteServer, THREAD_NUM, SUBSRCIPTION_DELAY); |  | ||||||
| 
 |  | ||||||
|         DeletableBulletinBoardServer queueServer = new BulletinBoardSQLServer(new H2QueryProvider(QUEUE_DB_NAME)); |  | ||||||
|         queueServer.init(); |  | ||||||
|         LocalBulletinBoardClient queueClient = new LocalBulletinBoardClient(queueServer, THREAD_NUM, SUBSRCIPTION_DELAY); |  | ||||||
| 
 |  | ||||||
|         List<SubscriptionBulletinBoardClient> clientList = new LinkedList<>(); |  | ||||||
|         clientList.add(remoteClient); |  | ||||||
| 
 |  | ||||||
|         BulletinBoardSubscriber subscriber = new ThreadedBulletinBoardSubscriber(clientList, localClient); |  | ||||||
| 
 |  | ||||||
|         cachedClient = new CachedBulletinBoardClient(localClient, remoteClient, subscriber, queueClient, SYNC_DELAY, SYNC_DELAY); |  | ||||||
|         subscriptionTester = new GenericSubscriptionClientTester(cachedClient); |  | ||||||
|         clientTest = new GenericBulletinBoardClientTester(cachedClient, 87351); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // Test methods
 |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Takes care of initializing the client and the test resources |  | ||||||
|      */ |  | ||||||
|     @Before |  | ||||||
|     public void init(){ |  | ||||||
| 
 |  | ||||||
|         clientTest.init(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Closes the client and makes sure the test fails when an exception occurred in a separate thread |  | ||||||
|      */ |  | ||||||
| 
 |  | ||||||
|     @After |  | ||||||
|     public void close() { |  | ||||||
| 
 |  | ||||||
|         cachedClient.close(); |  | ||||||
|         clientTest.close(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Test |  | ||||||
|     public void testPost() { |  | ||||||
| 
 |  | ||||||
|         clientTest.testPost(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Test |  | ||||||
|     public void testBatchPost() throws CommunicationException, SignatureException, InterruptedException { |  | ||||||
| 
 |  | ||||||
|         clientTest.testBatchPost(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Test |  | ||||||
|     public void testCompleteBatchPost() throws CommunicationException, SignatureException, InterruptedException { |  | ||||||
| 
 |  | ||||||
|         clientTest.testCompleteBatchPost(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Test |  | ||||||
|     public void testSubscription() throws SignatureException, CommunicationException { |  | ||||||
| 
 |  | ||||||
| //        subscriptionTester.init();
 |  | ||||||
| //        subscriptionTester.subscriptionTest();
 |  | ||||||
| //        subscriptionTester.close();
 |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,497 +0,0 @@ | ||||||
| package meerkat.bulletinboard; |  | ||||||
| 
 |  | ||||||
| import com.google.common.util.concurrent.FutureCallback; |  | ||||||
| import com.google.protobuf.ByteString; |  | ||||||
| import com.google.protobuf.Timestamp; |  | ||||||
| import meerkat.comm.CommunicationException; |  | ||||||
| import meerkat.crypto.concrete.ECDSASignature; |  | ||||||
| import meerkat.crypto.concrete.SHA256Digest; |  | ||||||
| import meerkat.protobuf.BulletinBoardApi.*; |  | ||||||
| import meerkat.protobuf.Crypto; |  | ||||||
| import meerkat.util.BulletinBoardMessageComparator; |  | ||||||
| import meerkat.util.BulletinBoardMessageGenerator; |  | ||||||
| import meerkat.util.BulletinBoardUtils; |  | ||||||
| import meerkat.bulletinboard.AsyncBulletinBoardClient.BatchIdentifier; |  | ||||||
| 
 |  | ||||||
| import java.io.IOException; |  | ||||||
| import java.io.InputStream; |  | ||||||
| import java.security.*; |  | ||||||
| import java.security.cert.CertificateException; |  | ||||||
| import java.util.*; |  | ||||||
| import java.util.concurrent.Semaphore; |  | ||||||
| 
 |  | ||||||
| import static org.hamcrest.CoreMatchers.*; |  | ||||||
| import static org.hamcrest.number.OrderingComparison.greaterThanOrEqualTo; |  | ||||||
| import static org.junit.Assert.*; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 05-Dec-15. |  | ||||||
|  */ |  | ||||||
| public class GenericBulletinBoardClientTester { |  | ||||||
| 
 |  | ||||||
|     // Signature resources
 |  | ||||||
| 
 |  | ||||||
|     private BulletinBoardSignature signers[]; |  | ||||||
|     private ByteString[] signerIDs; |  | ||||||
| 
 |  | ||||||
|     private static String KEYFILE_EXAMPLE = "/certs/enduser-certs/user1-key-with-password-secret.p12"; |  | ||||||
|     private static String KEYFILE_EXAMPLE3 = "/certs/enduser-certs/user3-key-with-password-shh.p12"; |  | ||||||
| 
 |  | ||||||
|     private static String KEYFILE_PASSWORD1 = "secret"; |  | ||||||
|     private static String KEYFILE_PASSWORD3 = "shh"; |  | ||||||
| 
 |  | ||||||
|     private static String CERT1_PEM_EXAMPLE = "/certs/enduser-certs/user1.crt"; |  | ||||||
|     private static String CERT3_PEM_EXAMPLE = "/certs/enduser-certs/user3.crt"; |  | ||||||
| 
 |  | ||||||
|     // Client and callbacks
 |  | ||||||
| 
 |  | ||||||
|     private AsyncBulletinBoardClient bulletinBoardClient; |  | ||||||
| 
 |  | ||||||
|     private PostCallback postCallback; |  | ||||||
| 
 |  | ||||||
|     private RedundancyCallback redundancyCallback; |  | ||||||
|     private ReadCallback readCallback; |  | ||||||
| 
 |  | ||||||
|     // Sync and misc
 |  | ||||||
| 
 |  | ||||||
|     private Semaphore jobSemaphore; |  | ||||||
|     private Vector<Throwable> thrown; |  | ||||||
|     private Random random; |  | ||||||
|     private BulletinBoardMessageGenerator generator; |  | ||||||
| 
 |  | ||||||
|     private BulletinBoardDigest digest; |  | ||||||
| 
 |  | ||||||
|     // Constructor
 |  | ||||||
| 
 |  | ||||||
|     public GenericBulletinBoardClientTester(AsyncBulletinBoardClient bulletinBoardClient, int seed){ |  | ||||||
| 
 |  | ||||||
|         this.bulletinBoardClient = bulletinBoardClient; |  | ||||||
| 
 |  | ||||||
|         signers = new GenericBulletinBoardSignature[2]; |  | ||||||
|         signerIDs = new ByteString[signers.length]; |  | ||||||
|         signers[0] = new GenericBulletinBoardSignature(new ECDSASignature()); |  | ||||||
|         signers[1] = new GenericBulletinBoardSignature(new ECDSASignature()); |  | ||||||
| 
 |  | ||||||
|         InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE); |  | ||||||
|         char[] password = KEYFILE_PASSWORD1.toCharArray(); |  | ||||||
| 
 |  | ||||||
|         KeyStore.Builder keyStoreBuilder; |  | ||||||
|         try { |  | ||||||
|             keyStoreBuilder = signers[0].getPKCS12KeyStoreBuilder(keyStream, password); |  | ||||||
| 
 |  | ||||||
|             signers[0].loadSigningCertificate(keyStoreBuilder); |  | ||||||
| 
 |  | ||||||
|             signers[0].loadVerificationCertificates(getClass().getResourceAsStream(CERT1_PEM_EXAMPLE)); |  | ||||||
| 
 |  | ||||||
|             keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE3); |  | ||||||
|             password = KEYFILE_PASSWORD3.toCharArray(); |  | ||||||
| 
 |  | ||||||
|             keyStoreBuilder = signers[1].getPKCS12KeyStoreBuilder(keyStream, password); |  | ||||||
|             signers[1].loadSigningCertificate(keyStoreBuilder); |  | ||||||
| 
 |  | ||||||
|             signers[1].loadVerificationCertificates(getClass().getResourceAsStream(CERT3_PEM_EXAMPLE)); |  | ||||||
| 
 |  | ||||||
|             for (int i = 0 ; i < signers.length ; i++) { |  | ||||||
|                 signerIDs[i] = signers[i].getSignerID(); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|         } catch (IOException e) { |  | ||||||
|             System.err.println("Failed reading from signature file " + e.getMessage()); |  | ||||||
|             fail("Failed reading from signature file " + e.getMessage()); |  | ||||||
|         } catch (CertificateException e) { |  | ||||||
|             System.err.println("Failed reading certificate " + e.getMessage()); |  | ||||||
|             fail("Failed reading certificate " + e.getMessage()); |  | ||||||
|         } catch (KeyStoreException e) { |  | ||||||
|             System.err.println("Failed reading keystore " + e.getMessage()); |  | ||||||
|             fail("Failed reading keystore " + e.getMessage()); |  | ||||||
|         } catch (NoSuchAlgorithmException e) { |  | ||||||
|             System.err.println("Couldn't find signing algorithm " + e.getMessage()); |  | ||||||
|             fail("Couldn't find signing algorithm " + e.getMessage()); |  | ||||||
|         } catch (UnrecoverableKeyException e) { |  | ||||||
|             System.err.println("Couldn't find signing key " + e.getMessage()); |  | ||||||
|             fail("Couldn't find signing key " + e.getMessage()); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         this.random = new Random(seed); |  | ||||||
|         this.generator = new BulletinBoardMessageGenerator(random); |  | ||||||
|         this.digest = new GenericBulletinBoardDigest(new SHA256Digest()); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // Callback definitions
 |  | ||||||
| 
 |  | ||||||
|     protected void genericHandleFailure(Throwable t){ |  | ||||||
|         System.err.println(t.getCause() + " " + t.getMessage()); |  | ||||||
|         thrown.add(t); |  | ||||||
|         jobSemaphore.release(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private class PostCallback implements FutureCallback<Boolean>{ |  | ||||||
| 
 |  | ||||||
|         private boolean isAssert; |  | ||||||
|         private boolean assertValue; |  | ||||||
| 
 |  | ||||||
|         public PostCallback() { |  | ||||||
|             this(false); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public PostCallback(boolean isAssert) { |  | ||||||
|             this(isAssert,true); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public PostCallback(boolean isAssert, boolean assertValue) { |  | ||||||
|             this.isAssert = isAssert; |  | ||||||
|             this.assertValue = assertValue; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onSuccess(Boolean msg) { |  | ||||||
| 
 |  | ||||||
|             System.err.println("Post operation completed"); |  | ||||||
| 
 |  | ||||||
|             if (isAssert) { |  | ||||||
|                 if (assertValue && !msg) { |  | ||||||
|                     genericHandleFailure(new AssertionError("Post operation failed")); |  | ||||||
|                 } else if (!assertValue && msg){ |  | ||||||
|                     genericHandleFailure(new AssertionError("Post operation succeeded unexpectedly")); |  | ||||||
|                 } else { |  | ||||||
|                     jobSemaphore.release(); |  | ||||||
|                 } |  | ||||||
|             } else { |  | ||||||
|                 jobSemaphore.release(); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onFailure(Throwable t) { |  | ||||||
|             genericHandleFailure(t); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private class RedundancyCallback implements FutureCallback<Float>{ |  | ||||||
| 
 |  | ||||||
|         private float minRedundancy; |  | ||||||
| 
 |  | ||||||
|         public RedundancyCallback(float minRedundancy) { |  | ||||||
|             this.minRedundancy = minRedundancy; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onSuccess(Float redundancy) { |  | ||||||
|             System.err.println("Redundancy found is: " + redundancy); |  | ||||||
|             jobSemaphore.release(); |  | ||||||
|             assertThat(redundancy, greaterThanOrEqualTo(minRedundancy)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onFailure(Throwable t) { |  | ||||||
|             genericHandleFailure(t); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private class ReadCallback implements FutureCallback<List<BulletinBoardMessage>>{ |  | ||||||
| 
 |  | ||||||
|         private List<BulletinBoardMessage> expectedMsgList; |  | ||||||
| 
 |  | ||||||
|         public ReadCallback(List<BulletinBoardMessage> expectedMsgList) { |  | ||||||
|             this.expectedMsgList = expectedMsgList; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onSuccess(List<BulletinBoardMessage> messages) { |  | ||||||
| 
 |  | ||||||
|             System.err.println(messages); |  | ||||||
|             jobSemaphore.release(); |  | ||||||
| 
 |  | ||||||
|             BulletinBoardMessageComparator msgComparator = new BulletinBoardMessageComparator(); |  | ||||||
| 
 |  | ||||||
|             assertThat(messages.size(), is(expectedMsgList.size())); |  | ||||||
| 
 |  | ||||||
|             Iterator<BulletinBoardMessage> expectedMessageIterator = expectedMsgList.iterator(); |  | ||||||
|             Iterator<BulletinBoardMessage> receivedMessageIterator = messages.iterator(); |  | ||||||
| 
 |  | ||||||
|             while (expectedMessageIterator.hasNext()) { |  | ||||||
|                 assertThat(msgComparator.compare(expectedMessageIterator.next(), receivedMessageIterator.next()), is(0)); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onFailure(Throwable t) { |  | ||||||
|             genericHandleFailure(t); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private class ReadBatchCallback implements FutureCallback<BulletinBoardMessage>{ |  | ||||||
| 
 |  | ||||||
|         private BulletinBoardMessage expectedMsg; |  | ||||||
| 
 |  | ||||||
|         public ReadBatchCallback(BulletinBoardMessage expectedMsg) { |  | ||||||
|             this.expectedMsg = expectedMsg; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onSuccess(BulletinBoardMessage msg) { |  | ||||||
| 
 |  | ||||||
|             BulletinBoardMessageComparator msgComparator = new BulletinBoardMessageComparator(); |  | ||||||
| 
 |  | ||||||
|             if (msgComparator.compare(msg, expectedMsg) != 0) { |  | ||||||
|                 genericHandleFailure(new AssertionError("Batch read returned different message.\nExpected:" + expectedMsg + "\nRecieved:" + msg + "\n")); |  | ||||||
|             } else { |  | ||||||
|                 jobSemaphore.release(); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onFailure(Throwable t) { |  | ||||||
|             genericHandleFailure(t); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // Test methods
 |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Takes care of initializing the client and the test resources |  | ||||||
|      */ |  | ||||||
|     public void init(){ |  | ||||||
| 
 |  | ||||||
|         random = new Random(0); // We use insecure randomness in tests for repeatability
 |  | ||||||
| 
 |  | ||||||
|         postCallback = new PostCallback(); |  | ||||||
|         redundancyCallback = new RedundancyCallback((float) 1.0); |  | ||||||
| 
 |  | ||||||
|         thrown = new Vector<>(); |  | ||||||
|         jobSemaphore = new Semaphore(0); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Closes the client and makes sure the test fails when an exception occurred in a separate thread |  | ||||||
|      */ |  | ||||||
| 
 |  | ||||||
|     public void close() { |  | ||||||
| 
 |  | ||||||
|         if (thrown.size() > 0) { |  | ||||||
| 
 |  | ||||||
|             for (Throwable t : thrown){ |  | ||||||
|                 System.err.println(t.getMessage()); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             assert false; |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Tests the standard post, redundancy and read methods |  | ||||||
|      */ |  | ||||||
|     public void testPost() { |  | ||||||
| 
 |  | ||||||
|         byte[] b1 = {(byte) 1, (byte) 2, (byte) 3, (byte) 4}; |  | ||||||
|         byte[] b2 = {(byte) 11, (byte) 12, (byte) 13, (byte) 14}; |  | ||||||
|         byte[] b3 = {(byte) 21, (byte) 22, (byte) 23, (byte) 24}; |  | ||||||
| 
 |  | ||||||
|         BulletinBoardMessage msg; |  | ||||||
| 
 |  | ||||||
|         MessageFilterList filterList; |  | ||||||
|         List<BulletinBoardMessage> msgList; |  | ||||||
| 
 |  | ||||||
|         MessageID messageID; |  | ||||||
| 
 |  | ||||||
|         msg = BulletinBoardMessage.newBuilder() |  | ||||||
|                 .setMsg(UnsignedBulletinBoardMessage.newBuilder() |  | ||||||
|                         .addTag("Signature") |  | ||||||
|                         .addTag("Trustee") |  | ||||||
|                         .setData(ByteString.copyFrom(b1)) |  | ||||||
|                         .setTimestamp(Timestamp.newBuilder() |  | ||||||
|                                 .setSeconds(20) |  | ||||||
|                                 .setNanos(30) |  | ||||||
|                                 .build()) |  | ||||||
|                         .build()) |  | ||||||
|                 .addSig(Crypto.Signature.newBuilder() |  | ||||||
|                         .setType(Crypto.SignatureType.DSA) |  | ||||||
|                         .setData(ByteString.copyFrom(b2)) |  | ||||||
|                         .setSignerId(ByteString.copyFrom(b3)) |  | ||||||
|                         .build()) |  | ||||||
|                 .addSig(Crypto.Signature.newBuilder() |  | ||||||
|                         .setType(Crypto.SignatureType.ECDSA) |  | ||||||
|                         .setData(ByteString.copyFrom(b3)) |  | ||||||
|                         .setSignerId(ByteString.copyFrom(b2)) |  | ||||||
|                         .build()) |  | ||||||
|                 .build(); |  | ||||||
| 
 |  | ||||||
|         messageID = bulletinBoardClient.postMessage(msg,postCallback); |  | ||||||
| 
 |  | ||||||
|         try { |  | ||||||
|             jobSemaphore.acquire(); |  | ||||||
|         } catch (InterruptedException e) { |  | ||||||
|             System.err.println(e.getCause() + " " + e.getMessage()); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         bulletinBoardClient.getRedundancy(messageID,redundancyCallback); |  | ||||||
| 
 |  | ||||||
|         filterList = MessageFilterList.newBuilder() |  | ||||||
|                 .addFilter( |  | ||||||
|                         MessageFilter.newBuilder() |  | ||||||
|                                 .setType(FilterType.TAG) |  | ||||||
|                                 .setTag("Signature") |  | ||||||
|                                 .build() |  | ||||||
|                 ) |  | ||||||
|                 .addFilter( |  | ||||||
|                         MessageFilter.newBuilder() |  | ||||||
|                                 .setType(FilterType.TAG) |  | ||||||
|                                 .setTag("Trustee") |  | ||||||
|                                 .build() |  | ||||||
|                 ) |  | ||||||
|                 .build(); |  | ||||||
| 
 |  | ||||||
|         msgList = new LinkedList<>(); |  | ||||||
|         msgList.add(msg); |  | ||||||
| 
 |  | ||||||
|         readCallback = new ReadCallback(msgList); |  | ||||||
| 
 |  | ||||||
|         bulletinBoardClient.readMessages(filterList, readCallback); |  | ||||||
|         try { |  | ||||||
|             jobSemaphore.acquire(2); |  | ||||||
|         } catch (InterruptedException e) { |  | ||||||
|             System.err.println(e.getCause() + " " + e.getMessage()); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Tests posting a batch by parts |  | ||||||
|      * @throws CommunicationException, SignatureException, InterruptedException |  | ||||||
|      */ |  | ||||||
|     public void testBatchPost() throws CommunicationException, SignatureException, InterruptedException { |  | ||||||
| 
 |  | ||||||
|         final int BATCH_LENGTH = 100; |  | ||||||
|         final int CHUNK_SIZE = 10; |  | ||||||
|         final int TAG_NUM = 10; |  | ||||||
| 
 |  | ||||||
|         final Timestamp timestamp = Timestamp.newBuilder() |  | ||||||
|                 .setSeconds(141515) |  | ||||||
|                 .setNanos(859018) |  | ||||||
|                 .build(); |  | ||||||
| 
 |  | ||||||
|         final BulletinBoardMessage msg = generator.generateRandomMessage(signers, timestamp, BATCH_LENGTH, TAG_NUM); |  | ||||||
| 
 |  | ||||||
|         // Begin batch
 |  | ||||||
| 
 |  | ||||||
|         bulletinBoardClient.beginBatch(msg.getMsg().getTagList(), new FutureCallback<BatchIdentifier>() { |  | ||||||
| 
 |  | ||||||
|             @Override |  | ||||||
|             public void onSuccess(final BatchIdentifier identifier) { |  | ||||||
| 
 |  | ||||||
|                 bulletinBoardClient.postBatchData(identifier, BulletinBoardUtils.breakToBatch(msg, CHUNK_SIZE), new FutureCallback<Boolean>() { |  | ||||||
| 
 |  | ||||||
|                     @Override |  | ||||||
|                     public void onSuccess(Boolean result) { |  | ||||||
| 
 |  | ||||||
|                         bulletinBoardClient.closeBatch(identifier, msg.getMsg().getTimestamp(), msg.getSigList(), new FutureCallback<Boolean>() { |  | ||||||
|                             @Override |  | ||||||
|                             public void onSuccess(Boolean result) { |  | ||||||
|                                 jobSemaphore.release(); |  | ||||||
|                             } |  | ||||||
| 
 |  | ||||||
|                             @Override |  | ||||||
|                             public void onFailure(Throwable t) { |  | ||||||
|                                 genericHandleFailure(t); |  | ||||||
|                             } |  | ||||||
|                         }); |  | ||||||
| 
 |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     @Override |  | ||||||
|                     public void onFailure(Throwable t) { |  | ||||||
|                         genericHandleFailure(t); |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                 }); |  | ||||||
| 
 |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             @Override |  | ||||||
|             public void onFailure(Throwable t) { |  | ||||||
|                 genericHandleFailure(t); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         jobSemaphore.acquire(); |  | ||||||
| 
 |  | ||||||
|         digest.reset(); |  | ||||||
|         digest.update(msg); |  | ||||||
| 
 |  | ||||||
|         bulletinBoardClient.readMessage(digest.digestAsMessageID(), new ReadBatchCallback(msg)); |  | ||||||
| 
 |  | ||||||
|         jobSemaphore.acquire(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Posts a complete batch message |  | ||||||
|      * Checks reading of the message in two parts |  | ||||||
|      * @throws CommunicationException, SignatureException, InterruptedException |  | ||||||
|      */ |  | ||||||
|     public void testCompleteBatchPost() throws CommunicationException, SignatureException, InterruptedException { |  | ||||||
| 
 |  | ||||||
|         final int BATCH_LENGTH = 100; |  | ||||||
|         final int CHUNK_SIZE = 99; |  | ||||||
|         final int TAG_NUM = 8; |  | ||||||
| 
 |  | ||||||
|         final Timestamp timestamp = Timestamp.newBuilder() |  | ||||||
|                 .setSeconds(7776151) |  | ||||||
|                 .setNanos(252616) |  | ||||||
|                 .build(); |  | ||||||
| 
 |  | ||||||
|         final BulletinBoardMessage msg = generator.generateRandomMessage(signers, timestamp, BATCH_LENGTH, TAG_NUM); |  | ||||||
| 
 |  | ||||||
|         // Post batch
 |  | ||||||
| 
 |  | ||||||
|         MessageID msgID = bulletinBoardClient.postAsBatch(msg, CHUNK_SIZE, postCallback); |  | ||||||
| 
 |  | ||||||
|         jobSemaphore.acquire(); |  | ||||||
| 
 |  | ||||||
|         // Read batch
 |  | ||||||
| 
 |  | ||||||
|         MessageFilterList filterList = MessageFilterList.newBuilder() |  | ||||||
|                 .addFilter(MessageFilter.newBuilder() |  | ||||||
|                         .setType(FilterType.MSG_ID) |  | ||||||
|                         .setId(msgID.getID()) |  | ||||||
|                         .build()) |  | ||||||
|                 .build(); |  | ||||||
| 
 |  | ||||||
|         bulletinBoardClient.readMessages(filterList, new FutureCallback<List<BulletinBoardMessage>>() { |  | ||||||
| 
 |  | ||||||
|             @Override |  | ||||||
|             public void onSuccess(List<BulletinBoardMessage> msgList) { |  | ||||||
| 
 |  | ||||||
|                 if (msgList.size() != 1) { |  | ||||||
| 
 |  | ||||||
|                     genericHandleFailure(new AssertionError("Wrong number of stubs returned. Expected: 1; Found: " + msgList.size())); |  | ||||||
| 
 |  | ||||||
|                 } else { |  | ||||||
| 
 |  | ||||||
|                     BulletinBoardMessage retrievedMsg = msgList.get(0); |  | ||||||
|                     bulletinBoardClient.readBatchData(retrievedMsg, new ReadBatchCallback(msg)); |  | ||||||
| 
 |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             @Override |  | ||||||
|             public void onFailure(Throwable t) { |  | ||||||
|                 genericHandleFailure(t); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         jobSemaphore.acquire(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,226 +0,0 @@ | ||||||
| package meerkat.bulletinboard; |  | ||||||
| 
 |  | ||||||
| import com.google.common.util.concurrent.FutureCallback; |  | ||||||
| import com.google.protobuf.ByteString; |  | ||||||
| import com.google.protobuf.Timestamp; |  | ||||||
| import meerkat.comm.CommunicationException; |  | ||||||
| import meerkat.crypto.concrete.ECDSASignature; |  | ||||||
| import meerkat.protobuf.BulletinBoardApi.*; |  | ||||||
| import meerkat.util.BulletinBoardMessageComparator; |  | ||||||
| import meerkat.util.BulletinBoardMessageGenerator; |  | ||||||
| 
 |  | ||||||
| import java.io.IOException; |  | ||||||
| import java.io.InputStream; |  | ||||||
| import java.security.*; |  | ||||||
| import java.security.cert.CertificateException; |  | ||||||
| import java.util.*; |  | ||||||
| import java.util.concurrent.Semaphore; |  | ||||||
| 
 |  | ||||||
| import static org.junit.Assert.fail; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 22-Mar-16. |  | ||||||
|  */ |  | ||||||
| public class GenericSubscriptionClientTester { |  | ||||||
| 
 |  | ||||||
|     private BulletinBoardSignature signers[]; |  | ||||||
|     private ByteString[] signerIDs; |  | ||||||
| 
 |  | ||||||
|     private static String KEYFILE_EXAMPLE = "/certs/enduser-certs/user1-key-with-password-secret.p12"; |  | ||||||
|     private static String KEYFILE_EXAMPLE3 = "/certs/enduser-certs/user3-key-with-password-shh.p12"; |  | ||||||
| 
 |  | ||||||
|     private static String KEYFILE_PASSWORD1 = "secret"; |  | ||||||
|     private static String KEYFILE_PASSWORD3 = "shh"; |  | ||||||
| 
 |  | ||||||
|     private static String CERT1_PEM_EXAMPLE = "/certs/enduser-certs/user1.crt"; |  | ||||||
|     private static String CERT3_PEM_EXAMPLE = "/certs/enduser-certs/user3.crt"; |  | ||||||
| 
 |  | ||||||
|     private SubscriptionBulletinBoardClient bulletinBoardClient; |  | ||||||
| 
 |  | ||||||
|     private Random random; |  | ||||||
|     private BulletinBoardMessageGenerator generator; |  | ||||||
| 
 |  | ||||||
|     private Semaphore jobSemaphore; |  | ||||||
|     private Vector<Throwable> thrown; |  | ||||||
| 
 |  | ||||||
|     public GenericSubscriptionClientTester(SubscriptionBulletinBoardClient bulletinBoardClient){ |  | ||||||
| 
 |  | ||||||
|         this.bulletinBoardClient = bulletinBoardClient; |  | ||||||
| 
 |  | ||||||
|         signers = new BulletinBoardSignature[2]; |  | ||||||
|         signerIDs = new ByteString[signers.length]; |  | ||||||
|         signers[0] = new GenericBulletinBoardSignature(new ECDSASignature()); |  | ||||||
|         signers[1] = new GenericBulletinBoardSignature(new ECDSASignature()); |  | ||||||
| 
 |  | ||||||
|         InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE); |  | ||||||
|         char[] password = KEYFILE_PASSWORD1.toCharArray(); |  | ||||||
| 
 |  | ||||||
|         KeyStore.Builder keyStoreBuilder; |  | ||||||
|         try { |  | ||||||
|             keyStoreBuilder = signers[0].getPKCS12KeyStoreBuilder(keyStream, password); |  | ||||||
| 
 |  | ||||||
|             signers[0].loadSigningCertificate(keyStoreBuilder); |  | ||||||
| 
 |  | ||||||
|             signers[0].loadVerificationCertificates(getClass().getResourceAsStream(CERT1_PEM_EXAMPLE)); |  | ||||||
| 
 |  | ||||||
|             keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE3); |  | ||||||
|             password = KEYFILE_PASSWORD3.toCharArray(); |  | ||||||
| 
 |  | ||||||
|             keyStoreBuilder = signers[1].getPKCS12KeyStoreBuilder(keyStream, password); |  | ||||||
|             signers[1].loadSigningCertificate(keyStoreBuilder); |  | ||||||
| 
 |  | ||||||
|             signers[1].loadVerificationCertificates(getClass().getResourceAsStream(CERT3_PEM_EXAMPLE)); |  | ||||||
| 
 |  | ||||||
|             for (int i = 0 ; i < signers.length ; i++) { |  | ||||||
|                 signerIDs[i] = signers[i].getSignerID(); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|         } catch (IOException e) { |  | ||||||
|             System.err.println("Failed reading from signature file " + e.getMessage()); |  | ||||||
|             fail("Failed reading from signature file " + e.getMessage()); |  | ||||||
|         } catch (CertificateException e) { |  | ||||||
|             System.err.println("Failed reading certificate " + e.getMessage()); |  | ||||||
|             fail("Failed reading certificate " + e.getMessage()); |  | ||||||
|         } catch (KeyStoreException e) { |  | ||||||
|             System.err.println("Failed reading keystore " + e.getMessage()); |  | ||||||
|             fail("Failed reading keystore " + e.getMessage()); |  | ||||||
|         } catch (NoSuchAlgorithmException e) { |  | ||||||
|             System.err.println("Couldn't find signing algorithm " + e.getMessage()); |  | ||||||
|             fail("Couldn't find signing algorithm " + e.getMessage()); |  | ||||||
|         } catch (UnrecoverableKeyException e) { |  | ||||||
|             System.err.println("Couldn't find signing key " + e.getMessage()); |  | ||||||
|             fail("Couldn't find signing key " + e.getMessage()); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Takes care of initializing the client and the test resources |  | ||||||
|      */ |  | ||||||
|     public void init(){ |  | ||||||
| 
 |  | ||||||
|         random = new Random(0); // We use insecure randomness in tests for repeatability
 |  | ||||||
|         generator = new BulletinBoardMessageGenerator(random); |  | ||||||
| 
 |  | ||||||
|         thrown = new Vector<>(); |  | ||||||
|         jobSemaphore = new Semaphore(0); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Closes the client and makes sure the test fails when an exception occurred in a separate thread |  | ||||||
|      */ |  | ||||||
| 
 |  | ||||||
|     public void close() { |  | ||||||
| 
 |  | ||||||
|         if (thrown.size() > 0) { |  | ||||||
|             assert false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private class SubscriptionCallback implements FutureCallback<List<BulletinBoardMessage>>{ |  | ||||||
| 
 |  | ||||||
|         private int stage; |  | ||||||
|         private final List<List<BulletinBoardMessage>> expectedMessages; |  | ||||||
|         private final List<BulletinBoardMessage> messagesToPost; |  | ||||||
|         private final BulletinBoardMessageComparator comparator; |  | ||||||
| 
 |  | ||||||
|         public SubscriptionCallback(List<List<BulletinBoardMessage>> expectedMessages, List<BulletinBoardMessage> messagesToPost) { |  | ||||||
| 
 |  | ||||||
|             this.expectedMessages = expectedMessages; |  | ||||||
|             this.messagesToPost = messagesToPost; |  | ||||||
|             this.stage = 0; |  | ||||||
|             this.comparator = new BulletinBoardMessageComparator(); |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onSuccess(List<BulletinBoardMessage> result) { |  | ||||||
| 
 |  | ||||||
|             if (stage >= expectedMessages.size()) |  | ||||||
|                 return; |  | ||||||
| 
 |  | ||||||
|             // Check for consistency
 |  | ||||||
| 
 |  | ||||||
|             List<BulletinBoardMessage> expectedMsgList = expectedMessages.get(stage); |  | ||||||
| 
 |  | ||||||
|             if (expectedMsgList.size() != result.size()){ |  | ||||||
|                 onFailure(new AssertionError("Received wrong number of messages")); |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             Iterator<BulletinBoardMessage> expectedMessageIterator = expectedMsgList.iterator(); |  | ||||||
|             Iterator<BulletinBoardMessage> receivedMessageIterator = result.iterator(); |  | ||||||
| 
 |  | ||||||
|             while (expectedMessageIterator.hasNext()) { |  | ||||||
|                 if(comparator.compare(expectedMessageIterator.next(), receivedMessageIterator.next()) != 0){ |  | ||||||
|                     onFailure(new AssertionError("Received unexpected message")); |  | ||||||
|                     return; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             // Post new message
 |  | ||||||
|             try { |  | ||||||
|                 if (stage < messagesToPost.size()) { |  | ||||||
|                     bulletinBoardClient.postMessage(messagesToPost.get(stage)); |  | ||||||
|                 } |  | ||||||
|             } catch (CommunicationException e) { |  | ||||||
|                 onFailure(e); |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             stage++; |  | ||||||
|             jobSemaphore.release(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void onFailure(Throwable t) { |  | ||||||
|             System.err.println(t.getCause() + " " + t.getMessage()); |  | ||||||
|             thrown.add(t); |  | ||||||
|             jobSemaphore.release(); |  | ||||||
|             stage = expectedMessages.size(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public void subscriptionTest() throws SignatureException, CommunicationException { |  | ||||||
| 
 |  | ||||||
|         final String COMMON_TAG = "SUBSCRIPTION_TEST"; |  | ||||||
|          |  | ||||||
|         List<String> tags = new LinkedList<>(); |  | ||||||
|         tags.add(COMMON_TAG); |  | ||||||
| 
 |  | ||||||
|         BulletinBoardMessage msg1 = generator.generateRandomMessage(signers, Timestamp.newBuilder().setSeconds(1000).setNanos(900).build(), 10, 4, tags); |  | ||||||
|         BulletinBoardMessage msg2 = generator.generateRandomMessage(signers, Timestamp.newBuilder().setSeconds(800).setNanos(300).build(), 10, 4); |  | ||||||
|         BulletinBoardMessage msg3 = generator.generateRandomMessage(signers, Timestamp.newBuilder().setSeconds(2000).setNanos(0).build(), 10, 4, tags); |  | ||||||
| 
 |  | ||||||
|         MessageFilterList filterList = MessageFilterList.newBuilder() |  | ||||||
|                 .addFilter(MessageFilter.newBuilder() |  | ||||||
|                         .setType(FilterType.TAG) |  | ||||||
|                         .setTag(COMMON_TAG) |  | ||||||
|                         .build()) |  | ||||||
|                 .build(); |  | ||||||
| 
 |  | ||||||
|         List<List<BulletinBoardMessage>> expectedMessages = new ArrayList<>(3); |  | ||||||
|         expectedMessages.add(new LinkedList<>()); |  | ||||||
|         expectedMessages.add(new LinkedList<>()); |  | ||||||
|         expectedMessages.add(new LinkedList<>()); |  | ||||||
|         expectedMessages.get(0).add(msg1); |  | ||||||
|         expectedMessages.get(2).add(msg3); |  | ||||||
| 
 |  | ||||||
|         List<BulletinBoardMessage> messagesToPost = new ArrayList<>(2); |  | ||||||
|         messagesToPost.add(msg2); |  | ||||||
|         messagesToPost.add(msg3); |  | ||||||
| 
 |  | ||||||
|         bulletinBoardClient.postMessage(msg1); |  | ||||||
|         bulletinBoardClient.subscribe(filterList, new SubscriptionCallback(expectedMessages, messagesToPost)); |  | ||||||
| 
 |  | ||||||
|         try { |  | ||||||
|             jobSemaphore.acquire(3); |  | ||||||
|         } catch (InterruptedException e) { |  | ||||||
|             System.err.println(e.getCause() + " " + e.getMessage()); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,90 +0,0 @@ | ||||||
| package meerkat.bulletinboard; |  | ||||||
| 
 |  | ||||||
| import meerkat.bulletinboard.sqlserver.*; |  | ||||||
| import meerkat.comm.CommunicationException; |  | ||||||
| import org.junit.After; |  | ||||||
| import org.junit.Before; |  | ||||||
| import org.junit.Test; |  | ||||||
| 
 |  | ||||||
| import java.security.SignatureException; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 05-Dec-15. |  | ||||||
|  */ |  | ||||||
| public class LocalBulletinBoardClientTest { |  | ||||||
| 
 |  | ||||||
|     private static final int THREAD_NUM = 3; |  | ||||||
|     private static final String DB_NAME = "TestDB"; |  | ||||||
| 
 |  | ||||||
|     private static final int SUBSRCIPTION_DELAY = 3000; |  | ||||||
| 
 |  | ||||||
|     // Testers
 |  | ||||||
|     private GenericBulletinBoardClientTester clientTest; |  | ||||||
|     private GenericSubscriptionClientTester subscriptionTester; |  | ||||||
| 
 |  | ||||||
|     public LocalBulletinBoardClientTest() throws CommunicationException { |  | ||||||
| 
 |  | ||||||
|         H2QueryProvider queryProvider = new H2QueryProvider(DB_NAME); |  | ||||||
| 
 |  | ||||||
|         DeletableBulletinBoardServer server = new BulletinBoardSQLServer(queryProvider); |  | ||||||
|         server.init(); |  | ||||||
| 
 |  | ||||||
|         LocalBulletinBoardClient client = new LocalBulletinBoardClient(server, THREAD_NUM, SUBSRCIPTION_DELAY); |  | ||||||
|         subscriptionTester = new GenericSubscriptionClientTester(client); |  | ||||||
|         clientTest = new GenericBulletinBoardClientTester(client, 98354); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // Test methods
 |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Takes care of initializing the client and the test resources |  | ||||||
|      */ |  | ||||||
|     @Before |  | ||||||
|     public void init(){ |  | ||||||
| 
 |  | ||||||
|         clientTest.init(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Closes the client and makes sure the test fails when an exception occurred in a separate thread |  | ||||||
|      */ |  | ||||||
| 
 |  | ||||||
|     @After |  | ||||||
|     public void close() { |  | ||||||
| 
 |  | ||||||
|         clientTest.close(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Test |  | ||||||
|     public void testPost() { |  | ||||||
| 
 |  | ||||||
|         clientTest.testPost(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Test |  | ||||||
|     public void testBatchPost() throws CommunicationException, SignatureException, InterruptedException { |  | ||||||
| 
 |  | ||||||
|         clientTest.testBatchPost(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Test |  | ||||||
|     public void testCompleteBatchPost() throws CommunicationException, SignatureException, InterruptedException { |  | ||||||
| 
 |  | ||||||
|         clientTest.testCompleteBatchPost(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Test |  | ||||||
|     public void testSubscription() throws SignatureException, CommunicationException { |  | ||||||
| 
 |  | ||||||
|         subscriptionTester.init(); |  | ||||||
|         subscriptionTester.subscriptionTest(); |  | ||||||
|         subscriptionTester.close(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,104 +0,0 @@ | ||||||
| package meerkat.bulletinboard; |  | ||||||
| 
 |  | ||||||
| import com.google.common.util.concurrent.ListeningExecutorService; |  | ||||||
| import com.google.common.util.concurrent.MoreExecutors; |  | ||||||
| import meerkat.comm.CommunicationException; |  | ||||||
| import meerkat.protobuf.Voting.BulletinBoardClientParams; |  | ||||||
| import org.junit.After; |  | ||||||
| import org.junit.Before; |  | ||||||
| import org.junit.Test; |  | ||||||
| 
 |  | ||||||
| import java.security.SignatureException; |  | ||||||
| import java.util.LinkedList; |  | ||||||
| import java.util.List; |  | ||||||
| import java.util.concurrent.Executors; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 05-Dec-15. |  | ||||||
|  */ |  | ||||||
| public class SingleServerBulletinBoardClientIntegrationTest { |  | ||||||
| 
 |  | ||||||
|     // Server data
 |  | ||||||
| 
 |  | ||||||
|     private static final String PROP_GETTY_URL = "gretty.httpBaseURI"; |  | ||||||
|     private static final String DEFAULT_BASE_URL = "http://localhost:8081"; |  | ||||||
|     private static final String BASE_URL =  System.getProperty(PROP_GETTY_URL, DEFAULT_BASE_URL); |  | ||||||
| 
 |  | ||||||
|     private static final int THREAD_NUM = 3; |  | ||||||
|     private static final long FAIL_DELAY = 3000; |  | ||||||
|     private static final long SUBSCRIPTION_INTERVAL = 500; |  | ||||||
| 
 |  | ||||||
|     // Testers
 |  | ||||||
|     private GenericBulletinBoardClientTester clientTest; |  | ||||||
|     private GenericSubscriptionClientTester subscriptionTester; |  | ||||||
| 
 |  | ||||||
|     public SingleServerBulletinBoardClientIntegrationTest(){ |  | ||||||
| 
 |  | ||||||
|         SingleServerBulletinBoardClient client = new SingleServerBulletinBoardClient(THREAD_NUM, FAIL_DELAY, SUBSCRIPTION_INTERVAL); |  | ||||||
| 
 |  | ||||||
|         List<String> testDB = new LinkedList<>(); |  | ||||||
|         testDB.add(BASE_URL); |  | ||||||
| 
 |  | ||||||
|         client.init(BulletinBoardClientParams.newBuilder() |  | ||||||
|                 .addAllBulletinBoardAddress(testDB) |  | ||||||
|                 .setMinRedundancy((float) 1.0) |  | ||||||
|                 .build()); |  | ||||||
| 
 |  | ||||||
|         clientTest = new GenericBulletinBoardClientTester(client, 981541); |  | ||||||
|         subscriptionTester = new GenericSubscriptionClientTester(client); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // Test methods
 |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Takes care of initializing the client and the test resources |  | ||||||
|      */ |  | ||||||
|     @Before |  | ||||||
|     public void init(){ |  | ||||||
| 
 |  | ||||||
|         clientTest.init(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Closes the client and makes sure the test fails when an exception occurred in a separate thread |  | ||||||
|      */ |  | ||||||
| 
 |  | ||||||
|     @After |  | ||||||
|     public void close() { |  | ||||||
| 
 |  | ||||||
|         clientTest.close(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Test |  | ||||||
|     public void testPost() { |  | ||||||
| 
 |  | ||||||
|         clientTest.testPost(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Test |  | ||||||
|     public void testBatchPost() throws CommunicationException, SignatureException, InterruptedException { |  | ||||||
| 
 |  | ||||||
|         clientTest.testBatchPost(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Test |  | ||||||
|     public void testCompleteBatchPost() throws CommunicationException, SignatureException, InterruptedException { |  | ||||||
| 
 |  | ||||||
|         clientTest.testCompleteBatchPost(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Test |  | ||||||
|     public void testSubscription() throws SignatureException, CommunicationException { |  | ||||||
| 
 |  | ||||||
|         subscriptionTester.init(); |  | ||||||
|         subscriptionTester.subscriptionTest(); |  | ||||||
|         subscriptionTester.close(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,89 +0,0 @@ | ||||||
| package meerkat.bulletinboard; |  | ||||||
| 
 |  | ||||||
| import meerkat.comm.CommunicationException; |  | ||||||
| 
 |  | ||||||
| import meerkat.protobuf.Voting.*; |  | ||||||
| 
 |  | ||||||
| import org.junit.After; |  | ||||||
| import org.junit.Before; |  | ||||||
| import org.junit.Test; |  | ||||||
| 
 |  | ||||||
| import java.security.SignatureException; |  | ||||||
| import java.util.LinkedList; |  | ||||||
| import java.util.List; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Created by Arbel Deutsch Peled on 05-Dec-15. |  | ||||||
|  */ |  | ||||||
| public class ThreadedBulletinBoardClientIntegrationTest { |  | ||||||
| 
 |  | ||||||
|     // Server data
 |  | ||||||
| 
 |  | ||||||
|     private static String PROP_GETTY_URL = "gretty.httpBaseURI"; |  | ||||||
|     private static String DEFAULT_BASE_URL = "http://localhost:8081"; |  | ||||||
|     private static String BASE_URL =  System.getProperty(PROP_GETTY_URL, DEFAULT_BASE_URL); |  | ||||||
| 
 |  | ||||||
|     // Tester
 |  | ||||||
|     private GenericBulletinBoardClientTester clientTest; |  | ||||||
| 
 |  | ||||||
|     public ThreadedBulletinBoardClientIntegrationTest(){ |  | ||||||
| 
 |  | ||||||
|         ThreadedBulletinBoardClient client = new ThreadedBulletinBoardClient(3,0,500); |  | ||||||
| 
 |  | ||||||
|         List<String> testDB = new LinkedList<>(); |  | ||||||
|         testDB.add(BASE_URL); |  | ||||||
| 
 |  | ||||||
|         client.init(BulletinBoardClientParams.newBuilder() |  | ||||||
|                 .addAllBulletinBoardAddress(testDB) |  | ||||||
|                 .setMinRedundancy((float) 1.0) |  | ||||||
|                 .build()); |  | ||||||
| 
 |  | ||||||
|         clientTest = new GenericBulletinBoardClientTester(client, 52351); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // Test methods
 |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Takes care of initializing the client and the test resources |  | ||||||
|      */ |  | ||||||
|     @Before |  | ||||||
|     public void init(){ |  | ||||||
| 
 |  | ||||||
|         clientTest.init(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Closes the client and makes sure the test fails when an exception occurred in a separate thread |  | ||||||
|      */ |  | ||||||
| 
 |  | ||||||
|     @After |  | ||||||
|     public void close() { |  | ||||||
| 
 |  | ||||||
|         clientTest.close(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Test |  | ||||||
|     public void testPost() { |  | ||||||
| 
 |  | ||||||
|         clientTest.testPost(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Test |  | ||||||
|     public void testBatchPost() throws CommunicationException, SignatureException, InterruptedException { |  | ||||||
| 
 |  | ||||||
|         clientTest.testBatchPost(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Test |  | ||||||
|     public void testCompleteBatchPost() throws CommunicationException, SignatureException, InterruptedException { |  | ||||||
| 
 |  | ||||||
|         clientTest.testCompleteBatchPost(); |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | @ -1,57 +0,0 @@ | ||||||
| { |  | ||||||
|   "$schema": "./node_modules/@angular/cli/lib/config/schema.json", |  | ||||||
|   "project": { |  | ||||||
|     "name": "bulletin-board-server-frontend" |  | ||||||
|   }, |  | ||||||
|   "apps": [ |  | ||||||
|     { |  | ||||||
|       "root": "src", |  | ||||||
|       "outDir": "dist", |  | ||||||
|       "assets": [ |  | ||||||
|         "assets", |  | ||||||
|         "favicon.ico" |  | ||||||
|       ], |  | ||||||
|       "index": "index.html", |  | ||||||
|       "main": "main.ts", |  | ||||||
|       "polyfills": "polyfills.ts", |  | ||||||
|       "test": "test.ts", |  | ||||||
|       "tsconfig": "tsconfig.app.json", |  | ||||||
|       "testTsconfig": "tsconfig.spec.json", |  | ||||||
|       "prefix": "app", |  | ||||||
|       "styles": [ |  | ||||||
|         "styles.css" |  | ||||||
|       ], |  | ||||||
|       "scripts": [], |  | ||||||
|       "environmentSource": "environments/environment.ts", |  | ||||||
|       "environments": { |  | ||||||
|         "dev": "environments/environment.ts", |  | ||||||
|         "prod": "environments/environment.prod.ts" |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   ], |  | ||||||
|   "e2e": { |  | ||||||
|     "protractor": { |  | ||||||
|       "config": "./protractor.conf.js" |  | ||||||
|     } |  | ||||||
|   }, |  | ||||||
|   "lint": [ |  | ||||||
|     { |  | ||||||
|       "project": "src/tsconfig.app.json" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "project": "src/tsconfig.spec.json" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "project": "e2e/tsconfig.e2e.json" |  | ||||||
|     } |  | ||||||
|   ], |  | ||||||
|   "test": { |  | ||||||
|     "karma": { |  | ||||||
|       "config": "./karma.conf.js" |  | ||||||
|     } |  | ||||||
|   }, |  | ||||||
|   "defaults": { |  | ||||||
|     "styleExt": "css", |  | ||||||
|     "component": {} |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  | @ -1,13 +0,0 @@ | ||||||
| # Editor configuration, see http://editorconfig.org |  | ||||||
| root = true |  | ||||||
| 
 |  | ||||||
| [*] |  | ||||||
| charset = utf-8 |  | ||||||
| indent_style = space |  | ||||||
| indent_size = 2 |  | ||||||
| insert_final_newline = true |  | ||||||
| trim_trailing_whitespace = true |  | ||||||
| 
 |  | ||||||
| [*.md] |  | ||||||
| max_line_length = off |  | ||||||
| trim_trailing_whitespace = false |  | ||||||
|  | @ -1,42 +0,0 @@ | ||||||
| # See http://help.github.com/ignore-files/ for more about ignoring files. |  | ||||||
| 
 |  | ||||||
| # compiled output |  | ||||||
| /dist |  | ||||||
| /tmp |  | ||||||
| /out-tsc |  | ||||||
| 
 |  | ||||||
| # dependencies |  | ||||||
| /node_modules |  | ||||||
| 
 |  | ||||||
| # IDEs and editors |  | ||||||
| /.idea |  | ||||||
| .project |  | ||||||
| .classpath |  | ||||||
| .c9/ |  | ||||||
| *.launch |  | ||||||
| .settings/ |  | ||||||
| *.sublime-workspace |  | ||||||
| 
 |  | ||||||
| # IDE - VSCode |  | ||||||
| .vscode/* |  | ||||||
| !.vscode/settings.json |  | ||||||
| !.vscode/tasks.json |  | ||||||
| !.vscode/launch.json |  | ||||||
| !.vscode/extensions.json |  | ||||||
| 
 |  | ||||||
| # misc |  | ||||||
| /.sass-cache |  | ||||||
| /connect.lock |  | ||||||
| /coverage |  | ||||||
| /libpeerconnection.log |  | ||||||
| npm-debug.log |  | ||||||
| testem.log |  | ||||||
| /typings |  | ||||||
| 
 |  | ||||||
| # e2e |  | ||||||
| /e2e/*.js |  | ||||||
| /e2e/*.map |  | ||||||
| 
 |  | ||||||
| # System Files |  | ||||||
| .DS_Store |  | ||||||
| Thumbs.db |  | ||||||
|  | @ -1,28 +0,0 @@ | ||||||
| # BulletinBoardServerFrontend |  | ||||||
| 
 |  | ||||||
| This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.2.0. |  | ||||||
| 
 |  | ||||||
| ## Development server |  | ||||||
| 
 |  | ||||||
| Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. |  | ||||||
| 
 |  | ||||||
| ## Code scaffolding |  | ||||||
| 
 |  | ||||||
| Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|module`. |  | ||||||
| 
 |  | ||||||
| ## Build |  | ||||||
| 
 |  | ||||||
| Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build. |  | ||||||
| 
 |  | ||||||
| ## Running unit tests |  | ||||||
| 
 |  | ||||||
| Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). |  | ||||||
| 
 |  | ||||||
| ## Running end-to-end tests |  | ||||||
| 
 |  | ||||||
| Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). |  | ||||||
| Before running the tests make sure you are serving the app via `ng serve`. |  | ||||||
| 
 |  | ||||||
| ## Further help |  | ||||||
| 
 |  | ||||||
| To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). |  | ||||||
|  | @ -1,60 +0,0 @@ | ||||||
| plugins { |  | ||||||
|   id "com.moowork.node" version "1.2.0" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| node { |  | ||||||
|   download = true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| ext { |  | ||||||
|   bundlejs = "src/app/bundle.js" |  | ||||||
|   bundlets = "src/app/bundle.d.ts" |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| //task installAngularCli(type: NpmTask) { |  | ||||||
| //  args = ['install', '@angular/cli'] |  | ||||||
| //} |  | ||||||
| //npm_install.dependsOn(installAngularCli); |  | ||||||
| 
 |  | ||||||
| task printProtos { |  | ||||||
|   doLast { |  | ||||||
|     println getProtoFiles().join(" ") |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| printProtos.description = "List all the .proto files we can find." |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def getProtoFiles() { |  | ||||||
|   def protoFiles = [] |  | ||||||
| 
 |  | ||||||
|   rootProject.subprojects { proj -> |  | ||||||
|     if (proj.hasProperty('sourceSets') && proj.sourceSets.hasProperty('main') && |  | ||||||
|       proj.sourceSets.main.hasProperty('proto')) { |  | ||||||
|       protoFiles = protoFiles + proj.sourceSets.main.proto.getFiles(); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|   return protoFiles; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| task generateProtobufBundle(type: NpmTask) { |  | ||||||
|   doFirst { |  | ||||||
|     // We need do this in doFirst since getProtoFiles won't find all subprojects |  | ||||||
|     // until after configuration is complete |  | ||||||
|     def protoFiles = getProtoFiles() |  | ||||||
|     generateProtobufBundle.args = ['run', 'protoc', '--' ] + protoFiles; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   dependsOn npm_install |  | ||||||
| } |  | ||||||
| generateProtobufBundle.description = "Generate the bundle.js/bundle.d.ts files containing compiled protobufs" |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| npm_run_build.dependsOn(npm_install) |  | ||||||
| npm_run_build.dependsOn(generateProtobufBundle) |  | ||||||
| npm_start.dependsOn(generateProtobufBundle) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| // It would be nice if this worked, but it seems to get stuck: |  | ||||||
| //npm_start.dependsOn(project(':bulletin-board-server').jettyStart); |  | ||||||
| 
 |  | ||||||
|  | @ -1,14 +0,0 @@ | ||||||
| import { BulletinBoardServerFrontendPage } from './app.po'; |  | ||||||
| 
 |  | ||||||
| describe('bulletin-board-server-frontend App', () => { |  | ||||||
|   let page: BulletinBoardServerFrontendPage; |  | ||||||
| 
 |  | ||||||
|   beforeEach(() => { |  | ||||||
|     page = new BulletinBoardServerFrontendPage(); |  | ||||||
|   }); |  | ||||||
| 
 |  | ||||||
|   it('should display welcome message', () => { |  | ||||||
|     page.navigateTo(); |  | ||||||
|     expect(page.getParagraphText()).toEqual('Welcome to app!!'); |  | ||||||
|   }); |  | ||||||
| }); |  | ||||||
|  | @ -1,11 +0,0 @@ | ||||||
| import { browser, by, element } from 'protractor'; |  | ||||||
| 
 |  | ||||||
| export class BulletinBoardServerFrontendPage { |  | ||||||
|   navigateTo() { |  | ||||||
|     return browser.get('/'); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   getParagraphText() { |  | ||||||
|     return element(by.css('app-root h1')).getText(); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  | @ -1,13 +0,0 @@ | ||||||
| { |  | ||||||
|   "extends": "../tsconfig.json", |  | ||||||
|   "compilerOptions": { |  | ||||||
|     "outDir": "../out-tsc/e2e", |  | ||||||
|     "module": "commonjs", |  | ||||||
|     "target": "es5", |  | ||||||
|     "types": [ |  | ||||||
|       "jasmine", |  | ||||||
|       "jasminewd2", |  | ||||||
|       "node" |  | ||||||
|     ] |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  | @ -1,32 +0,0 @@ | ||||||
| #!/usr/bin/env node
 |  | ||||||
| const { execFileSync } = require('child_process'); |  | ||||||
| const  path  = require('path'); |  | ||||||
| 
 |  | ||||||
| const isWin = (process.platform === 'win32'); |  | ||||||
| 
 |  | ||||||
| const bundlejs = 'src/app/bundle.js' |  | ||||||
| const bundlets = 'src/app/bundle.d.ts' |  | ||||||
| 
 |  | ||||||
| const npm = isWin ? 'npm.cmd' : 'npm'; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| var args = process.argv.slice(2).map(function(x) { |  | ||||||
|   return path.normalize(x); |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| var pbjs_args = [ |  | ||||||
|   '-t', 'static-module', '-w', 'commonjs', '-o', bundlejs |  | ||||||
| ].concat(args); |  | ||||||
| 
 |  | ||||||
| console.log("Running pbjs " + pbjs_args.join(" ")); |  | ||||||
| var out = execFileSync(npm, ['run', 'pbjs', '--'].concat(pbjs_args)); |  | ||||||
| console.log("" + out); |  | ||||||
| 
 |  | ||||||
| var pbts_args = [ |  | ||||||
|   '-o', bundlets, bundlejs |  | ||||||
| ]; |  | ||||||
| 
 |  | ||||||
| console.log("Running pbts " + pbts_args.join(" ")); |  | ||||||
| out = execFileSync(npm, ['run', 'pbts', '--'].concat(pbts_args)); |  | ||||||
| console.log("" + out); |  | ||||||
|  | @ -1 +0,0 @@ | ||||||
| ../gradlew |  | ||||||
|  | @ -1,33 +0,0 @@ | ||||||
| // Karma configuration file, see link for more information
 |  | ||||||
| // https://karma-runner.github.io/0.13/config/configuration-file.html
 |  | ||||||
| 
 |  | ||||||
| module.exports = function (config) { |  | ||||||
|   config.set({ |  | ||||||
|     basePath: '', |  | ||||||
|     frameworks: ['jasmine', '@angular/cli'], |  | ||||||
|     plugins: [ |  | ||||||
|       require('karma-jasmine'), |  | ||||||
|       require('karma-chrome-launcher'), |  | ||||||
|       require('karma-jasmine-html-reporter'), |  | ||||||
|       require('karma-coverage-istanbul-reporter'), |  | ||||||
|       require('@angular/cli/plugins/karma') |  | ||||||
|     ], |  | ||||||
|     client:{ |  | ||||||
|       clearContext: false // leave Jasmine Spec Runner output visible in browser
 |  | ||||||
|     }, |  | ||||||
|     coverageIstanbulReporter: { |  | ||||||
|       reports: [ 'html', 'lcovonly' ], |  | ||||||
|       fixWebpackSourcePaths: true |  | ||||||
|     }, |  | ||||||
|     angularCli: { |  | ||||||
|       environment: 'dev' |  | ||||||
|     }, |  | ||||||
|     reporters: ['progress', 'kjhtml'], |  | ||||||
|     port: 9876, |  | ||||||
|     colors: true, |  | ||||||
|     logLevel: config.LOG_INFO, |  | ||||||
|     autoWatch: true, |  | ||||||
|     browsers: ['Chrome'], |  | ||||||
|     singleRun: false |  | ||||||
|   }); |  | ||||||
| }; |  | ||||||
|  | @ -1,53 +0,0 @@ | ||||||
| { |  | ||||||
|   "name": "bulletin-board-server-frontend", |  | ||||||
|   "version": "0.0.0", |  | ||||||
|   "license": "MIT", |  | ||||||
|   "scripts": { |  | ||||||
|     "ng": "ng", |  | ||||||
|     "start": "ng serve --proxy-config proxy.conf.json", |  | ||||||
|     "build": "ng build", |  | ||||||
|     "test": "ng test", |  | ||||||
|     "lint": "ng e2e", |  | ||||||
|     "pbts": "pbts", |  | ||||||
|     "pbjs": "pbjs", |  | ||||||
|     "protoc" : "node ./generate-protobuf.js" |  | ||||||
|   }, |  | ||||||
|   "private": true, |  | ||||||
|   "dependencies": { |  | ||||||
|     "@angular/animations": "^4.0.0", |  | ||||||
|     "@angular/common": "^4.0.0", |  | ||||||
|     "@angular/compiler": "^4.0.0", |  | ||||||
|     "@angular/core": "^4.0.0", |  | ||||||
|     "@angular/forms": "^4.0.0", |  | ||||||
|     "@angular/http": "^4.0.0", |  | ||||||
|     "@angular/platform-browser": "^4.0.0", |  | ||||||
|     "@angular/platform-browser-dynamic": "^4.0.0", |  | ||||||
|     "@angular/router": "^4.0.0", |  | ||||||
|     "@types/long": "^3.0.31", |  | ||||||
|     "core-js": "^2.4.1", |  | ||||||
|     "protobufjs": "^6.8.0", |  | ||||||
|     "rxjs": "^5.1.0", |  | ||||||
|     "zone.js": "^0.8.4" |  | ||||||
|   }, |  | ||||||
|   "devDependencies": { |  | ||||||
|     "@angular/cli": "^1.2.0", |  | ||||||
|     "@angular/compiler-cli": "^4.0.0", |  | ||||||
|     "@angular/language-service": "^4.0.0", |  | ||||||
|     "@types/jasmine": "~2.5.53", |  | ||||||
|     "@types/jasminewd2": "~2.0.2", |  | ||||||
|     "@types/node": "~6.0.60", |  | ||||||
|     "codelyzer": "~3.0.1", |  | ||||||
|     "jasmine-core": "~2.6.2", |  | ||||||
|     "jasmine-spec-reporter": "~4.1.0", |  | ||||||
|     "karma": "~1.7.0", |  | ||||||
|     "karma-chrome-launcher": "~2.1.1", |  | ||||||
|     "karma-cli": "~1.0.1", |  | ||||||
|     "karma-coverage-istanbul-reporter": "^1.2.1", |  | ||||||
|     "karma-jasmine": "~1.1.0", |  | ||||||
|     "karma-jasmine-html-reporter": "^0.2.2", |  | ||||||
|     "protractor": "~5.1.2", |  | ||||||
|     "ts-node": "~3.0.4", |  | ||||||
|     "tslint": "~5.3.2", |  | ||||||
|     "typescript": "~2.3.3" |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  | @ -1,28 +0,0 @@ | ||||||
| // Protractor configuration file, see link for more information
 |  | ||||||
| // https://github.com/angular/protractor/blob/master/lib/config.ts
 |  | ||||||
| 
 |  | ||||||
| const { SpecReporter } = require('jasmine-spec-reporter'); |  | ||||||
| 
 |  | ||||||
| exports.config = { |  | ||||||
|   allScriptsTimeout: 11000, |  | ||||||
|   specs: [ |  | ||||||
|     './e2e/**/*.e2e-spec.ts' |  | ||||||
|   ], |  | ||||||
|   capabilities: { |  | ||||||
|     'browserName': 'chrome' |  | ||||||
|   }, |  | ||||||
|   directConnect: true, |  | ||||||
|   baseUrl: 'http://localhost:4200/', |  | ||||||
|   framework: 'jasmine', |  | ||||||
|   jasmineNodeOpts: { |  | ||||||
|     showColors: true, |  | ||||||
|     defaultTimeoutInterval: 30000, |  | ||||||
|     print: function() {} |  | ||||||
|   }, |  | ||||||
|   onPrepare() { |  | ||||||
|     require('ts-node').register({ |  | ||||||
|       project: 'e2e/tsconfig.e2e.json' |  | ||||||
|     }); |  | ||||||
|     jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); |  | ||||||
|   } |  | ||||||
| }; |  | ||||||
|  | @ -1,6 +0,0 @@ | ||||||
| { |  | ||||||
|   "/bbserver" : { |  | ||||||
|     "target" : "http://localhost:8081/", |  | ||||||
|     "secure" : false |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  | @ -1,12 +0,0 @@ | ||||||
| <!--The content below is only a placeholder and can be replaced.--> |  | ||||||
| <div style="text-align:center"> |  | ||||||
|   <h1> |  | ||||||
|     Welcome to {{title}}!! |  | ||||||
|   </h1> |  | ||||||
| 
 |  | ||||||
|   <h3>Current status: {{status}}</h3> |  | ||||||
|   <img width="300" src="assets/meerkat-logo.svg"> |  | ||||||
|   <hr> |  | ||||||
|   {{errorMessage}} |  | ||||||
| </div> |  | ||||||
| 
 |  | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue