Update voting app with basic auth
[music.git] / examples / VotingApp / src / main / java / main / VotingApp.java
1 /*
2  * 
3 This licence applies to all files in this repository unless otherwise specifically
4 stated inside of the file. 
5
6  ---------------------------------------------------------------------------
7    Copyright (c) 2016 AT&T Intellectual Property
8
9    Licensed under the Apache License, Version 2.0 (the "License");
10    you may not use this file except in compliance with the License.
11    You may obtain a copy of the License at:
12
13        http://www.apache.org/licenses/LICENSE-2.0
14
15    Unless required by applicable law or agreed to in writing, software
16    distributed under the License is distributed on an "AS IS" BASIS,
17    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18    See the License for the specific language governing permissions and
19    limitations under the License.
20  ---------------------------------------------------------------------------
21
22  */
23
24 package main;
25 import java.io.BufferedReader;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.InputStreamReader;
29 import java.util.ArrayList;
30 import java.util.HashMap;
31 import java.util.Map;
32 import java.util.Base64;
33
34 import javax.ws.rs.core.MediaType;
35
36 import com.sun.jersey.api.client.Client;
37 import com.sun.jersey.api.client.ClientResponse;
38 import com.sun.jersey.api.client.WebResource;
39 import com.sun.jersey.api.client.WebResource.Builder;
40 import com.sun.jersey.api.client.config.ClientConfig;
41 import com.sun.jersey.api.client.config.DefaultClientConfig;
42 import com.sun.jersey.api.json.JSONConfiguration;
43
44 public class VotingApp {
45     String keyspaceName;
46     ArrayList<String> lockNames;
47     MusicConnector musicHandle;
48     private final String version="1.0.0";
49     
50     //UPDATE your onboarding information here
51     String namespace = "votingapp";
52     String userId = "abc123d";
53     String password = "password";
54     
55     public VotingApp(String[] musicIps){
56         lockNames = new ArrayList<String>();    
57         musicHandle = new MusicConnector(musicIps);
58         bootStrap();
59     }
60     
61     /**
62      * Adds MUSIC's authentication headers into the webresource
63      * @param webResource
64      */
65     private Builder addMusicHeaders(WebResource webResource) {
66         Builder builder = webResource.accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_JSON);
67         if (!namespace.equals("")) {
68             builder.header("ns", namespace);
69         }
70         if (!userId.equals("") && !password.equals("")) {
71             String authString = Base64.getEncoder().encodeToString((userId + ":" + password).getBytes());
72             builder.header("Authorization", "Basic " + authString);
73         }
74
75         return builder;
76     }
77     
78     public void createVotingKeyspace(){
79         keyspaceName = "VotingAppForMusic";
80         System.out.println("Voting app version "+ version+" .....");
81         Map<String,Object> replicationInfo = new HashMap<String, Object>();
82         replicationInfo.put("class", "SimpleStrategy");
83         replicationInfo.put("replication_factor", 1);
84         String durabilityOfWrites="false";
85         Map<String,String> consistencyInfo= new HashMap<String, String>();
86         consistencyInfo.put("type", "eventual");
87         JsonKeySpace jsonKp = new JsonKeySpace();
88         jsonKp.setConsistencyInfo(consistencyInfo);
89         jsonKp.setDurabilityOfWrites(durabilityOfWrites);
90         jsonKp.setReplicationInfo(replicationInfo);
91
92         ClientConfig clientConfig = new DefaultClientConfig();
93
94         clientConfig.getFeatures().put(
95                 JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
96
97         Client client = Client.create(clientConfig);
98
99         WebResource webResource = client
100                 .resource(musicHandle.getMusicNodeURL()+"/keyspaces/"+keyspaceName);
101
102         ClientResponse response = addMusicHeaders(webResource).accept("application/json")
103                 .type("application/json").post(ClientResponse.class, jsonKp);
104         if (response.getStatus() < 200 || (response.getStatus() > 299 && response.getStatus()!=400)) { //supress keyspace already exists
105             Map<String, String> map = response.getEntity(Map.class);
106             throw new RuntimeException("Failed : HTTP error code : "+ response.getStatus() + "- " + map);
107         }
108
109     }
110
111     public void createVotingTable(){
112         Map<String,String> fields = new HashMap<String,String>();
113         fields.put("name", "text");
114         fields.put("count", "varint");
115         fields.put("PRIMARY KEY", "(name)");
116
117
118         Map<String,String> consistencyInfo= new HashMap<String, String>();
119         consistencyInfo.put("type", "eventual");
120
121         JsonTable jtab = new JsonTable();
122         jtab.setFields(fields);
123         jtab.setConsistencyInfo(consistencyInfo);
124
125         ClientConfig clientConfig = new DefaultClientConfig();
126
127         clientConfig.getFeatures().put(
128                 JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
129
130         Client client = Client.create(clientConfig);
131         String url = musicHandle.getMusicNodeURL()+"/keyspaces/"+keyspaceName+"/tables/votecount";
132         System.out.println("create url:"+url);
133         WebResource webResource = client
134                 .resource(url);
135
136         ClientResponse response = addMusicHeaders(webResource).accept("application/json")
137                 .type("application/json").post(ClientResponse.class, jtab);
138
139         System.out.println(response.getEntity(Map.class));
140         if (response.getStatus() < 200 || (response.getStatus() > 299 && response.getStatus()!=400)) 
141             throw new RuntimeException("Failed : HTTP error code : "+ response.getStatus());
142
143     }
144     private void checkMusicVersion(){
145         Client client = Client.create();
146         System.out.println(musicHandle.getMusicNodeURL()+"/version");
147         
148
149 //        System.setProperty("sun.net.http.allowRestrictedHeaders", "true");
150         WebResource webResource = client
151                 .resource(musicHandle.getMusicNodeURL()+"/version");
152         
153
154         ClientResponse response = addMusicHeaders(webResource)
155                 .accept(MediaType.APPLICATION_JSON).header("Connection", "close").get(ClientResponse.class);
156         
157         if (response.getStatus() != 200) {
158             throw new RuntimeException("Failed : HTTP error code : "
159                     + response.getStatus());
160         }
161
162         String output = response.getEntity(Map.class).toString();
163
164         System.out.println(output);
165
166     }
167
168     private  void createEntryForCandidate(String candidateName){
169         Map<String,Object> values = new HashMap<String,Object>();
170         values.put("name",candidateName );
171         values.put("count",0);
172
173         Map<String,String> consistencyInfo= new HashMap<String, String>();
174         consistencyInfo.put("type", "eventual");
175
176         JsonInsert jIns = new JsonInsert();
177         jIns.setValues(values);
178         jIns.setConsistencyInfo(consistencyInfo);
179         ClientConfig clientConfig = new DefaultClientConfig();
180
181         clientConfig.getFeatures().put(
182                 JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
183
184         Client client = Client.create(clientConfig);
185
186         String url = musicHandle.getMusicNodeURL()+"/keyspaces/"+keyspaceName+"/tables/votecount/rows";
187         WebResource webResource = client
188                 .resource(url);
189
190         ClientResponse response = addMusicHeaders(webResource).accept("application/json")
191                 .type("application/json").post(ClientResponse.class, jIns);
192
193         if (response.getStatus() < 200 || response.getStatus() > 299) 
194             throw new RuntimeException("Failed : HTTP error code : "+ response.getStatus()+"url:"+url+" candidate name:"+ candidateName);
195
196
197     }
198
199     private  String createLock(String lockName){
200         Client client = Client.create();
201         String msg = musicHandle.getMusicNodeURL()+"/locks/create/"+lockName;
202         WebResource webResource = client.resource(msg);
203         System.out.println(msg);
204         WebResource.Builder wb = addMusicHeaders(webResource).accept(MediaType.APPLICATION_JSON);
205
206         ClientResponse response = wb.post(ClientResponse.class);
207
208         if (response.getStatus() != 200) {
209             throw new RuntimeException("Failed : HTTP error code : "+ response.getStatus()+"url:"+msg);
210         }
211
212         Map<String,Object> responseMap = response.getEntity(Map.class);
213
214         String lockid = ((Map<String,String>) responseMap.get("lock")).get("lock");
215 //        System.out.println("Server response .... \n");
216 //        System.out.println(output);
217         return lockid;
218     }
219
220     private  boolean acquireLock(String lockId){
221         Client client = Client.create();
222         String msg = musicHandle.getMusicNodeURL()+"/locks/acquire/"+lockId;
223         System.out.println(msg);
224         WebResource webResource = client.resource(msg);
225
226
227         WebResource.Builder wb = addMusicHeaders(webResource).accept(MediaType.APPLICATION_JSON);
228
229         ClientResponse response = wb.get(ClientResponse.class);
230
231         Map<String, Object> responseMap = response.getEntity(Map.class);
232         
233         if (response.getStatus() != 200) {
234             throw new RuntimeException("Failed : HTTP error code : "+ response.getStatus()+ ":" + responseMap);
235         }
236
237         System.out.println(responseMap);
238         Boolean status = responseMap.get("status").equals("SUCCESS");
239         System.out.println("Server response .... " + status);
240         return status;
241     }
242
243     private  void unlock(String lockId){
244         Client client = Client.create();
245         WebResource webResource = client.resource(musicHandle.getMusicNodeURL()+"/locks/release/"+lockId);
246
247         ClientResponse response = addMusicHeaders(webResource).delete(ClientResponse.class);
248
249
250         if (response.getStatus() < 200 || response.getStatus()>299) {
251             throw new RuntimeException("Failed : HTTP error code : "
252                     + response.getStatus());
253         }
254     }
255
256     private  void updateVoteCountAtomically(String candidateName,int count){
257         /*create lock for the candidate. The music API dictates that
258          * the lock name must be of the form keyspacename.tableName.primaryKeyName
259          * */
260         System.out.println("trying to acquire lock!");
261
262         String lockName = keyspaceName+".votecount."+candidateName;
263         lockNames.add(lockName);
264         String lockId = createLock(lockName);
265         while(acquireLock(lockId) != true);
266         
267         System.out.println("acquired lock!");
268         //update candidate entry if you have the lock
269         Map<String,Object> values = new HashMap<String,Object>();
270         values.put("count",count);
271
272         Map<String,String> consistencyInfo= new HashMap<String, String>();
273         consistencyInfo.put("type", "critical");
274         consistencyInfo.put("lockId", lockId);
275
276         JsonInsert jIns = new JsonInsert();
277         jIns.setValues(values);
278         jIns.setConsistencyInfo(consistencyInfo);
279         ClientConfig clientConfig = new DefaultClientConfig();
280
281         clientConfig.getFeatures().put(
282                 JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
283
284         Client client = Client.create(clientConfig);
285         String url = musicHandle.getMusicNodeURL()+"/keyspaces/"+keyspaceName+"/tables/votecount/rows?name="+candidateName;
286         System.out.println(url);
287         WebResource webResource = client
288                 .resource(url);
289
290         ClientResponse response = addMusicHeaders(webResource).accept("application/json")
291                 .type("application/json").put(ClientResponse.class, jIns);
292
293         Map<String,String> map = response.getEntity(Map.class);
294         
295         if (response.getStatus() < 200 || response.getStatus() > 299) 
296             throw new RuntimeException("Failed : HTTP error code : "+ response.getStatus()+":"+map);
297
298         //release lock now that the operation is done
299         unlock(lockId);
300
301     }
302
303     private  void deleteCandidateEntryEventually(String candidateName){
304         Map<String,String> consistencyInfo= new HashMap<String, String>();
305         consistencyInfo.put("type", "eventual");
306
307         JsonDelete jDel = new JsonDelete();
308         jDel.setConsistencyInfo(consistencyInfo);
309         ClientConfig clientConfig = new DefaultClientConfig();
310
311         clientConfig.getFeatures().put(
312                 JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
313
314         Client client = Client.create(clientConfig);
315         String url = musicHandle.getMusicNodeURL()+"/keyspaces/"+keyspaceName+"/tables/votecount/rows?name="+candidateName;
316         System.out.println(url);
317         WebResource webResource = client
318                 .resource(url);
319
320         ClientResponse response = addMusicHeaders(webResource).accept("application/json")
321                 .type("application/json").delete(ClientResponse.class, jDel);
322
323         if (response.getStatus() < 200 || response.getStatus() > 299) 
324             throw new RuntimeException("Failed : HTTP error code : "+ response.getStatus()+"url:"+url);
325
326     }
327
328     public  Map<String,Object> readVoteCountForCandidate(String candidateName){
329         ClientConfig clientConfig = new DefaultClientConfig();
330
331         clientConfig.getFeatures().put(
332                 JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
333
334         Client client = Client.create(clientConfig);
335         String url = musicHandle.getMusicNodeURL()+"/keyspaces/"+keyspaceName+"/tables/votecount/rows?name="+candidateName;
336         WebResource webResource = client
337                 .resource(url);
338
339         ClientResponse response = addMusicHeaders(webResource).accept("application/json").get(ClientResponse.class);
340
341         if (response.getStatus() < 200 || response.getStatus() > 299) 
342             throw new RuntimeException("Failed : HTTP error code : "+ response.getStatus());
343         
344         Map<String,Object> output = response.getEntity(Map.class);
345         return output;    
346     }
347
348     public  Map<String,Object> readAllVotes(){
349         ClientConfig clientConfig = new DefaultClientConfig();
350
351         clientConfig.getFeatures().put(
352                 JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
353
354         Client client = Client.create(clientConfig);
355         String url = musicHandle.getMusicNodeURL()+"/keyspaces/"+keyspaceName+"/tables/votecount/rows";
356         WebResource webResource = client
357                 .resource(url);
358
359         ClientResponse response = addMusicHeaders(webResource).accept("application/json").get(ClientResponse.class);
360
361         if (response.getStatus() < 200 || response.getStatus() > 299) 
362             throw new RuntimeException("Failed : HTTP error code : "+ response.getStatus());
363         
364         Map<String,Object> output = response.getEntity(Map.class);
365         return output;    
366     }
367     
368     
369     /*
370      * Unable to use this because of the error: 
371      * Exception in thread "main" com.sun.jersey.api.client.ClientHandlerException: java.net.ProtocolException: 
372      * HTTP method DELETE doesn't support output. Seems to be a error in the rest java combination according to the interwebs
373      */
374     private void dropKeySpace(){
375         Map<String,String> consistencyInfo= new HashMap<String, String>();
376         consistencyInfo.put("type", "eventual");
377
378         JsonKeySpace jsonKp = new JsonKeySpace();
379         jsonKp.setConsistencyInfo(consistencyInfo);
380
381         ClientConfig clientConfig = new DefaultClientConfig();
382
383         clientConfig.getFeatures().put(
384                 JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
385
386         Client client = Client.create(clientConfig);
387
388         WebResource webResource = client
389                 .resource(musicHandle.getMusicNodeURL()+"/keyspaces/"+keyspaceName);
390
391         ClientResponse response = addMusicHeaders(webResource).type("application/json")
392                 .delete(ClientResponse.class, jsonKp);
393
394         if (response.getStatus() < 200 || response.getStatus() > 299) 
395             throw new RuntimeException("Failed : HTTP error code : "+ response.getStatus());
396     }
397     
398     private void deleteLock(String lockName){
399         Client client = Client.create();
400         WebResource webResource = client.resource(musicHandle.getMusicNodeURL()+"/locks/delete/"+lockName);
401
402         ClientResponse response = addMusicHeaders(webResource).delete(ClientResponse.class);
403
404
405         if (response.getStatus() <200 || response.getStatus()>299) {
406             throw new RuntimeException("Failed : HTTP error code : "
407                     + response.getStatus());
408         }
409     }
410
411     private void resetMusic(){
412         Client client = Client.create();
413         WebResource webResource = client.resource(musicHandle.getMusicNodeURL()+"/reset");
414
415         ClientResponse response = addMusicHeaders(webResource).delete(ClientResponse.class);
416
417
418         if (response.getStatus() != 204) {
419             throw new RuntimeException("Failed : HTTP error code : "
420                     + response.getStatus());
421         }
422         
423     }
424     public void deleteAllLocks(){
425         for (String lockName : lockNames) {
426             deleteLock(lockName);
427         }
428     }
429     
430     
431     public void bootStrap(){
432         checkMusicVersion();
433         createVotingKeyspace();
434
435
436         createVotingTable();
437         
438
439         //the next few lines just create an entry in the voting table for all these candidates with vote count as 0
440         createEntryForCandidate("Popeye");
441
442         createEntryForCandidate("Judy");
443
444         createEntryForCandidate("Flash");
445
446         createEntryForCandidate("Mickey");
447
448     }
449     
450     public void overAllTests(){
451         //update the count atomically
452         updateVoteCountAtomically("Popeye",5);
453
454         updateVoteCountAtomically("Judy",7);
455         
456         updateVoteCountAtomically("Mickey",8);
457
458         updateVoteCountAtomically("Flash",2);
459
460         
461         //read votecount         
462         System.out.println(readAllVotes());
463         
464         System.out.println(readVoteCountForCandidate("Popeye"));
465         
466         System.out.println(readVoteCountForCandidate("Flash"));
467
468         deleteCandidateEntryEventually("Mickey");
469
470         System.out.println(readAllVotes());
471
472 //        dropKeySpace();
473
474         deleteAllLocks();
475     }
476     
477     public void flipTest(){
478         checkMusicVersion();
479     }
480     
481     public static String executeBashScript(String pathToScript, String arg1, String arg2){
482         try {
483             ProcessBuilder pb = new ProcessBuilder(pathToScript,arg1, arg2);
484             final Process process = pb.start();
485             InputStream is = process.getInputStream();
486             InputStreamReader isr = new InputStreamReader(is);
487             BufferedReader br = new BufferedReader(isr);
488             return br.readLine();
489         } catch (IOException e) {
490             e.printStackTrace();
491         }
492         return null;    
493     }
494     
495     public static void main(String[] args) {    
496         long start = System.currentTimeMillis();
497
498         if (args.length==0) {
499             args = new String[]{"localhost"};
500         }
501         for(int i =0; i < 2;++i){
502             VotingApp vHandle = new VotingApp(args);
503             vHandle.overAllTests();
504
505             System.out.println("=====================================");
506             System.out.println("Test no."+i+" completed:");
507         }
508         long diff =     System.currentTimeMillis() - start;
509         System.out.println(diff);
510     }
511             
512
513 }