2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017-2018 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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
20 package org.onap.policy.pdp.rest.api.services;
22 import java.io.ByteArrayInputStream;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.ObjectInputStream;
26 import java.io.OutputStream;
27 import java.net.HttpURLConnection;
29 import java.nio.charset.StandardCharsets;
30 import java.util.Arrays;
31 import java.util.Base64;
32 import java.util.Collections;
33 import java.util.List;
35 import java.util.UUID;
37 import org.apache.commons.io.IOUtils;
38 import org.onap.policy.api.PolicyException;
39 import org.onap.policy.common.logging.flexlogger.FlexLogger;
40 import org.onap.policy.common.logging.flexlogger.Logger;
41 import org.onap.policy.pdp.rest.config.PDPApiAuth;
42 import org.onap.policy.rest.XACMLRestProperties;
43 import org.onap.policy.xacml.api.XACMLErrorConstants;
44 import org.onap.policy.xacml.std.pap.StdPDPPolicy;
46 import com.att.research.xacml.util.XACMLProperties;
47 import com.fasterxml.jackson.databind.ObjectMapper;
49 public class PAPServices {
50 private static final String SUCCESS = "success";
51 private static Logger LOGGER = FlexLogger.getLogger(PAPServices.class
54 private int responseCode = 0;
55 private static String environment = "DEVL";
56 public static Boolean junit = false;
57 private static List<String> paps = null;
58 private static final Object papResourceLock = new Object();
59 private String operation = null;
60 private String requestMethod = null;
61 private String encoding = null;
63 public PAPServices() {
64 environment = PDPApiAuth.getEnvironment();
66 synchronized (papResourceLock) {
67 String urlList = XACMLProperties.getProperty(XACMLRestProperties.PROP_PAP_URLS);
69 urlList = XACMLProperties.getProperty(XACMLRestProperties.PROP_PAP_URL);
71 paps = Arrays.asList(urlList.split(","));
76 private String getPAPEncoding(){
78 String userID = XACMLProperties.getProperty(XACMLRestProperties.PROP_PAP_USERID);
79 String pass = XACMLProperties.getProperty(XACMLRestProperties.PROP_PAP_PASS);
80 Base64.Encoder encoder = Base64.getEncoder();
81 encoding = encoder.encodeToString((userID+":"+pass).getBytes(StandardCharsets.UTF_8));
86 private void rotatePAPList(){
87 synchronized (papResourceLock) {
88 Collections.rotate(paps, -1);
92 private String getPAP(){
94 synchronized (papResourceLock) {
100 public static void setPaps(List<String> paps) {
101 PAPServices.paps = paps;
104 public int getResponseCode() {
108 public Object callPAP(Object content, String[] parameters, UUID requestID,
109 String clientScope) throws PolicyException {
110 String response = null;
111 HttpURLConnection connection = null;
113 // Checking for the available PAPs is done during the first Request and
114 // the List is going to have the connected PAP as first element.
115 // This makes it Real-Time to change the list depending on their
117 if (paps == null || paps.isEmpty()) {
118 String message = XACMLErrorConstants.ERROR_SYSTEM_ERROR + "PAPs List is Empty.";
119 LOGGER.error(message);
120 throw new PolicyException(message);
123 boolean connected = false;
124 while (papsCount < paps.size()) {
126 String fullURL = getPAP();
127 fullURL = checkParameter(parameters, fullURL);
128 URL url = new URL(fullURL);
129 LOGGER.debug("--- Sending Request to PAP : "+ url.toString() + " ---");
130 // Open the connection
131 connection = (HttpURLConnection) url.openConnection();
132 // Setting Content-Type
133 connection.setRequestProperty("Content-Type","application/json");
134 // Adding Authorization
135 connection.setRequestProperty("Authorization", "Basic "+ getPAPEncoding());
136 connection.setRequestProperty("Environment", environment);
137 connection.setRequestProperty("ClientScope", clientScope);
138 // set the method and headers
139 connection.setRequestMethod(requestMethod);
140 connection.setUseCaches(false);
141 connection.setInstanceFollowRedirects(false);
142 connection.setDoOutput(true);
143 connection.setDoInput(true);
145 if (requestID == null) {
146 requestID = UUID.randomUUID();
147 LOGGER.info("No request ID provided, sending generated ID: "
148 + requestID.toString());
150 LOGGER.info("Using provided request ID: "
151 + requestID.toString());
153 connection.setRequestProperty("X-ECOMP-RequestID",
154 requestID.toString());
155 if (content != null && (content instanceof InputStream)) {
156 // send current configuration
157 try (OutputStream os = connection.getOutputStream()) {
158 int count = IOUtils.copy((InputStream) content, os);
159 if (LOGGER.isDebugEnabled()) {
160 LOGGER.debug("copied to output, bytes=" + count);
163 } else if(content != null){
164 // the content is an object to be encoded in JSON
165 ObjectMapper mapper = new ObjectMapper();
167 mapper.writeValue(connection.getOutputStream(),
172 connection.connect();
173 responseCode = connection.getResponseCode();
174 // If Connected to PAP then break from the loop and continue
176 if (connection.getResponseCode() > 0 || junit) {
180 LOGGER.debug(XACMLErrorConstants.ERROR_PERMISSIONS+ "PAP Response Code : " + connection.getResponseCode());
183 } catch (Exception e) {
184 // This means that the PAP is not working
189 LOGGER.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR
190 + "PAP connection Error : " + e);
197 LOGGER.debug("connected to the PAP : " + getPAP());
198 LOGGER.debug("--- Response: ---");
199 if(connection != null){
200 Map<String, List<String>> headers = connection.getHeaderFields();
201 for (String key : headers.keySet()) {
202 LOGGER.debug("Header :" + key + " Value: " + headers.get(key));
206 response = checkResponse(connection, requestID);
207 } catch (IOException e) {
208 LOGGER.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + e);
209 response = XACMLErrorConstants.ERROR_SYSTEM_ERROR + e;
210 throw new PolicyException(
211 XACMLErrorConstants.ERROR_SYSTEM_ERROR
212 + "Decoding the result ", e);
218 response = XACMLErrorConstants.ERROR_SYSTEM_ERROR + "connection is null";
222 response = XACMLErrorConstants.ERROR_DATA_ISSUE
223 + "Unable to get valid response from PAP(s) " + paps;
228 public String getActiveVersion(String policyScope, String filePrefix, String policyName, String clientScope, UUID requestID) {
229 String version = null;
230 HttpURLConnection connection = null;
231 String [] parameters = {"apiflag=version","policyScope="+policyScope, "filePrefix="+filePrefix, "policyName="+policyName};
232 if (paps == null || paps.isEmpty()) {
233 LOGGER.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "PAPs List is Empty.");
236 boolean connected = false;
237 while (papsCount < paps.size()) {
239 String fullURL = getPAP();
240 if (parameters != null && parameters.length > 0) {
241 String queryString = "";
242 for (String p : parameters) {
243 queryString += "&" + p;
245 fullURL += "?" + queryString.substring(1);
248 URL url = new URL (fullURL);
250 //Open the connection
251 connection = (HttpURLConnection)url.openConnection();
253 // Setting Content-Type
254 connection.setRequestProperty("Content-Type",
257 // Adding Authorization
258 connection.setRequestProperty("Authorization", "Basic "
261 connection.setRequestProperty("Environment", environment);
262 connection.setRequestProperty("ClientScope", clientScope);
265 //set the method and headers
266 connection.setRequestMethod("GET");
267 connection.setUseCaches(false);
268 connection.setInstanceFollowRedirects(false);
269 connection.setDoOutput(true);
270 connection.setDoInput(true);
271 connection.setRequestProperty("X-ECOMP-RequestID", requestID.toString());
274 connection.connect();
276 // If Connected to PAP then break from the loop and continue with the Request
277 if (connection.getResponseCode() > 0) {
282 LOGGER.debug(XACMLErrorConstants.ERROR_SYSTEM_ERROR + "PAP connection Error");
284 } catch (Exception e) {
285 // This means that the PAP is not working
286 LOGGER.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + "PAP connection Error : " + e);
294 LOGGER.debug("connected to the PAP : " + getPAP());
295 LOGGER.debug("--- Response: ---");
296 Map<String, List<String>> headers = connection.getHeaderFields();
297 for (String key : headers.keySet()) {
298 LOGGER.debug("Header :" + key + " Value: " + headers.get(key));
301 if (connection.getResponseCode() == 200) {
302 // Check for successful creation of policy
303 version = connection.getHeaderField("version");
304 LOGGER.debug("ActiveVersion from the Header: " + version);
305 } else if (connection.getResponseCode() == 403) {
306 LOGGER.error(XACMLErrorConstants.ERROR_PERMISSIONS + "response code of the URL is "
307 + connection.getResponseCode() + ". PEP is not Authorized for making this Request!! \n Contact Administrator for this Scope. ");
309 } else if (connection.getResponseCode() == 404) {
310 LOGGER.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "response code of the URL is "
311 + connection.getResponseCode() + ". This indicates a problem with getting the version from the PAP");
314 LOGGER.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "BAD REQUEST: Error occured while getting the version from the PAP. The request may be incorrect.");
316 } catch (IOException e) {
317 LOGGER.error(XACMLErrorConstants.ERROR_DATA_ISSUE + e);
320 LOGGER.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + "Unable to get valid response from PAP(s) " + paps);
326 private String checkResponse(HttpURLConnection connection, UUID requestID) throws IOException {
327 String response = null;
328 if (responseCode == 200 || junit) {
329 // Check for successful creation of policy
330 String isSuccess = null;
331 if (!junit) { // is this a junit test?
332 isSuccess = connection.getHeaderField("successMapKey");
333 operation = connection.getHeaderField("operation");
337 if (SUCCESS.equals(isSuccess)) {
338 if ("update".equals(operation)) {
339 response = "Transaction ID: " + requestID + " --Policy with the name "+ connection.getHeaderField("policyName")
340 + " was successfully updated. ";
341 if (connection.getHeaderField("safetyChecker")!=null) {
343 + "\n\nPolicy Safety Checker Warning: This closedLoopControlName "
344 + "is potentially in conflict with " + connection.getHeaderField("conflictCLName")
345 + " that already exists." + " See detailed information on ClosedLoop Pairs below: "
346 +"\n\n"+connection.getHeaderField("safetyChecker");
348 } else if ("create".equals(operation)) {
349 response = "Transaction ID: " + requestID + " --Policy with the name "+ connection.getHeaderField("policyName")
350 + " was successfully created.";
351 if (connection.getHeaderField("safetyChecker")!=null) {
353 + "\n\nPolicy Safety Checker Warning: This closedLoopControlName "
354 + "is potentially in conflict with " + connection.getHeaderField("conflictCLName")
355 + " that already exists. " + "See detailed information on ClosedLoop Pairs below: "
356 +"\n\n"+connection.getHeaderField("safetyChecker");
358 } else if ("delete".equals(operation)) {
359 response = "Transaction ID: " + requestID + " --The policy was successfully deleted.";
360 } else if ("import".equals(operation)) {
361 response = "Transaction ID: " + requestID + " --The policy engine import for "+ connection.getHeaderField("service")
362 + " was successfull.";
363 } else if ("createDictionary".equals(operation)) {
364 response = "Transaction ID: " + requestID + " --Dictionary Item was added successfully!";
365 } else if ("updateDictionary".equals(operation)) {
366 response = "Transaction ID: " + requestID + " --Dictionary Item was updated successfully!";
367 } else if ("getDictionary".equals(operation)) {
371 //get the json string from the response
372 InputStream is = connection.getInputStream();
374 // read the inputStream into a buffer (trick found online scans entire input looking for end-of-file)
375 java.util.Scanner scanner = new java.util.Scanner(is);
376 scanner.useDelimiter("\\A");
377 json = scanner.hasNext() ? scanner.next() : "";
380 } catch (IOException e1) {
381 LOGGER.error(e1.getMessage() + e1);
383 response = "Transaction ID: " + requestID + " --Dictionary Items Retrieved " + json;
384 } else if ("getMetrics".equals(operation)) {
385 response = "Transaction ID: " + requestID + " --Policy Metrics Retrieved " + connection.getHeaderField("metrics");
387 LOGGER.info(response);
389 String message = XACMLErrorConstants.ERROR_DATA_ISSUE
390 + "Operation unsuccessful, unable to complete the request!";
391 LOGGER.error(message);
394 } else if (connection.getResponseCode() == 202) {
395 if ("delete".equalsIgnoreCase(connection.getHeaderField("operation")) &&
396 "true".equals(connection.getHeaderField("lockdown"))) {
397 response = "Transaction ID: "
399 + " --Policies are locked down, please try again later.";
400 LOGGER.warn(response);
402 } else if (connection.getResponseCode() == 204) {
403 if ("push".equals(connection.getHeaderField("operation"))) {
404 response = "Transaction ID: "
407 + connection.getHeaderField("policyId")
408 + "' was successfully pushed to the PDP group '"
409 + connection.getHeaderField("groupId") + "'.";
410 LOGGER.info(response);
412 } else if (connection.getResponseCode() == 400 && connection.getHeaderField("error") != null) {
413 response = connection.getHeaderField("error");
414 LOGGER.error(response);
415 } else if (connection.getResponseCode() == 403) {
416 response = XACMLErrorConstants.ERROR_PERMISSIONS
417 + "response code of the URL is "
418 + connection.getResponseCode()
419 + ". PEP is not Authorized for making this Request!! \n Contact Administrator for this Scope. ";
420 LOGGER.error(response);
421 } else if (connection.getResponseCode() == 404 && connection.getHeaderField("error") != null) {
422 if ("UnknownGroup".equals(connection.getHeaderField("error"))) {
423 response = XACMLErrorConstants.ERROR_DATA_ISSUE
424 + connection.getHeaderField("message")
425 + " Please check the pdpGroup you are requesting to push the policy to.";
426 LOGGER.error(response);
427 } else if ("policyNotAvailableForEdit".equals(connection.getHeaderField("error"))) {
428 response = XACMLErrorConstants.ERROR_DATA_ISSUE
429 + connection.getHeaderField("message");
431 } else if (connection.getResponseCode() == 409 && connection.getHeaderField("error") != null) {
432 if ("modelExistsDB".equals(connection.getHeaderField("error"))) {
433 response = XACMLErrorConstants.ERROR_DATA_ISSUE
434 + "Import Value Exist Error: The import value "
435 + connection.getHeaderField("service")
436 + " already exist on the PAP. "
437 + "Please create a new import value.";
438 } else if ("policyExists".equals(connection.getHeaderField("error"))) {
439 response = XACMLErrorConstants.ERROR_DATA_ISSUE
440 + "Policy Exist Error: The Policy "
441 + connection.getHeaderField("policyName")
442 + " already exist on the PAP. "
443 + "Please create a new policy or use the update API to modify the existing one.";
444 } else if ("dictionaryItemExists".equals(connection.getHeaderField("error"))) {
445 response = XACMLErrorConstants.ERROR_DATA_ISSUE
446 + "Dictionary Item Exist Error: The Dictionary Item already exist in the database. "
447 + "Please create a new Dictionary Item or use the update API to modify the existing one.";
448 } else if ("duplicateGroup".equals(connection.getHeaderField("error"))) {
449 response = XACMLErrorConstants.ERROR_DATA_ISSUE
450 + "Group Policy Scope List Exist Error: The Group Policy Scope List for this Dictionary Item already exist in the database. "
451 + "Duplicate Group Policy Scope Lists for multiple groupNames is not allowed. "
452 + "Please review the request and verify that the groupPolicyScopeListData1 is unique compared to existing groups.";
453 } else if("PolicyInPDP".equals(connection.getHeaderField("error"))){
454 response = XACMLErrorConstants.ERROR_DATA_ISSUE
455 + "Policy Exist Error: The Policy trying to be deleted is active in PDP. "
456 + "Active PDP Polcies are not allowed to be deleted from PAP. "
457 + "Please First remove the policy from PDP in order to successfully delete the Policy from PAP.";
459 LOGGER.error(response);
460 } else if (connection.getResponseCode() == 500 && connection.getHeaderField("error") != null) {
461 if ("jpautils".equals(connection.getHeaderField("error"))) {
462 response = XACMLErrorConstants.ERROR_SYSTEM_ERROR
463 + "Could not create JPAUtils instance on the PAP";
464 } else if ("deleteDB".equals(connection.getHeaderField("error"))) {
465 response = XACMLErrorConstants.ERROR_SYSTEM_ERROR
466 + "Failed to delete Policy from database.";
467 } else if ("deleteFile".equals(connection.getHeaderField("error"))) {
468 response = XACMLErrorConstants.ERROR_DATA_ISSUE
469 + "Cannot delete the policy file";
470 } else if ("groupUpdate".equals(connection.getHeaderField("error"))) {
471 response = connection.getHeaderField("message");
472 } else if ("unknown".equals(connection.getHeaderField("error"))) {
473 response = XACMLErrorConstants.ERROR_UNKNOWN
474 + "Failed to delete the policy for an unknown reason. Check the file system and other logs for further information.";
475 } else if ("deleteConfig".equals(connection.getHeaderField("error"))) {
476 response = XACMLErrorConstants.ERROR_DATA_ISSUE
477 + "Cannot delete the configuration or action body file in specified location.";
478 } else if ("missing".equals(connection.getHeaderField("error"))) {
479 response = XACMLErrorConstants.ERROR_DATA_ISSUE
480 + "Failed to create value in database because service does match a value in file";
481 } else if ("importDB".equals(connection.getHeaderField("error"))) {
482 response = XACMLErrorConstants.ERROR_DATA_ISSUE
483 + "Database errors during policy engine import";
484 } else if ("policyCopyError".equals(connection.getHeaderField("error"))) {
485 response = XACMLErrorConstants.ERROR_PROCESS_FLOW
486 + connection.getHeaderField("message");
487 } else if ("addGroupError".equals(connection.getHeaderField("error"))) {
488 response = connection.getHeaderField("message");
489 } else if ("validation".equals(connection.getHeaderField("error"))){
490 response = XACMLErrorConstants.ERROR_DATA_ISSUE
491 + "Validation errors during policy engine " + connection.getHeaderField("operation") +
492 " for " + connection.getHeaderField("service");
493 } else if ("error".equals(connection.getHeaderField("error"))) {
494 response = XACMLErrorConstants.ERROR_UNKNOWN
495 + "Could not create or update the policy for and unknown reason";
497 response = XACMLErrorConstants.ERROR_SYSTEM_ERROR
498 + "Error occured while attempting perform this operation.. "
499 + "the request may be incorrect or the PAP is unreachable. "
500 + connection.getHeaderField("error");
502 LOGGER.error(response);
504 response = XACMLErrorConstants.ERROR_SYSTEM_ERROR
505 + "Error occured while attempting perform this operation.. "
506 + "the request may be incorrect or the PAP is unreachable.";
507 LOGGER.error(response);
512 private String checkParameter(String[] parameters, String fullURL) {
513 if (parameters != null && parameters.length > 0) {
514 String queryString = "";
515 for (String p : parameters) {
516 queryString += "&" + p;
517 if (p.equalsIgnoreCase("operation=post")) {
518 requestMethod = "POST";
519 } else if (p.equalsIgnoreCase("operation=delete")) {
520 requestMethod = "DELETE";
521 operation = "delete";
522 } else if (p.equalsIgnoreCase("operation=get")) {
523 requestMethod = "GET";
525 } else if (p.equalsIgnoreCase("operation=put")||p.equalsIgnoreCase("operation=create")
526 ||p.equalsIgnoreCase("operation=update")||p.equalsIgnoreCase("operation=createDictionary")){
527 requestMethod = "PUT";
528 if (p.equalsIgnoreCase("operation=create")) {
529 operation = "create";
530 } else if (p.equalsIgnoreCase("operation=update")) {
531 operation = "update";
532 } else if (p.equalsIgnoreCase("operation=createDictionary")){
533 operation = "createDictionary";
535 }else if (p.equalsIgnoreCase("importService=MICROSERVICE")||p.equalsIgnoreCase("importService=BRMSPARAM")){
536 requestMethod = "PUT";
539 fullURL += "?" + queryString.substring(1);
544 public StdPDPPolicy pushPolicy(String policyScope, String filePrefix,
545 String policyName, String clientScope, String pdpGroup,
546 UUID requestID) throws PolicyException {
548 + "\"apiflag\": \"api\","
549 + "\"policyScope\": \""+policyScope+"\","
550 + "\"filePrefix\": \""+filePrefix+"\","
551 + "\"policyName\": \""+policyName+"\","
552 + "\"clientScope\": \""+clientScope+"\","
553 + "\"pdpGroup\": \""+pdpGroup+"\"}";
555 HttpURLConnection connection = null;
557 if (paps == null || paps.isEmpty()) {
558 String message = XACMLErrorConstants.ERROR_SYSTEM_ERROR + "PAPs List is Empty.";
559 LOGGER.error(message);
560 throw new PolicyException(message);
563 boolean connected = false;
564 while (papsCount < paps.size()) {
566 String fullURL = getPAP();
567 fullURL = (fullURL.endsWith("/"))? fullURL+"onap/pushPolicy" : fullURL+"/onap/pushPolicy";
568 URL url = new URL(fullURL);
569 LOGGER.debug("--- Sending Request to PAP : "+ url.toString() + " ---");
570 // Open the connection
571 connection = (HttpURLConnection) url.openConnection();
572 // Setting Content-Type
573 connection.setRequestProperty("Content-Type","application/json");
574 // Adding Authorization
575 connection.setRequestProperty("Authorization", "Basic "+ getPAPEncoding());
576 connection.setRequestProperty("Environment", environment);
577 connection.setRequestProperty("ClientScope", clientScope);
578 // set the method and headers
579 connection.setRequestMethod("POST");
580 connection.setUseCaches(false);
581 connection.setInstanceFollowRedirects(false);
582 connection.setDoOutput(true);
584 if (requestID == null) {
585 requestID = UUID.randomUUID();
586 LOGGER.info("No request ID provided, sending generated ID: "
587 + requestID.toString());
589 LOGGER.info("Using provided request ID: "
590 + requestID.toString());
592 connection.setRequestProperty("X-ECOMP-RequestID",
593 requestID.toString());
595 try (OutputStream os = connection.getOutputStream()) {
596 int count = IOUtils.copy(new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8)), os);
597 if (LOGGER.isDebugEnabled()) {
598 LOGGER.debug("copied to output, bytes=" + count);
601 connection.connect();
602 responseCode = connection.getResponseCode();
603 // If Connected to PAP then break from the loop and continue
605 if (connection.getResponseCode() > 0 || junit) {
609 LOGGER.debug(XACMLErrorConstants.ERROR_PERMISSIONS+ "PAP Response Code : " + connection.getResponseCode());
612 } catch (Exception e) {
613 // This means that the PAP is not working
618 LOGGER.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR
619 + "PAP connection Error : " + e);
626 LOGGER.debug("connected to the PAP : " + getPAP());
627 LOGGER.debug("--- Response: ---");
628 if(connection != null){
629 Map<String, List<String>> headers = connection.getHeaderFields();
630 for (String key : headers.keySet()) {
631 LOGGER.debug("Header :" + key + " Value: " + headers.get(key));
634 if(responseCode==202){
635 StdPDPPolicy policy = (StdPDPPolicy) new ObjectInputStream(connection.getInputStream()).readObject();
638 } catch (IOException | ClassNotFoundException e) {
639 LOGGER.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + e);
640 throw new PolicyException(
641 XACMLErrorConstants.ERROR_SYSTEM_ERROR
642 + "Decoding the result ", e);