CSIT Fix for SDC-2585
[sdc.git] / catalog-dao / src / main / java / org / openecomp / sdc / be / dao / neo4j / Neo4jClient.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.openecomp.sdc.be.dao.neo4j;
22
23 import fj.data.Either;
24 import org.apache.http.HttpEntity;
25 import org.apache.http.client.ClientProtocolException;
26 import org.apache.http.client.HttpResponseException;
27 import org.apache.http.client.methods.CloseableHttpResponse;
28 import org.apache.http.client.methods.HttpGet;
29 import org.apache.http.client.methods.HttpPost;
30 import org.apache.http.client.protocol.HttpClientContext;
31 import org.apache.http.entity.StringEntity;
32 import org.apache.http.impl.client.BasicResponseHandler;
33 import org.apache.http.impl.client.CloseableHttpClient;
34 import org.apache.http.impl.client.HttpClients;
35 import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
36 import org.apache.http.util.EntityUtils;
37 import org.json.simple.JSONArray;
38 import org.json.simple.JSONObject;
39 import org.json.simple.parser.JSONParser;
40 import org.json.simple.parser.ParseException;
41 import org.openecomp.sdc.be.config.ConfigurationManager;
42 import org.openecomp.sdc.be.dao.graph.GraphElementFactory;
43 import org.openecomp.sdc.be.dao.graph.datatype.GraphElement;
44 import org.openecomp.sdc.be.dao.graph.datatype.GraphElementTypeEnum;
45 import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
46 import org.openecomp.sdc.be.dao.neo4j.filters.MatchFilter;
47 import org.openecomp.sdc.be.dao.neo4j.filters.RecursiveFilter;
48 import org.openecomp.sdc.be.dao.neo4j.filters.UpdateFilter;
49 import org.openecomp.sdc.be.dao.utils.DaoUtils;
50 import org.openecomp.sdc.common.log.wrappers.Logger;
51
52 import javax.annotation.PostConstruct;
53 import javax.annotation.PreDestroy;
54 import java.io.IOException;
55 import java.util.*;
56
57 //@Component("neo4j-client")
58 public class Neo4jClient {
59         private CloseableHttpClient httpClient;
60         private JSONParser jsonParser;
61
62         private CypherTranslator cypherTranslator;
63
64         private static Logger logger = Logger.getLogger(Neo4jClient.class.getName());
65
66         private static final String getServiceRoot = "http://$host$:$port$/db/data/";
67         // Error's Classification templates
68         private static final String ClientError = "ClientError";
69         private static final String DatabaseError = "DatabaseError";
70         private static final String TransientError = "TransientError";
71
72         // Error's Category templates
73         private static final String General = "General";
74         private static final String LegacyIndex = "LegacyIndex";
75         private static final String Request = "Request";
76         private static final String Schema = "Schema";
77         private static final String Security = "Security";
78         private static final String Statement = "Statement";
79         private static final String Transaction = "Transaction";
80
81         // Error's Title templates
82         private static final String EntityNotFound = "EntityNotFound";
83         private static final String ConstraintViolation = "ConstraintViolation";
84
85         @PostConstruct
86         public void init() {
87
88                 PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
89                 connectionManager.setMaxTotal(100);
90                 connectionManager.setDefaultMaxPerRoute(20);
91                 connectionManager.setValidateAfterInactivity(15000);
92                 this.httpClient = HttpClients.custom().setConnectionManager(connectionManager).build();
93                 jsonParser = new JSONParser();
94                 cypherTranslator = new CypherTranslator();
95
96         }
97
98         @PreDestroy
99         public void shutdown() {
100                 try {
101                         httpClient.close();
102                         logger.debug("Http client to Neo4j Graph closed");
103                 } catch (Exception e) {
104                         logger.info("Failed to close http client", e);
105                 }
106         }
107
108         /**
109          * 
110          * @param builder
111          * @return
112          */
113         public Either<List<List<GraphElement>>, Neo4jOperationStatus> execute(BatchBuilder builder) {
114
115                 String json = cypherTranslator.translate(builder);
116                 logger.debug("Try to execute cypher request [{}]", json);
117
118                 Either<String, Neo4jOperationStatus> result = sendPostCypher(json);
119                 if (result.isRight()) {
120                         return Either.right(result.right().value());
121                 }
122                 List<List<GraphElement>> batchResult;
123                 try {
124                         batchResult = parseResult(result.left().value(), false);
125                 } catch (ParseException e) {
126                         logger.error("Failed to parse batchresponse", e);
127                         return Either.right(Neo4jOperationStatus.GENERAL_ERROR);
128                 }
129
130                 return Either.left(batchResult);
131         }
132
133         public Either<List<List<GraphElement>>, Neo4jOperationStatus> executeGet(RecursiveFilter filter) {
134                 String json = cypherTranslator.translateGet(filter);
135                 logger.debug("Try to execute cypher request [{}]", json);
136
137                 Either<String, Neo4jOperationStatus> result = sendPostCypher(json);
138                 if (result.isRight()) {
139                         return Either.right(result.right().value());
140                 }
141                 List<List<GraphElement>> batchResult;
142                 try {
143                         batchResult = parseResult(result.left().value(), true);
144                 } catch (ParseException e) {
145                         logger.error("Failed to parse batchresponse", e);
146                         return Either.right(Neo4jOperationStatus.GENERAL_ERROR);
147                 }
148
149                 return Either.left(batchResult);
150
151         }
152
153         /**
154          * 
155          * @param element
156          * @param ip
157          * @param user
158          * @param password
159          * @return
160          */
161         public Neo4jOperationStatus createElement(GraphElement element) {
162                 Neo4jOperationStatus result = Neo4jOperationStatus.OK;
163                 switch (element.getElementType()) {
164                 case Node:
165                         Either<String, Neo4jOperationStatus> status = createNode(element);
166                         if (status.isRight()) {
167                                 result = status.right().value();
168                         }
169                         break;
170                 case Relationship:
171                         // TODO
172                         break;
173
174                 default:
175                         break;
176                 }
177
178                 return result;
179         }
180
181         public Either<GraphElement, Neo4jOperationStatus> createSingleElement(GraphElement element) {
182                 switch (element.getElementType()) {
183                 case Node:
184                         Either<String, Neo4jOperationStatus> status = createNode(element);
185                         if (status.isRight()) {
186                                 return Either.right(status.right().value());
187                         }
188                         // parse response
189                         String response = status.left().value();
190                         try {
191                                 List<GraphElement> listElements = parseGetResponse(element.getElementType(),
192                                                 ((GraphNode) element).getLabel(), response);
193                                 if (listElements == null || listElements.isEmpty()) {
194                                         return Either.right(Neo4jOperationStatus.NOT_FOUND);
195                                 } else {
196                                         return Either.left(listElements.get(0));
197                                 }
198                         } catch (Exception e) {
199                                 logger.error("Failed to parse fetched data from graph", e);
200                                 return Either.right(Neo4jOperationStatus.GENERAL_ERROR);
201                         }
202                 case Relationship:
203                         // TODO
204                         break;
205
206                 default:
207                         break;
208                 }
209
210                 return Either.right(Neo4jOperationStatus.NOT_SUPPORTED);
211         }
212
213         /**
214          * 
215          * @param type
216          * @param label
217          * @param filter
218          * @param ip
219          * @param user
220          * @param password
221          * @return
222          */
223         public Either<List<GraphElement>, Neo4jOperationStatus> getByFilter(GraphElementTypeEnum type, String label,
224                         MatchFilter filter) {
225
226                 List<GraphElement> result = null;
227
228                 String requestJson;
229                 // replace return type
230                 if (type.equals(GraphElementTypeEnum.Node)) {
231                         requestJson = CypherTemplates.CypherMatchTemplate.replace("$type$", "n");
232                 } else {
233                         requestJson = CypherTemplates.CypherMatchTemplate.replace("$type$", "r");
234                 }
235                 // replace label
236                 if (label != null && !label.isEmpty()) {
237                         requestJson = requestJson.replace("$label$", label);
238                 } else {
239                         requestJson = requestJson.replace("$label$", "");
240                 }
241
242                 // replace filter
243                 if (filter.getProperties().isEmpty()) {
244                         // get all records by label
245                         requestJson = requestJson.replace("{$filter$}", "");
246                 } else {
247                         String filterStr = CypherTranslator.prepareFilterBody(filter);
248                         requestJson = requestJson.replace("$filter$", filterStr);
249                 }
250                 logger.debug("Try to perform request []", requestJson);
251
252                 Either<String, Neo4jOperationStatus> status = sendPostCypher(requestJson);
253                 if (status.isRight()) {
254                         return Either.right(Neo4jOperationStatus.GENERAL_ERROR);
255                 }
256                 // parse response
257                 String response = status.left().value();
258                 try {
259                         result = parseGetResponse(type, label, response);
260                 } catch (Exception e) {
261                         logger.error("Failed to parse fetched data from graph", e);
262                         Either.right(Neo4jOperationStatus.GENERAL_ERROR);
263                 }
264
265                 return Either.left(result);
266         }
267
268         /**
269          * 
270          * @param type
271          * @param label
272          * @param toMatch
273          * @param toUpdate
274          * @param ip
275          * @param user
276          * @param password
277          * @return
278          */
279         public Neo4jOperationStatus updateElement(GraphElementTypeEnum type, String label, UpdateFilter toUpdate) {
280
281                 String requestJson;
282                 // replace return type
283                 if (type.equals(GraphElementTypeEnum.Node)) {
284                         requestJson = CypherTemplates.CypherUpdateTemplate.replace("$type$", "n");
285                 } else {
286                         requestJson = CypherTemplates.CypherUpdateTemplate.replace("$type$", "r");
287                 }
288                 // replace label
289                 if (label != null && !label.isEmpty()) {
290                         requestJson = requestJson.replace("$label$", label);
291                 } else {
292                         requestJson = requestJson.replace("$label$", "");
293                 }
294
295                 // replace filter
296                 if (toUpdate.getProperties().isEmpty()) {
297                         // get all records by label
298                         requestJson = requestJson.replace("{$filter$}", "");
299                 } else {
300                         String filterStr = CypherTranslator.prepareFilterBody(toUpdate);
301                         requestJson = requestJson.replace("$filter$", filterStr);
302                 }
303                 String props = preparePropertiesInStatement(toUpdate.getToUpdate());
304                 requestJson = requestJson.replace("$props$", props);
305
306                 logger.debug("Try to perform request [{}]", requestJson);
307
308                 Either<String, Neo4jOperationStatus> result = sendPostCypher(requestJson);
309                 if (result.isRight()) {
310                         return Neo4jOperationStatus.GENERAL_ERROR;
311                 }
312                 return Neo4jOperationStatus.OK;
313         }
314
315         /**
316          * 
317          * @param type
318          * @param label
319          * @param response
320          * @return
321          * @throws ParseException
322          */
323
324         private List<GraphElement> parseGetResponse(GraphElementTypeEnum type, String label, String response)
325                         throws ParseException {
326                 List<GraphElement> result = new ArrayList<>();
327                 JSONObject responseData = (JSONObject) jsonParser.parse(response);
328                 JSONArray results = (JSONArray) responseData.get("results");
329                 Iterator<JSONObject> iteratorResults = results.iterator();
330                 while (iteratorResults.hasNext()) {
331                         JSONObject elementResult = iteratorResults.next();
332                         // JSONArray data = (JSONArray) elementResult.get("row");
333                         JSONArray data = (JSONArray) elementResult.get("data");
334
335                         Iterator<JSONObject> iterator = data.iterator();
336                         JSONObject element;
337                         while (iterator.hasNext()) {
338                                 element = (JSONObject) iterator.next();
339                                 JSONArray row = (JSONArray) element.get("row");
340
341                                 Iterator<JSONObject> iteratorRow = row.iterator();
342                                 while (iteratorRow.hasNext()) {
343                                         JSONObject rowElement = iteratorRow.next();
344
345                                         Map<String, Object> props = new HashMap<>();
346
347                                         for (Map.Entry<String, Object> entry : (Set<Map.Entry<String, Object>>) rowElement.entrySet()) {
348                                                 // props.put(entry.getKey(),
349                                                 // rowElement.get(entry.getValue()));
350                                                 props.put(entry.getKey(), entry.getValue());
351                                         }
352                                         GraphElement newElement = GraphElementFactory.createElement(label, type, props);
353                                         result.add(newElement);
354                                 }
355                         }
356                 }
357                 return result;
358         }
359
360         private List<List<GraphElement>> parseResult(String response, boolean storeRelationNode) throws ParseException {
361
362                 List<List<GraphElement>> batchList = new ArrayList<>();
363
364                 JSONObject responseData = (JSONObject) jsonParser.parse(response);
365                 JSONArray results = (JSONArray) responseData.get("results");
366                 Iterator<JSONObject> iteratorResults = results.iterator();
367
368                 while (iteratorResults.hasNext()) {
369                         JSONObject elementResult = iteratorResults.next();
370                         JSONArray data = (JSONArray) elementResult.get("data");
371                         JSONArray columns = (JSONArray) elementResult.get("columns");
372                         Iterator<JSONObject> iteratorData = data.iterator();
373                         List<GraphElement> singleDataList = new ArrayList<>();
374                         while (iteratorData.hasNext()) {
375
376                                 JSONObject singleData = iteratorData.next();
377                                 JSONArray row = (JSONArray) singleData.get("row");
378                                 if (columns.size() == 2) {
379                                         // node
380                                         JSONArray labelArray = (JSONArray) row.get(1);
381                                         JSONObject node = (JSONObject) row.get(0);
382
383                                         Map<String, Object> props = jsonObjectToMap(node);
384                                         // get only first label on node. Now single label supported
385                                         GraphElement newElement = GraphElementFactory.createElement((String) labelArray.get(0),
386                                                         GraphElementTypeEnum.Node, props);
387                                         singleDataList.add(newElement);
388                                 }
389                                 if (columns.size() == 10) {
390                                         // relation
391                                         JSONObject startNode = (JSONObject) row.get(0);
392                                         JSONArray startNodeArray = (JSONArray) row.get(1);
393
394                                         JSONObject relationFromStart = (JSONObject) row.get(2);
395                                         String relationFromStartType = (String) row.get(3);
396
397                                         JSONObject nodeFrom = (JSONObject) row.get(4);
398                                         JSONArray labelFromArray = (JSONArray) row.get(5);
399
400                                         JSONObject nodeTo = (JSONObject) row.get(6);
401                                         JSONArray labelToArray = (JSONArray) row.get(7);
402
403                                         JSONObject relation = (JSONObject) row.get(8);
404                                         String type = (String) row.get(9);
405
406                                         Map<String, Object> propsStartNode = jsonObjectToMap(startNode);
407                                         Map<String, Object> propsRelationStartNode = jsonObjectToMap(relationFromStart);
408
409                                         Map<String, Object> propsFrom = jsonObjectToMap(nodeFrom);
410                                         Map<String, Object> propsTo = jsonObjectToMap(nodeTo);
411                                         Map<String, Object> propsRelation = jsonObjectToMap(relation);
412
413                                         GraphNode startN = (GraphNode) GraphElementFactory.createElement((String) startNodeArray.get(0),
414                                                         GraphElementTypeEnum.Node, propsStartNode);
415
416                                         GraphNode from = (GraphNode) GraphElementFactory.createElement((String) labelFromArray.get(0),
417                                                         GraphElementTypeEnum.Node, propsFrom);
418                                         GraphNode to = (GraphNode) GraphElementFactory.createElement((String) labelToArray.get(0),
419                                                         GraphElementTypeEnum.Node, propsTo);
420
421                                         singleDataList.add(startN);
422
423                                         GraphElement relationFromStartNode = GraphElementFactory.createRelation(type,
424                                                         propsRelationStartNode, startN, from);
425                                         singleDataList.add(relationFromStartNode);
426
427                                         singleDataList.add(from);
428                                         singleDataList.add(to);
429                                         // get only first type on relationship. Now single type
430                                         // supported
431                                         GraphElement newElement = GraphElementFactory.createRelation(type, propsRelation, from, to);
432                                         singleDataList.add(newElement);
433                                 }
434                                 if (columns.size() == 8) {
435
436                                 }
437                         }
438                         batchList.add(singleDataList);
439                 }
440                 return batchList;
441         }
442
443         private Map<String, Object> jsonObjectToMap(JSONObject node) {
444                 Map<String, Object> props = new HashMap<>();
445
446                 for (Map.Entry<String, Object> entry : (Set<Map.Entry<String, Object>>) node.entrySet()) {
447                         props.put(entry.getKey(), entry.getValue());
448                 }
449                 return props;
450         }
451
452         private String preparePropertiesInStatement(Map<String, Object> properties) {
453                 StringBuilder sb = new StringBuilder();
454                 int count = 0;
455                 int size = properties.entrySet().size();
456                 for (Map.Entry<String, Object> entry : properties.entrySet()) {
457                         sb.append("\"").append(entry.getKey()).append("\"").append(":");
458                         if (entry.getValue() instanceof String) {
459                                 sb.append("\"");
460                         }
461                         sb.append(entry.getValue());
462                         if (entry.getValue() instanceof String) {
463                                 sb.append("\"");
464                         }
465                         ++count;
466                         if (count < size) {
467                                 sb.append(",");
468                         }
469                 }
470                 return sb.toString();
471         }
472
473         private Either<String, Neo4jOperationStatus> createNode(GraphElement element) {
474                 Either<String, Neo4jOperationStatus> status;
475                 if (element instanceof GraphNode) {
476                         GraphNode node = (GraphNode) element;
477                         String json = prepareCreateNodeBody(node);
478
479                         logger.debug("Try to save Node [{}] on graph", json);
480
481                         status = sendPostCypher(json);
482
483                         return status;
484
485                 } else {
486                         return Either.right(Neo4jOperationStatus.WRONG_INPUT);
487                 }
488         }
489
490         private Either<String, Neo4jOperationStatus> sendPostCypher(String json) {
491                 Map<String, Object> neo4jParams = ConfigurationManager.getConfigurationManager().getConfiguration().getNeo4j();
492                 String host = (String) neo4jParams.get("host");
493                 Integer port = (Integer) neo4jParams.get("port");
494                 String user = (String) neo4jParams.get("user");
495                 String password = (String) neo4jParams.get("password");
496
497                 String uri = CypherTemplates.CypherUrlTemplate.replace("$host$", host);
498                 uri = uri.replace("$port$", port.toString());
499
500                 HttpClientContext context = creatClientContext(host, user, password);
501                 CloseableHttpResponse response = null;
502
503                 HttpPost post = new HttpPost(uri);
504                 try {
505                         StringEntity input = new StringEntity(json);
506                         input.setContentType("application/json");
507                         post.setEntity(input);
508
509                         response = httpClient.execute(post, context);
510
511                         int status = response.getStatusLine().getStatusCode();
512                         String responseString;
513                         responseString = new BasicResponseHandler().handleResponse(response);
514                         logger.debug("response [{}]", responseString);
515
516                         if (status == 200 || status == 201) {
517                                 logger.debug("cypher request [{}] was succeeded", json);
518                                 Neo4jOperationStatus responseStatus = checkResponse(responseString);
519                                 if (Neo4jOperationStatus.OK.equals(responseStatus)) {
520                                         return Either.left(responseString);
521                                 } else {
522                                         return Either.right(responseStatus);
523                                 }
524                         } else {
525                                 logger.debug("cypher request [{}] was failed : [{}]", json, responseString);
526                                 return Either.right(Neo4jOperationStatus.GENERAL_ERROR);
527                         }
528
529                 } catch (HttpResponseException e) {
530                         logger.debug("failed to perform cypher request [{}]", json, e);
531                         if (e.getStatusCode() == 401) {
532                                 return Either.right(Neo4jOperationStatus.NOT_AUTHORIZED);
533                         } else {
534                                 return Either.right(Neo4jOperationStatus.GENERAL_ERROR);
535                         }
536                 } catch (ClientProtocolException e) {
537                         logger.debug("failed to perform cypher request [{}]", json, e);
538                         return Either.right(Neo4jOperationStatus.HTTP_PROTOCOL_ERROR);
539                 } catch (IOException e) {
540                         logger.debug("failed to perform cypher request [{}]", json, e);
541                         return Either.right(Neo4jOperationStatus.NOT_CONNECTED);
542                 } finally {
543                         releaseResource(response);
544                 }
545         }
546
547         private Neo4jOperationStatus checkResponse(String responseString) {
548                 try {
549                         JSONObject response = (JSONObject) jsonParser.parse(responseString);
550                         JSONArray errors = (JSONArray) response.get("errors");
551                         if (errors.size() == 0) {
552                                 return Neo4jOperationStatus.OK;
553                         } else {
554                                 Iterator<JSONObject> iterator = errors.iterator();
555                                 JSONObject error;
556                                 while (iterator.hasNext()) {
557                                         error = (JSONObject) iterator.next();
558                                         String code = (String) error.get("code");
559                                         String message = (String) error.get("message");
560
561                     return mapToNeoError(code, message);
562                                 }
563                                 return Neo4jOperationStatus.GENERAL_ERROR;
564                         }
565                 } catch (ParseException e) {
566                         logger.error("Failed to parse response", e);
567                         return Neo4jOperationStatus.GENERAL_ERROR;
568                 }
569         }
570
571         private Neo4jOperationStatus mapToNeoError(String code, String message) {
572                 Neo4jOperationStatus error;
573
574                 String[] errorCode = code.split("\\.");
575                 if (errorCode.length < 4) {
576                         error = Neo4jOperationStatus.GENERAL_ERROR;
577                 } else {
578                         // by Classification
579                         switch (errorCode[1]) {
580                         case ClientError:
581                                 // by Category
582                                 switch (errorCode[2]) {
583                                 case General:
584                                         error = Neo4jOperationStatus.DB_READ_ONLY;
585                                         break;
586                                 case LegacyIndex:
587                                         error = Neo4jOperationStatus.LEGACY_INDEX_ERROR;
588                                         break;
589                                 case Request:
590                                         error = Neo4jOperationStatus.BAD_REQUEST;
591                                         break;
592                                 case Schema:
593                                         if (errorCode[3].equals(ConstraintViolation)) {
594                                                 error = Neo4jOperationStatus.ENTITY_ALREADY_EXIST;
595                                         } else {
596                                                 error = Neo4jOperationStatus.SCHEMA_ERROR;
597                                         }
598                                         break;
599                                 case Security:
600                                         error = Neo4jOperationStatus.NOT_AUTHORIZED;
601                                         break;
602                                 case Statement:
603                                         // by Title
604                                         if (errorCode[3].equals(EntityNotFound)) {
605                                                 error = Neo4jOperationStatus.NOT_FOUND;
606                                         } else {
607                                                 if (errorCode[3].equals(ConstraintViolation)) {
608                                                         error = Neo4jOperationStatus.ENTITY_ALREADY_EXIST;
609                                                 } else {
610                                                         error = Neo4jOperationStatus.BAD_REQUEST;
611                                                 }
612                                         }
613                                         break;
614                                 case Transaction:
615                                         error = Neo4jOperationStatus.TRANSACTION_ERROR;
616                                         break;
617                                 default:
618                                         error = Neo4jOperationStatus.GENERAL_ERROR;
619                                         break;
620                                 }
621                                 break;
622                         case DatabaseError:
623                                 // by Category
624                                 switch (errorCode[2]) {
625                                 case General:
626                                         error = Neo4jOperationStatus.GENERAL_ERROR;
627                                         break;
628                                 case Schema:
629                                         error = Neo4jOperationStatus.SCHEMA_ERROR;
630                                         break;
631                                 case Statement:
632                                         error = Neo4jOperationStatus.EXECUTION_FAILED;
633                                         break;
634                                 case Transaction:
635                                         error = Neo4jOperationStatus.TRANSACTION_ERROR;
636                                         break;
637                                 default:
638                                         error = Neo4jOperationStatus.GENERAL_ERROR;
639                                         break;
640                                 }
641                                 break;
642                         case TransientError:
643                                 error = Neo4jOperationStatus.DB_NOT_AVAILABLE;
644                                 break;
645                         default:
646                                 error = Neo4jOperationStatus.GENERAL_ERROR;
647                                 break;
648                         }
649                         error.setOriginError(code).setMessage(message);
650                         String errorFromCfg = code.replace(".", "_");
651                         String helpMessage = ConfigurationManager.getConfigurationManager().getNeo4jErrorsConfiguration()
652                                         .getErrorMessage(errorFromCfg);
653                         if (helpMessage != null && !helpMessage.isEmpty()) {
654                                 error.setHelpErrorMsg(helpMessage);
655                         }
656                 }
657                 return error;
658         }
659
660         private String prepareCreateNodeBody(GraphNode node) {
661
662                 String body = CypherTemplates.CypherCreateNodeTemplate.replace("$label$", node.getLabel());
663
664                 body = body.replace("$props$", DaoUtils.convertToJson(node.toGraphMap()));
665
666                 return body;
667         }
668
669         /**
670          * the method returns all the indexes for the given label if no label is
671          * supplied ( null or "") all indexes will be returned
672          * 
673          * @param label
674          *            the name of the label
675          * @param ip
676          * @param user
677          * @param password
678          * @return a map of labels and there properties
679          */
680         public Either<Map<String, List<String>>, Neo4jOperationStatus> getIndexes(String label) {
681                 Map<String, Object> neo4jParams = ConfigurationManager.getConfigurationManager().getConfiguration().getNeo4j();
682                 String host = (String) neo4jParams.get("host");
683                 Integer port = (Integer) neo4jParams.get("port");
684                 String user = (String) neo4jParams.get("user");
685                 String password = (String) neo4jParams.get("password");
686
687                 String uri = null;
688                 if (label == null || "".equals(label)) {
689                         uri = CypherTemplates.getAllIndexsTemplate.replace("$host$", host);
690                 } else {
691                         uri = CypherTemplates.getAllIndexsTemplate.replace("$host$", host) + "/" + label;
692                 }
693                 uri = uri.replace("$port$", port.toString());
694
695                 HttpClientContext context = creatClientContext(host, user, password);
696                 CloseableHttpResponse response = null;
697
698                 HttpGet get = new HttpGet(uri);
699                 get.setHeader("Content-Type", "application/json");
700                 get.setHeader("Accept", "application/json; charset=UTF-8");
701
702                 try {
703
704                         response = httpClient.execute(get, context);
705                         int statusCode = response.getStatusLine().getStatusCode();
706                         if (statusCode != 200) {
707                                 logger.error("failed to get indexes requeste returned {}", statusCode);
708                                 return Either.right(Neo4jOperationStatus.GENERAL_ERROR);
709                         } else {
710                                 Map<String, List<String>> labels = getLeablesFromJson(response);
711                                 return Either.left(labels);
712                         }
713                 } catch (Exception e) {
714                         logger.debug("failed to get indexes ", e);
715                         return Either.right(Neo4jOperationStatus.GENERAL_ERROR);
716                 } finally {
717                         releaseResource(response);
718
719                 }
720
721         }
722
723         private Map<String, List<String>> getLeablesFromJson(CloseableHttpResponse response)
724                         throws HttpResponseException, IOException, ParseException {
725                 Map<String, List<String>> labels = new HashMap<>();
726                 String responseString = new BasicResponseHandler().handleResponse(response);
727                 JSONArray results = (JSONArray) jsonParser.parse(responseString);
728                 Iterator<JSONObject> iteratorResults = results.iterator();
729                 while (iteratorResults.hasNext()) {
730                         JSONObject elementResult = iteratorResults.next();
731                         String label = (String) elementResult.get("label");
732                         List<String> props = labels.get(label);
733                         if (props == null) {
734                                 props = new ArrayList<>();
735                                 labels.put(label, props);
736                         }
737                         JSONArray properties = (JSONArray) elementResult.get("property_keys");
738                         Iterator<String> iterator = properties.iterator();
739                         while (iterator.hasNext()) {
740                                 props.add(iterator.next());
741                         }
742                 }
743                 return labels;
744         }
745
746         public Neo4jOperationStatus createIndex(String label, List<String> propertyNames) {
747
748                 Neo4jOperationStatus result = Neo4jOperationStatus.OK;
749                 if (propertyNames != null && !propertyNames.isEmpty()) {
750
751                         Map<String, Object> neo4jParams = ConfigurationManager.getConfigurationManager().getConfiguration()
752                                         .getNeo4j();
753                         String host = (String) neo4jParams.get("host");
754                         Integer port = (Integer) neo4jParams.get("port");
755                         String user = (String) neo4jParams.get("user");
756                         String password = (String) neo4jParams.get("password");
757
758                         String uri = CypherTemplates.batchTemplate.replace("$host$", host);
759                         uri = uri.replace("$port$", port.toString());
760
761                         String opertionUri = "/schema/index/" + label;
762
763                         HttpClientContext context = creatClientContext(host, user, password);
764
765                         CloseableHttpResponse response = null;
766
767                         HttpPost post = new HttpPost(uri);
768
769                         String json = createBatchJson(HttpMethod.POST, opertionUri, propertyNames);
770
771                         try {
772                                 StringEntity input = new StringEntity(json);
773                                 input.setContentType("application/json");
774                                 post.setEntity(input);
775                                 response = httpClient.execute(post, context);
776                                 int statusCode = response.getStatusLine().getStatusCode();
777                                 if (statusCode != 200) {
778                                         logger.error("failed to create index for label [{}] with properties:{} requeste returned {}",label,propertyNames,statusCode);
779                                         result = Neo4jOperationStatus.GENERAL_ERROR;
780                                 } else {
781                                         logger.debug("index for label [{}] with properties: {} created", label, propertyNames);
782                                 }
783                         } catch (Exception e) {
784                                 logger.debug("failed to create index for label [{}] with properties: {}", label, propertyNames);
785                                 result = Neo4jOperationStatus.GENERAL_ERROR;
786                         } finally {
787
788                                 releaseResource(response);
789
790                         }
791
792                 }
793
794                 else {
795                         logger.debug("no index was created for label :{} the recived propertyNames list: {} is invalide",label,propertyNames);
796                         return Neo4jOperationStatus.WRONG_INPUT;
797                 }
798
799                 return result;
800         }
801
802         public Neo4jOperationStatus createUniquenessConstraints(String label, List<String> propertyNames) {
803                 Neo4jOperationStatus result = Neo4jOperationStatus.OK;
804                 if (propertyNames != null && !propertyNames.isEmpty()) {
805
806                         Map<String, Object> neo4jParams = ConfigurationManager.getConfigurationManager().getConfiguration()
807                                         .getNeo4j();
808                         String host = (String) neo4jParams.get("host");
809                         Integer port = (Integer) neo4jParams.get("port");
810                         String user = (String) neo4jParams.get("user");
811                         String password = (String) neo4jParams.get("password");
812
813                         String uri = CypherTemplates.batchTemplate.replace("$host$", host);
814                         uri = uri.replace("$port$", port.toString());
815
816                         String opertionUri = "/schema/constraint/" + label + "/uniqueness/";
817
818                         HttpClientContext context = creatClientContext(host, user, password);
819
820                         CloseableHttpResponse response = null;
821
822                         HttpPost post = new HttpPost(uri);
823
824                         String json = createBatchJson(HttpMethod.POST, opertionUri, propertyNames);
825
826                         try {
827                                 StringEntity input = new StringEntity(json);
828                                 input.setContentType("application/json");
829                                 post.setEntity(input);
830                                 response = httpClient.execute(post, context);
831
832                                 int statusCode = response.getStatusLine().getStatusCode();
833                                 if (statusCode != 200) {
834                                         logger.error("failed to create uniqueness constraint  for label [{}] on properties:{}. request returned ",
835                                                         label,propertyNames,statusCode);
836                                         result = Neo4jOperationStatus.GENERAL_ERROR;
837                                 } else {
838                                         logger.debug("uniqueness constraint for label [{}] on properties:{} created",label,propertyNames);
839                                 }
840                         } catch (Exception e) {
841                                 logger.error("failed to create uniqueness constraint [{}] with properties:{}",label,propertyNames,e);
842                                 result = Neo4jOperationStatus.GENERAL_ERROR;
843                         } finally {
844                                 releaseResource(response);
845                         }
846
847                 }
848
849                 else {
850                         logger.debug("no index was created for label :{} the recived propertyNames list: {} is invalide",label,propertyNames);
851                         return Neo4jOperationStatus.WRONG_INPUT;
852                 }
853
854                 return result;
855         }
856
857         public Neo4jOperationStatus deleteElement(GraphElementTypeEnum type, String label, MatchFilter filter) {
858
859                 String requestJson;
860                 // replace return type
861                 if (type.equals(GraphElementTypeEnum.Node)) {
862                         logger.debug("removing node label: {}", label);
863                         requestJson = createDeleteNodeStatment(label, filter);
864
865                 } else {
866                         logger.error(" delete on type {} is not yet supported", type);
867                         throw new RuntimeException(" delete on type " + type + " is not yet supported");
868                 }
869
870                 logger.debug("Try to perform request [{}]", requestJson);
871
872                 Either<String, Neo4jOperationStatus> status = sendPostCypher(requestJson);
873                 if (status.isRight()) {
874                         logger.error(" delete request failed with: {}", status.right());
875                         return Neo4jOperationStatus.GENERAL_ERROR;
876                 } else {
877                         return Neo4jOperationStatus.OK;
878                 }
879         }
880
881         public String getNeo4jVersion() throws Exception {
882                 Map<String, Object> neo4jParams = ConfigurationManager.getConfigurationManager().getConfiguration().getNeo4j();
883                 String host = (String) neo4jParams.get("host");
884                 Integer port = (Integer) neo4jParams.get("port");
885                 String user = (String) neo4jParams.get("user");
886                 String password = (String) neo4jParams.get("password");
887
888                 String uri = getServiceRoot.replace("$host$", host).replace("$port$", port.toString());
889
890                 HttpClientContext context = creatClientContext(host, user, password);
891                 CloseableHttpResponse response = null;
892                 String result = null;
893
894                 HttpGet get = new HttpGet(uri);
895                 get.setHeader("Content-Type", "application/json");
896                 get.setHeader("Accept", "application/json; charset=UTF-8");
897
898                 try {
899                         response = httpClient.execute(get, context);
900                         int statusCode = response.getStatusLine().getStatusCode();
901                         if (statusCode != 200) {
902                                 throw new Exception("Couldn't get Neo4j service root, HTTP status " + statusCode);
903                         } else {
904                                 // Parse response
905                                 String responseString = new BasicResponseHandler().handleResponse(response);
906                                 JSONObject responseData = (JSONObject) jsonParser.parse(responseString);
907                                 Object obj = responseData.get("neo4j_version");
908                                 if (obj != null) {
909                                         result = (String) obj;
910                                 }
911                                 return result;
912                         }
913                 } finally {
914                         releaseResource(response);
915                 }
916         }
917
918         private String createDeleteNodeStatment(String label, MatchFilter filter) {
919                 String requestJson;
920                 requestJson = CypherTemplates.CypherDeleteNodeTemplate;
921
922                 if (label != null && !label.isEmpty()) {
923                         requestJson = requestJson.replace("$label$", label);
924                 } else {
925                         requestJson = requestJson.replace("$label$", "");
926                 }
927
928                 // replace filter
929                 if (filter.getProperties().isEmpty()) {
930                         // get all records by label
931                         requestJson = requestJson.replace("{$filter$}", "");
932                 } else {
933                         String filterStr = CypherTranslator.prepareFilterBody(filter);
934                         requestJson = requestJson.replace("$filter$", filterStr);
935                 }
936                 return requestJson;
937         }
938
939         /*
940          * removed do to fortify scan CredentialsProvider cp = new
941          * BasicCredentialsProvider(); cp.setCredentials(AuthScope.ANY, new
942          * UsernamePasswordCredentials(user, password)); AuthCache authCache = new
943          * BasicAuthCache(); BasicScheme basicAuth = new BasicScheme();
944          * authCache.put(new HttpHost(ip, 7474, "http"), basicAuth);
945          * context.setAuthCache(authCache); context.setCredentialsProvider(cp);
946          * 
947          */
948         private HttpClientContext creatClientContext(String ip, String user, String password) {
949
950         return HttpClientContext.create();
951         }
952
953         private void releaseResource(CloseableHttpResponse response) {
954                 if (response != null) {
955                         try {
956                                 HttpEntity entity = response.getEntity();
957                                 EntityUtils.consume(entity);
958                                 response.close();
959                         } catch (Exception e) {
960                                 logger.error("failed to close connection exception", e);
961                         }
962                 }
963         }
964
965         private String createBatchJson(HttpMethod method, String opertionUri, List<String> propertyNames) {
966                 StringBuilder sb = new StringBuilder();
967                 sb.append("[ ");
968                 for (int i = 0; i < propertyNames.size(); i++) {
969                         sb.append("{ \"method\" : \"" + method + "\" , \"to\" : \"" + opertionUri
970                                         + "\" , \"body\" : { \"property_keys\" : [ \"" + propertyNames.get(i) + "\" ] } }");
971                         if (i + 1 < propertyNames.size()) {
972                                 sb.append(",");
973                         }
974                 }
975                 sb.append(" ]");
976         return sb.toString();
977         }
978
979         enum HttpMethod {
980                 GET, PUT, POST, DELETE
981         }
982
983 }