Ejemplo n.º 1
0
  private JsonObject calculateSummary(
      JsonArray regIds, HashMap<String, JsonObject> response, long multicastId) {
    int success = 0;
    int failure = 0;
    int canonicalIds = 0;
    JsonArray deliveries = new JsonArray();
    for (Object regId : regIds) {
      JsonObject result = response.get(regId);
      if (!voidNull(result.getString("message_id")).isEmpty()) {
        success++;
        if (!voidNull(result.getString("registration_id")).isEmpty()) {
          canonicalIds++;
        }
      } else {
        failure++;
      }
      // add results, in the same order as the input
      deliveries.add(result);
    }
    // build a new object with the overall result
    JsonObject reply = new JsonObject();
    reply.putNumber("multicast_id", multicastId);
    reply.putNumber("success", success);
    reply.putNumber("failure", failure);
    reply.putNumber("canonical_ids", canonicalIds);
    reply.putArray("results", deliveries);

    return reply;
  }
Ejemplo n.º 2
0
 private JsonObject setDefaults(JsonObject config) {
   config = config.copy();
   // Set the defaults
   if (config.getNumber("session_timeout") == null) {
     config.putNumber("session_timeout", 5l * 1000);
   }
   if (config.getBoolean("insert_JSESSIONID") == null) {
     config.putBoolean("insert_JSESSIONID", true);
   }
   if (config.getNumber("heartbeat_period") == null) {
     config.putNumber("heartbeat_period", 25l * 1000);
   }
   if (config.getNumber("max_bytes_streaming") == null) {
     config.putNumber("max_bytes_streaming", 128 * 1024);
   }
   if (config.getString("prefix") == null) {
     config.putString("prefix", "/");
   }
   if (config.getString("library_url") == null) {
     config.putString("library_url", "http://cdn.sockjs.org/sockjs-0.2.1.min.js");
   }
   if (config.getArray("disabled_transports") == null) {
     config.putArray("disabled_transports", new JsonArray());
   }
   return config;
 }
Ejemplo n.º 3
0
 protected Structure(String externalId, JsonObject struct) {
   if (struct != null && externalId != null && externalId.equals(struct.getString("externalId"))) {
     this.id = struct.getString("id");
   } else {
     throw new IllegalArgumentException("Invalid structure with externalId : " + externalId);
   }
   this.externalId = externalId;
   this.struct = struct;
 }
Ejemplo n.º 4
0
  /*
  Empty inboundPermitted means reject everything - this is the default.
  If at least one match is supplied and all the fields of any match match then the message inboundPermitted,
  this means that specifying one match with a JSON empty object means everything is accepted
   */
  private Match checkMatches(boolean inbound, String address, Object body) {

    if (inbound && acceptedReplyAddresses.remove(address)) {
      // This is an inbound reply, so we accept it
      return new Match(true, false);
    }

    List<JsonObject> matches = inbound ? inboundPermitted : outboundPermitted;

    for (JsonObject matchHolder : matches) {
      String matchAddress = matchHolder.getString("address");
      String matchRegex;
      if (matchAddress == null) {
        matchRegex = matchHolder.getString("address_re");
      } else {
        matchRegex = null;
      }

      boolean addressOK;
      if (matchAddress == null) {
        if (matchRegex == null) {
          addressOK = true;
        } else {
          addressOK = regexMatches(matchRegex, address);
        }
      } else {
        addressOK = matchAddress.equals(address);
      }

      if (addressOK) {
        boolean matched = true;
        // Can send message other than JSON too - in which case we can't do deep matching on
        // structure of message
        if (body instanceof JsonObject) {
          JsonObject match = matchHolder.getObject("match");
          if (match != null) {
            for (String fieldName : match.getFieldNames()) {
              if (!match.getField(fieldName).equals(((JsonObject) body).getField(fieldName))) {
                matched = false;
                break;
              }
            }
          }
        }
        if (matched) {
          Boolean b = matchHolder.getBoolean("requires_auth");
          return new Match(true, b != null && b);
        }
      }
    }
    return new Match(false, false);
  }
Ejemplo n.º 5
0
 private String getMandatoryString(JsonObject json, String field) {
   String value = json.getString(field);
   if (value == null) {
     throw new IllegalStateException(field + " must be specified for message");
   }
   return value;
 }
Ejemplo n.º 6
0
 private static String getLatestUpdate(JsonObject json) {
   JsonObject responseData = json.getObject("ResponseData");
   if (responseData != null) {
     return responseData.getString("LatestUpdate");
   }
   return "ResponseData missing";
 }
Ejemplo n.º 7
0
 @Test
 public void testJsonObject() throws Exception {
   JsonObject obj = new JsonObject().putString("foo", "bar");
   String str = obj.encode();
   JsonObject obj2 = new JsonObject(str);
   assertEquals("bar", obj2.getString("foo"));
 }
Ejemplo n.º 8
0
 private void doSendOrPub(
     final boolean send, final SockJSSocket sock, final String address, final JsonObject message) {
   final Object body = getMandatoryValue(message, "body");
   final String replyAddress = message.getString("replyAddress");
   if (log.isDebugEnabled()) {
     log.debug("Received msg from client in bridge. address:" + address + " message:" + body);
   }
   Match curMatch = checkMatches(true, address, body);
   if (curMatch.doesMatch) {
     if (curMatch.requiresAuth) {
       final String sessionID = message.getString("sessionID");
       if (sessionID != null) {
         authorise(
             message,
             sessionID,
             new AsyncResultHandler<Boolean>() {
               public void handle(AsyncResult<Boolean> res) {
                 if (res.succeeded()) {
                   if (res.result) {
                     cacheAuthorisation(sessionID, sock);
                     checkAndSend(send, address, body, sock, replyAddress);
                   } else {
                     log.debug(
                         "Inbound message for address "
                             + address
                             + " rejected because sessionID is not authorised");
                   }
                 } else {
                   log.error("Error in performing authorisation", res.exception);
                 }
               }
             });
       } else {
         log.debug(
             "Inbound message for address "
                 + address
                 + " rejected because it requires auth and sessionID is missing");
       }
     } else {
       checkAndSend(send, address, body, sock, replyAddress);
     }
   } else {
     log.debug("Inbound message for address " + address + " rejected because there is no match");
   }
 }
Ejemplo n.º 9
0
  @Override
  public void start(final Future<Void> startedResult) {
    logger = new DebloxLogger("Boot");
    config = this.getContainer().config();

    logger.info("config: " + config.toString());
    logger.info("main: " + config.getString("main"));

    container.deployVerticle(
        config.getString("main", "com.deblox.services.TransponderService"),
        config,
        new AsyncResultHandler<String>() {

          public void handle(AsyncResult<String> deployResult) {
            if (deployResult.succeeded()) {

              // service classes
              for (final Object service : config.getArray("services", new JsonArray())) {
                logger.info("deploying service: " + service);
                container.deployVerticle(
                    service.toString(),
                    config,
                    new AsyncResultHandler<String>() {

                      public void handle(AsyncResult<String> deployResult) {
                        if (deployResult.succeeded()) {
                          logger.info("deployed service: " + service);
                        } else {
                          logger.warning(
                              "deploy of service: " + service + " failed, " + deployResult.cause());
                        }
                      }
                    });
              }

              startedResult.setResult(null);

            } else {
              logger.warning("deploying module, " + deployResult.cause());
              startedResult.setFailure(deployResult.cause());
            }
          }
        });
  }
 private void replyForError(JsonObject message) {
   if (message != null) {
     log.error(message.getString("message"));
     request.response().end("{}");
   } else {
     log.error("Empty Request");
     request.response().setStatusCode(400);
   }
   cleanup(requestId);
 }
Ejemplo n.º 11
0
  @Test
  public void testRetrieveJsonElementFromJsonObject() {
    JsonObject objElement = new JsonObject().putString("foo", "bar");

    JsonObject tester = new JsonObject().putElement("elementField", objElement);

    JsonElement testElement = tester.getElement("elementField");

    assertEquals(objElement.getString("foo"), testElement.asObject().getString("foo"));
  }
Ejemplo n.º 12
0
  private static void writeHeaderFields(JsonObject result) {
    JsonObject train = findFirstTrain(result);

    if (train == null) {
      return;
    }

    result.putString("StopAreaName", train.getString("StopAreaName"));
    result.putNumber("SiteId", train.getInteger("SiteId"));
  }
Ejemplo n.º 13
0
 @Override
 public void handle(Message<JsonObject> event) {
   Integer id = event.body.getInteger("id");
   JsonObject params = event.body.getObject("op");
   JsonObject state = event.body.getObject("state");
   JsonObject meta = state.getObject("meta");
   if (meta == null) {
     meta = new JsonObject();
   }
   StringBuilder key = new StringBuilder();
   key.append(HandlerUtils.determineKs(params, state, null));
   key.append(' ');
   key.append(HandlerUtils.determineCf(params, state, null));
   key.append(' ');
   key.append(params.getString("type"));
   meta.putObject(key.toString(), new JsonObject().putString("clazz", params.getString("clazz")));
   state.putObject("meta", meta);
   event.reply(new JsonObject().putString(id.toString(), "OK").putObject("state", state));
 }
Ejemplo n.º 14
0
  @Test
  public void testRetrieveJsonElementFromJsonArray() {
    JsonObject objElement = new JsonObject().putString("foo", "bar");

    /* Insert an Object */
    JsonArray tester = new JsonArray().addElement(objElement);

    JsonElement testElement = (JsonElement) tester.get(0);

    assertEquals(objElement.getString("foo"), testElement.asObject().getString("foo"));
  }
Ejemplo n.º 15
0
  @Override
  protected void prepareUser(User user, String userId, JsonObject data) {
    user.setUser(data.getString(principalAttributeName));
    user.setAttributes(new HashMap<String, String>());

    try {
      if (data.getString("lastName") != null && data.getString("firstName") != null) {
        user.getAttributes().put("nom", data.getString("lastName"));
        user.getAttributes().put("prenom", data.getString("firstName"));
      }

      if (data.getString("birthDate") != null) {
        user.getAttributes()
            .put(
                "dateNaissance",
                data.getString("birthDate").replaceAll("([0-9]+)-([0-9]+)-([0-9]+)", "$3/$2/$1"));
      }
      if (data.getString("postalCode") != null) {
        user.getAttributes().put("codePostal", data.getString("postalCode"));
      }

      String category = null;
      JsonArray types = data.getArray("type");
      for (Object type : types.toList()) {
        switch (type.toString()) {
          case "Student":
            category = checkProfile(category, "National_1");
            break;
          case "Teacher":
            category = checkProfile(category, "National_3");
            break;
          case "Relative":
            category = checkProfile(category, "National_2");
            break;
          case "Personnel":
            category = checkProfile(category, "National_4");
            break;
        }
      }
      if (category != null) {
        user.getAttributes().put("categories", category);
      }
    } catch (Exception e) {
      log.error("Failed to transform User for Pronote");
    }
  }
Ejemplo n.º 16
0
  private static void setUniqueKeysOn(JsonArray trains) {
    if (trains == null) {
      return;
    }

    stream(trains.spliterator(), false)
        .forEach(
            o -> {
              JsonObject train = (JsonObject) o;
              Integer siteId = train.getInteger("SiteId");
              if (siteId != null) {
                train.putString(
                    "Key",
                    Keys.key(
                        siteId,
                        "" + train.getString("TimeTabledDateTime"),
                        train.getInteger("JourneyDirection")));
              } else {
                train.putString(
                    "Key",
                    Keys.key(
                        "" + train.getString("TimeTabledDateTime"),
                        train.getInteger("JourneyDirection")));
              }
            });

    List<String> keyList =
        stream(trains.spliterator(), false)
            .map(o -> ((JsonObject) o).getString("Key"))
            .collect(toList());

    Set<String> keySet =
        stream(trains.spliterator(), false)
            .map(o -> ((JsonObject) o).getString("Key"))
            .collect(toSet());

    if (keyList.size() != keySet.size()) {
      throw new IllegalStateException("duplicate keys in " + keyList);
    }
  }
Ejemplo n.º 17
0
    @Override
    public void handle(Message<JsonObject> reply) {
      final JsonObject body = reply.body();

      if ("ok".equals(body.getString("status"))) {
        this.worlds.add(body.getObject("result"));
        if (this.worlds.size() == this.queries) {
          // All queries have completed; send the response.
          final String result = Json.encode(worlds);
          final int contentLength = result.getBytes(StandardCharsets.UTF_8).length;
          this.req.response().putHeader("Content-Type", "application/json; charset=UTF-8");
          this.req.response().putHeader("Content-Length", String.valueOf(contentLength));
          this.req.response().end(result);
        }
      } else {
        System.err.println("Failed to execute query");
      }
    }
Ejemplo n.º 18
0
 /** Redeploys a deployment. */
 private void doRedeploy(final JsonObject deploymentInfo) {
   if (deploymentInfo.getString("type").equals("module")) {
     log.info(
         String.format(
             "%s - redeploying module %s",
             DefaultGroupManager.this, deploymentInfo.getString("module")));
     final CountDownLatch latch = new CountDownLatch(1);
     platform.deployModule(
         deploymentInfo.getString("module"),
         deploymentInfo.getObject("config", new JsonObject()),
         deploymentInfo.getInteger("instances", 1),
         createRedeployHandler(deploymentInfo, latch));
     try {
       latch.await(10, TimeUnit.SECONDS);
     } catch (InterruptedException e) {
     }
   } else if (deploymentInfo.getString("type").equals("verticle")) {
     log.info(
         String.format(
             "%s - redeploying verticle %s",
             DefaultGroupManager.this, deploymentInfo.getString("main")));
     final CountDownLatch latch = new CountDownLatch(1);
     if (deploymentInfo.getBoolean("worker", false)) {
       platform.deployWorkerVerticle(
           deploymentInfo.getString("main"),
           deploymentInfo.getObject("config", new JsonObject()),
           deploymentInfo.getInteger("instances", 1),
           deploymentInfo.getBoolean("multi-threaded"),
           createRedeployHandler(deploymentInfo, latch));
     } else {
       platform.deployVerticle(
           deploymentInfo.getString("main"),
           deploymentInfo.getObject("config", new JsonObject()),
           deploymentInfo.getInteger("instances", 1),
           createRedeployHandler(deploymentInfo, latch));
     }
     try {
       latch.await(10, TimeUnit.SECONDS);
     } catch (InterruptedException e) {
     }
   }
 }
Ejemplo n.º 19
0
 private boolean doResubmit(JsonObject entry) {
   return voidNull(entry.getString("error")).equalsIgnoreCase("Unavailable");
 }
Ejemplo n.º 20
0
 /**
  * Deserializes a context from JSON.
  *
  * @param context The serialized context.
  * @return The deserialized context.
  */
 public static <T extends Context<T>> T deserialize(JsonObject context) {
   return deserialize(context.getString("uri"), context.getObject("context"));
 }
  public void start() {

    // handler config JSON
    JsonObject config = container.config();

    int port = config.getNumber("hec_port").intValue();
    int poolsize = config.getNumber("hec_poolsize").intValue();
    this.token = config.getString("hec_token");
    this.index = config.getString("index");
    this.source = config.getString("source");
    this.sourcetype = config.getString("sourcetype");
    boolean useHTTPs = config.getBoolean("hec_https");

    this.hecBatchMode = config.getBoolean("hec_batch_mode");
    this.hecMaxBatchSizeBytes = config.getNumber("hec_max_batch_size_bytes").longValue();
    this.hecMaxBatchSizeEvents = config.getNumber("hec_max_batch_size_events").longValue();
    this.hecMaxInactiveTimeBeforeBatchFlush =
        config.getNumber("hec_max_inactive_time_before_batch_flush").longValue();

    this.batchBuffer = Collections.synchronizedList(new LinkedList<String>());
    this.lastEventReceivedTime = System.currentTimeMillis();

    // Event Bus (so we can receive the data)
    String eventBusAddress = config.getString("output_address");
    EventBus eb = vertx.eventBus();

    client = vertx.createHttpClient().setPort(port).setHost("localhost").setMaxPoolSize(poolsize);
    if (useHTTPs) {
      client.setSSLContext(getSSLContext());
      client.setVerifyHost(false);
      client.setSSL(true);
      client.setTrustAll(true);
    }

    // data handler that will process our received data
    Handler<Message<String>> myHandler =
        new Handler<Message<String>>() {

          public void handle(Message<String> message) {

            try {

              String messageContent = escapeMessageIfNeeded(message.body());

              StringBuffer json = new StringBuffer();
              json.append("{\"").append("event\":").append(messageContent).append(",\"");

              if (!index.equalsIgnoreCase("default"))
                json.append("index\":\"").append(index).append("\",\"");

              json.append("source\":\"")
                  .append(source)
                  .append("\",\"")
                  .append("sourcetype\":\"")
                  .append(sourcetype)
                  .append("\"")
                  .append("}");

              String bodyContent = json.toString();

              if (hecBatchMode) {
                lastEventReceivedTime = System.currentTimeMillis();
                currentBatchSizeBytes += bodyContent.length();
                batchBuffer.add(bodyContent);
                if (flushBuffer()) {
                  bodyContent = rollOutBatchBuffer();
                  batchBuffer.clear();
                  currentBatchSizeBytes = 0;
                  hecPost(bodyContent);
                }
              } else {
                hecPost(bodyContent);
              }

            } catch (Exception e) {
              logger.error("Error writing received data: " + ModularInput.getStackTrace(e));
            }
          }

          /**
           * from Tivo
           *
           * @param message
           * @return
           */
          private String escapeMessageIfNeeded(String message) {
            String trimmedMessage = message.trim();
            if (trimmedMessage.startsWith("{") && trimmedMessage.endsWith("}")) {
              // this is *probably* JSON.
              return trimmedMessage;
            } else if (trimmedMessage.startsWith("\"")
                && trimmedMessage.endsWith("\"")
                && !message.substring(1, message.length() - 1).contains("\"")) {
              // this appears to be a quoted string with no internal
              // quotes
              return trimmedMessage;
            } else {
              // don't know what this thing is, so need to escape all
              // quotes, and
              // then wrap the result in quotes
              return "\"" + message.replace("\"", "\\\"") + "\"";
            }
          }
        };

    if (hecBatchMode) {
      new BatchBufferActivityCheckerThread().start();
    }
    // start listening for data
    eb.registerHandler(eventBusAddress, myHandler);
  }
Ejemplo n.º 22
0
 public static boolean equals(JsonObject json) {
   return json.getString("status").equals("ok");
 }
Ejemplo n.º 23
0
  public void installApp(JsonObject config, final Handler<SockJSSocket> sockHandler) {

    config = setDefaults(config);

    String prefix = config.getString("prefix");

    if (prefix == null || prefix.equals("") || prefix.endsWith("/")) {
      throw new IllegalArgumentException("Invalid prefix: " + prefix);
    }

    // Base handler for app

    rm.getWithRegEx(
        prefix + "\\/?",
        new Handler<HttpServerRequest>() {
          public void handle(HttpServerRequest req) {
            if (log.isTraceEnabled()) log.trace("Returning welcome response");
            req.response.headers().put("Content-Type", "text/plain; charset=UTF-8");
            req.response.end("Welcome to SockJS!\n");
          }
        });

    // Iframe handlers
    String iframeHTML =
        IFRAME_TEMPLATE.replace("{{ sockjs_url }}", config.getString("library_url"));
    Handler<HttpServerRequest> iframeHandler = createIFrameHandler(iframeHTML);

    // Request exactly for iframe.html
    rm.getWithRegEx(prefix + "\\/iframe\\.html", iframeHandler);

    // Versioned
    rm.getWithRegEx(prefix + "\\/iframe-[^\\/]*\\.html", iframeHandler);

    // Chunking test
    rm.postWithRegEx(prefix + "\\/chunking_test", createChunkingTestHandler());
    rm.optionsWithRegEx(
        prefix + "\\/chunking_test",
        BaseTransport.createCORSOptionsHandler(config, "OPTIONS, POST"));

    // Info
    rm.getWithRegEx(prefix + "\\/info", BaseTransport.createInfoHandler(config));
    rm.optionsWithRegEx(
        prefix + "\\/info", BaseTransport.createCORSOptionsHandler(config, "OPTIONS, GET"));

    // Transports

    Set<String> enabledTransports = new HashSet<>();
    enabledTransports.add(Transport.EVENT_SOURCE.toString());
    enabledTransports.add(Transport.HTML_FILE.toString());
    enabledTransports.add(Transport.JSON_P.toString());
    enabledTransports.add(Transport.WEBSOCKET.toString());
    enabledTransports.add(Transport.XHR.toString());
    for (Object tr : config.getArray("disabled_transports", new JsonArray())) {
      enabledTransports.remove(tr);
    }

    if (enabledTransports.contains(Transport.XHR.toString())) {
      new XhrTransport(vertx, rm, prefix, sessions, config, sockHandler);
    }
    if (enabledTransports.contains(Transport.EVENT_SOURCE.toString())) {
      new EventSourceTransport(vertx, rm, prefix, sessions, config, sockHandler);
    }
    if (enabledTransports.contains(Transport.HTML_FILE.toString())) {
      new HtmlFileTransport(vertx, rm, prefix, sessions, config, sockHandler);
    }
    if (enabledTransports.contains(Transport.JSON_P.toString())) {
      new JsonPTransport(vertx, rm, prefix, sessions, config, sockHandler);
    }
    if (enabledTransports.contains(Transport.WEBSOCKET.toString())) {
      new WebSocketTransport(vertx, wsMatcher, rm, prefix, sessions, config, sockHandler);
      new RawWebSocketTransport(vertx, wsMatcher, rm, prefix, sockHandler);
    }
    // Catch all for any other requests on this app

    rm.getWithRegEx(
        prefix + "\\/.+",
        new Handler<HttpServerRequest>() {
          public void handle(HttpServerRequest req) {
            if (log.isTraceEnabled())
              log.trace("Request: " + req.uri + " does not match, returning 404");
            req.response.statusCode = 404;
            req.response.end();
          }
        });
  }
Ejemplo n.º 24
0
 @Override
 public void handleUser(Message<JsonObject> event) {
   Integer id = event.body.getInteger("id");
   JsonObject params = event.body.getObject("op");
   JsonObject state = event.body.getObject("state");
   JsonObject meta = state.getObject("meta");
   JsonObject metaColumn = state.getObject("metaColumn");
   JsonObject metaRanged = state.getObject("metaRanged");
   if (meta == null) {
     meta = new JsonObject();
   }
   if (metaColumn == null) {
     metaColumn = new JsonObject();
   }
   if (metaRanged == null) {
     metaRanged = new JsonObject();
   }
   if (params.getString("type") != null) {
     StringBuilder key = new StringBuilder();
     key.append(HandlerUtils.instance.determineKs(params, state, null));
     key.append(' ');
     key.append(HandlerUtils.instance.determineCf(params, state, null));
     key.append(' ');
     key.append(params.getString("type"));
     meta.putObject(
         key.toString(), new JsonObject().putString("clazz", params.getString("clazz")));
     state.putObject("meta", meta);
     event.reply(new JsonObject().putString(id.toString(), "OK").putObject("state", state));
   } else if (params.getField("name") != null) {
     Object o = params.getField("name");
     ByteBuffer bb = HandlerUtils.instance.byteBufferForObject(o);
     StringBuilder key = new StringBuilder();
     key.append(HandlerUtils.instance.determineKs(params, state, null));
     key.append(' ');
     key.append(HandlerUtils.instance.determineCf(params, state, null));
     key.append(' ');
     key.append(ByteBufferUtil.bytesToHex(bb));
     metaColumn.putObject(
         key.toString(), new JsonObject().putString("clazz", params.getString("clazz")));
     state.putObject("metaColumn", metaColumn);
     event.reply(new JsonObject().putString(id.toString(), "OK").putObject("state", state));
   } else if (params.getField(Operations.RANGE_START) != null) {
     Object start = params.getField(Operations.RANGE_START);
     Object end = params.getField(Operations.RANGE_END);
     ByteBuffer sbuf = HandlerUtils.instance.byteBufferForObject(start);
     ByteBuffer ebuf = HandlerUtils.instance.byteBufferForObject(end);
     StringBuilder key = new StringBuilder();
     key.append(HandlerUtils.instance.determineKs(params, state, null));
     key.append(' ');
     key.append(HandlerUtils.instance.determineCf(params, state, null));
     key.append(' ');
     key.append(ByteBufferUtil.bytesToHex(sbuf));
     JsonObject value = new JsonObject();
     value.putString("clazz", params.getString("clazz"));
     value.putString("end", ByteBufferUtil.bytesToHex(ebuf));
     metaRanged.putObject(key.toString(), value);
     state.putObject("metaRanged", metaRanged);
     event.reply(new JsonObject().putString(id.toString(), "OK").putObject("state", state));
   } else {
     throw new RuntimeException("hit bottom this is bad ok");
   }
 }
Ejemplo n.º 25
0
 protected Structure(JsonObject struct) {
   this(struct.getString("externalId"), struct);
 }