Example usage for java.util.concurrent CompletableFuture CompletableFuture

List of usage examples for java.util.concurrent CompletableFuture CompletableFuture

Introduction

In this page you can find the example usage for java.util.concurrent CompletableFuture CompletableFuture.

Prototype

public CompletableFuture() 

Source Link

Document

Creates a new incomplete CompletableFuture.

Usage

From source file:com.devicehive.handler.notification.NotificationSubscribeInsertIntegrationTest.java

@Test
public void shouldSubscribeToDeviceNotifications() throws Exception {
    String device1 = randomUUID().toString();
    String device2 = randomUUID().toString();

    String subscriber1 = randomUUID().toString();
    String subscriber2 = randomUUID().toString();
    String subscriber3 = randomUUID().toString();

    NotificationSubscribeRequest sr1 = new NotificationSubscribeRequest(subscriber1, device1, null, null);
    Request r1 = Request.newBuilder().withBody(sr1).withSingleReply(false).build();
    TestCallback c1 = new TestCallback();
    client.call(r1, c1);//ww w.ja  v  a  2s  .c o m

    NotificationSubscribeRequest sr2 = new NotificationSubscribeRequest(subscriber1, device2,
            Collections.singleton("temperature"), null);
    Request r2 = Request.newBuilder().withBody(sr2).withSingleReply(false).build();
    TestCallback c2 = new TestCallback();
    client.call(r2, c2);

    NotificationSubscribeRequest sr3 = new NotificationSubscribeRequest(subscriber2, device2, null, null);
    Request r3 = Request.newBuilder().withBody(sr3).withSingleReply(false).build();
    TestCallback c3 = new TestCallback();
    client.call(r3, c3);

    NotificationSubscribeRequest sr4 = new NotificationSubscribeRequest(subscriber2, device1,
            Collections.singleton("vibration"), null);
    Request r4 = Request.newBuilder().withBody(sr4).withSingleReply(false).build();
    TestCallback c4 = new TestCallback();
    client.call(r4, c4);

    NotificationSubscribeRequest sr5 = new NotificationSubscribeRequest(subscriber3, randomUUID().toString(),
            null, null);
    Request r5 = Request.newBuilder().withBody(sr5).withSingleReply(false).build();
    TestCallback c5 = new TestCallback();
    client.call(r5, c5);

    //wait subsribers to subscribe
    Stream.of(c1.subscribeFuture, c2.subscribeFuture, c3.subscribeFuture, c4.subscribeFuture,
            c5.subscribeFuture).forEach(CompletableFuture::join);

    //devices send notifications
    List<CompletableFuture<Response>> futures = Stream.of(device1, device2).flatMap(device -> {
        List<CompletableFuture<Response>> list = Stream.of("temperature", "vibration").map(name -> {
            DeviceNotification notification = new DeviceNotification();
            notification.setId(0);
            notification.setNotification(name);
            notification.setDeviceGuid(device);
            NotificationInsertRequest event = new NotificationInsertRequest(notification);
            CompletableFuture<Response> f = new CompletableFuture<>();
            client.call(Request.newBuilder().withBody(event).build(), f::complete);
            return f;
        }).collect(Collectors.toList());
        return list.stream();
    }).collect(Collectors.toList());

    //wait notifications delivered
    futures.forEach(CompletableFuture::join);

    assertThat(c1.notifications, hasSize(2));
    c1.notifications.forEach(event -> {
        assertNotNull(event.getNotification());
        assertEquals(event.getNotification().getDeviceGuid(), device1);
        assertEquals(event.getNotification().getId(), Long.valueOf(0));
    });
    Set<String> names = c1.notifications.stream().map(n -> n.getNotification().getNotification())
            .collect(Collectors.toSet());
    assertThat(names, containsInAnyOrder("temperature", "vibration"));

    assertThat(c2.notifications, hasSize(1));
    NotificationEvent e = c2.notifications.stream().findFirst().get();
    assertNotNull(e.getNotification());
    assertEquals(e.getNotification().getDeviceGuid(), device2);
    assertEquals(e.getNotification().getId(), Long.valueOf(0));
    assertEquals(e.getNotification().getNotification(), "temperature");

    assertThat(c3.notifications, hasSize(2));
    c3.notifications.forEach(event -> {
        assertNotNull(event.getNotification());
        assertEquals(event.getNotification().getDeviceGuid(), device2);
        assertEquals(event.getNotification().getId(), Long.valueOf(0));
    });
    names = c3.notifications.stream().map(n -> n.getNotification().getNotification())
            .collect(Collectors.toSet());
    assertThat(names, containsInAnyOrder("temperature", "vibration"));

    assertThat(c4.notifications, hasSize(1));
    e = c4.notifications.stream().findFirst().get();
    assertNotNull(e.getNotification());
    assertEquals(e.getNotification().getDeviceGuid(), device1);
    assertEquals(e.getNotification().getId(), Long.valueOf(0));
    assertEquals(e.getNotification().getNotification(), "vibration");

    assertThat(c5.notifications, is(empty()));
}

From source file:org.terracotta.voter.TCVoterMain.java

protected void startVoter(Optional<Properties> connectionProps, String... hostPorts) {
    new ActiveVoter(ID, new CompletableFuture<>(), connectionProps, hostPorts).start();
}

From source file:io.pravega.controller.server.SegmentHelper.java

public CompletableFuture<Boolean> deleteSegment(final String scope, final String stream,
        final int segmentNumber, final HostControllerStore hostControllerStore,
        final ConnectionFactory clientCF) {
    final CompletableFuture<Boolean> result = new CompletableFuture<>();
    final Controller.NodeUri uri = getSegmentUri(scope, stream, segmentNumber, hostControllerStore);

    final WireCommandType type = WireCommandType.DELETE_SEGMENT;

    final FailingReplyProcessor replyProcessor = new FailingReplyProcessor() {

        @Override/*from   w  w w  . j  a  v  a  2s. co  m*/
        public void connectionDropped() {
            result.completeExceptionally(
                    new WireCommandFailedException(type, WireCommandFailedException.Reason.ConnectionDropped));
        }

        @Override
        public void wrongHost(WireCommands.WrongHost wrongHost) {
            result.completeExceptionally(
                    new WireCommandFailedException(type, WireCommandFailedException.Reason.UnknownHost));
        }

        @Override
        public void noSuchSegment(WireCommands.NoSuchSegment noSuchSegment) {
            result.complete(true);
        }

        @Override
        public void segmentDeleted(WireCommands.SegmentDeleted segmentDeleted) {
            result.complete(true);
        }

        @Override
        public void processingFailure(Exception error) {
            result.completeExceptionally(error);
        }
    };

    WireCommands.DeleteSegment request = new WireCommands.DeleteSegment(idGenerator.get(),
            Segment.getScopedName(scope, stream, segmentNumber));
    sendRequestAsync(request, replyProcessor, result, clientCF, ModelHelper.encode(uri));
    return result;
}

From source file:org.apache.pulsar.compaction.CompactedTopicTest.java

/**
 * Build a compacted ledger, and return the id of the ledger, the position of the different
 * entries in the ledger, and a list of gaps, and the entry which should be returned after the gap.
 *//*from www  .  j a v a2 s.  c o  m*/
private Triple<Long, List<Pair<MessageIdData, Long>>, List<Pair<MessageIdData, Long>>> buildCompactedLedger(
        BookKeeper bk, int count) throws Exception {
    LedgerHandle lh = bk.createLedger(1, 1, Compactor.COMPACTED_TOPIC_LEDGER_DIGEST_TYPE,
            Compactor.COMPACTED_TOPIC_LEDGER_PASSWORD);
    List<Pair<MessageIdData, Long>> positions = new ArrayList<>();
    List<Pair<MessageIdData, Long>> idsInGaps = new ArrayList<>();

    AtomicLong ledgerIds = new AtomicLong(10L);
    AtomicLong entryIds = new AtomicLong(0L);
    CompletableFuture.allOf(IntStream.range(0, count).mapToObj((i) -> {
        List<MessageIdData> idsInGap = new ArrayList<MessageIdData>();
        if (r.nextInt(10) == 1) {
            long delta = r.nextInt(10) + 1;
            idsInGap.add(MessageIdData.newBuilder().setLedgerId(ledgerIds.get()).setEntryId(entryIds.get() + 1)
                    .build());
            ledgerIds.addAndGet(delta);
            entryIds.set(0);
        }
        long delta = r.nextInt(5);
        if (delta != 0) {
            idsInGap.add(MessageIdData.newBuilder().setLedgerId(ledgerIds.get()).setEntryId(entryIds.get() + 1)
                    .build());
        }
        MessageIdData id = MessageIdData.newBuilder().setLedgerId(ledgerIds.get())
                .setEntryId(entryIds.addAndGet(delta + 1)).build();

        @Cleanup
        RawMessage m = new RawMessageImpl(id, Unpooled.EMPTY_BUFFER);

        CompletableFuture<Void> f = new CompletableFuture<>();
        ByteBuf buffer = m.serialize();

        lh.asyncAddEntry(buffer, (rc, ledger, eid, ctx) -> {
            if (rc != BKException.Code.OK) {
                f.completeExceptionally(BKException.create(rc));
            } else {
                positions.add(Pair.of(id, eid));
                idsInGap.forEach((gid) -> idsInGaps.add(Pair.of(gid, eid)));
                f.complete(null);
            }
        }, null);
        return f;
    }).toArray(CompletableFuture[]::new)).get();
    lh.close();

    return Triple.of(lh.getId(), positions, idsInGaps);
}

From source file:com.hurence.logisland.engine.vanilla.stream.amqp.AmqpClientPipelineStream.java

private CompletableFuture<ProtonConnection> setupConnection() {
    CompletableFuture<ProtonConnection> completableFuture = new CompletableFuture<>();
    String hostname = streamContext.getPropertyValue(StreamOptions.CONNECTION_HOST).asString();
    int port = streamContext.getPropertyValue(StreamOptions.CONNECTION_PORT).asInteger();
    int credits = streamContext.getPropertyValue(StreamOptions.LINK_CREDITS).asInteger();

    String user = streamContext.getPropertyValue(StreamOptions.CONNECTION_AUTH_USERNAME).asString();
    String password = streamContext.getPropertyValue(StreamOptions.CONNECTION_AUTH_PASSWORD).asString();
    if (user != null && password != null) {
        options.addEnabledSaslMechanism("PLAIN");
    } else if (streamContext.getPropertyValue(StreamOptions.CONNECTION_AUTH_TLS_CERT).isSet()) {
        String tlsCert = streamContext.getPropertyValue(StreamOptions.CONNECTION_AUTH_TLS_CERT).asString();
        String tlsKey = streamContext.getPropertyValue(StreamOptions.CONNECTION_AUTH_TLS_KEY).asString();
        String caCert = streamContext.getPropertyValue(StreamOptions.CONNECTION_AUTH_CA_CERT).asString();
        options.addEnabledSaslMechanism("EXTERNAL").setHostnameVerificationAlgorithm("")
                .setPemKeyCertOptions(new PemKeyCertOptions().addCertPath(new File(tlsCert).getAbsolutePath())
                        .addKeyPath(new File(tlsKey).getAbsolutePath()));
        if (caCert != null) {
            options.setPemTrustOptions(new PemTrustOptions().addCertPath(new File(caCert).getAbsolutePath()));
        }/*from  www. ja  va 2  s.com*/

    }
    protonClient.connect(options, hostname, port, user, password, event -> {
        if (event.failed()) {
            handleConnectionFailure(false);
            completableFuture.completeExceptionally(event.cause());
            return;
        }
        connectionControl.connected();
        completableFuture.complete(event.result());
        protonConnection = event.result();
        String containerId = streamContext.getPropertyValue(StreamOptions.CONTAINER_ID).asString();
        if (containerId != null) {
            protonConnection.setContainer(containerId);
        }
        protonConnection.closeHandler(x -> {
            handleConnectionFailure(true);
        }).disconnectHandler(x -> {
            handleConnectionFailure(false);
        }).openHandler(onOpen -> {

            //setup the output path
            sender = protonConnection
                    .createSender(streamContext.getPropertyValue(StreamOptions.WRITE_TOPIC).asString());
            sender.setAutoDrained(true);
            sender.setAutoSettle(true);
            sender.open();

            //setup the input path
            receiver = protonConnection
                    .createReceiver(streamContext.getPropertyValue(StreamOptions.READ_TOPIC).asString());
            receiver.setPrefetch(credits);
            receiver.handler((delivery, message) -> {
                try {
                    Record record;
                    if (deserializer == null) {
                        record = RecordUtils.getKeyValueRecord(
                                StringUtils.defaultIfEmpty(message.getSubject(), ""),
                                new String(extractBodyContent(message.getBody())));
                    } else {
                        record = deserializer
                                .deserialize(new ByteArrayInputStream(extractBodyContent(message.getBody())));
                        if (!record.hasField(FieldDictionary.RECORD_KEY)) {
                            record.setField(FieldDictionary.RECORD_KEY, FieldType.STRING, message.getSubject());
                        }
                    }

                    Collection<Record> r = Collections.singleton(record);
                    for (ProcessContext processContext : streamContext.getProcessContexts()) {
                        r = processContext.getProcessor().process(processContext, r);
                    }
                    List<Message> toAdd = new ArrayList<>();
                    for (Record out : r) {
                        ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
                        serializer.serialize(byteOutputStream, out);
                        Message mo = ProtonHelper.message();
                        if (out.hasField(FieldDictionary.RECORD_KEY)) {
                            mo.setSubject(out.getField(FieldDictionary.RECORD_KEY).asString());
                        }
                        if (StringUtils.isNotBlank(contentType)) {
                            mo.setContentType(contentType);
                        }
                        mo.setMessageId(out.getId());
                        mo.setBody(new Data(Binary.create(ByteBuffer.wrap(byteOutputStream.toByteArray()))));
                        toAdd.add(mo);
                    }
                    toAdd.forEach(sender::send);
                    delivery.disposition(Accepted.getInstance(), true);
                } catch (Exception e) {
                    Rejected rejected = new Rejected();
                    delivery.disposition(rejected, true);
                    getLogger().warn("Unable to process message : " + e.getMessage());
                }
            }).open();

        }).open();

    });
    return completableFuture;
}

From source file:org.apache.pulsar.broker.authorization.PulsarAuthorizationProvider.java

/**
 * Check if the specified role has permission to receive messages from the specified fully qualified topic
 * name./*from  w w w . jav  a  2 s . c  o  m*/
 *
 * @param topicName
 *            the fully qualified topic name associated with the topic.
 * @param role
 *            the app id used to receive messages from the topic.
 * @param subscription
 *            the subscription name defined by the client
 */
@Override
public CompletableFuture<Boolean> canConsumeAsync(TopicName topicName, String role,
        AuthenticationDataSource authenticationData, String subscription) {
    CompletableFuture<Boolean> permissionFuture = new CompletableFuture<>();
    try {
        configCache.policiesCache().getAsync(POLICY_ROOT + topicName.getNamespace()).thenAccept(policies -> {
            if (!policies.isPresent()) {
                if (log.isDebugEnabled()) {
                    log.debug("Policies node couldn't be found for topic : {}", topicName);
                }
            } else {
                if (isNotBlank(subscription)) {
                    // validate if role is authorize to access subscription. (skip validatation if authorization
                    // list is empty)
                    Set<String> roles = policies.get().auth_policies.subscription_auth_roles.get(subscription);
                    if (roles != null && !roles.isEmpty() && !roles.contains(role)) {
                        log.warn("[{}] is not authorized to subscribe on {}-{}", role, topicName, subscription);
                        PulsarServerException ex = new PulsarServerException(
                                String.format("%s is not authorized to access subscription %s on topic %s",
                                        role, subscription, topicName));
                        permissionFuture.complete(false);
                        return;
                    }

                    // validate if subscription-auth mode is configured
                    switch (policies.get().subscription_auth_mode) {
                    case Prefix:
                        if (!subscription.startsWith(role)) {
                            PulsarServerException ex = new PulsarServerException(String.format(
                                    "Failed to create consumer - The subscription name needs to be prefixed by the authentication role, like %s-xxxx for topic: %s",
                                    role, topicName));
                            permissionFuture.completeExceptionally(ex);
                            return;
                        }
                        break;
                    default:
                        break;
                    }
                }
            }
            // check namespace and topic level consume-permissions
            checkAuthorization(topicName, role, AuthAction.consume).thenAccept(isAuthorized -> {
                permissionFuture.complete(isAuthorized);
            });
        }).exceptionally(ex -> {
            log.warn("Client with Role - {} failed to get permissions for topic - {}. {}", role, topicName,
                    ex.getMessage());
            permissionFuture.completeExceptionally(ex);
            return null;
        });
    } catch (Exception e) {
        log.warn("Client  with Role - {} failed to get permissions for topic - {}. {}", role, topicName,
                e.getMessage());
        permissionFuture.completeExceptionally(e);
    }
    return permissionFuture;
}

From source file:com.canoo.dolphin.client.impl.ClientContextImpl.java

@Override
public synchronized CompletableFuture<Void> disconnect() {
    checkForInitializedState();//from  w ww  .j a v a 2s .  c  o  m
    state = State.DESTROYING;
    clientDolphin.stopPushListening();
    final CompletableFuture<Void> result = new CompletableFuture<>();

    Executors.newSingleThreadExecutor().execute(() -> {
        state = State.DESTROYED;
        dolphinCommandHandler.invokeDolphinCommand(PlatformConstants.DESTROY_CONTEXT_COMMAND_NAME)
                .handle((v, t) -> {
                    if (t != null) {
                        result.completeExceptionally(new DolphinRemotingException("Can't disconnect", t));
                    } else {
                        result.complete(null);
                    }
                    return null;
                });
        //TODO: Stop communication in client connector
    });

    return result;
}

From source file:org.apache.pulsar.client.impl.HttpLookupService.java

@Override
public CompletableFuture<List<String>> getTopicsUnderNamespace(NamespaceName namespace, Mode mode) {
    CompletableFuture<List<String>> future = new CompletableFuture<>();

    String format = namespace.isV2() ? "admin/v2/namespaces/%s/topics?mode=%s"
            : "admin/namespaces/%s/destinations?mode=%s";
    httpClient.get(String.format(format, namespace, mode.toString()), String[].class).thenAccept(topics -> {
        List<String> result = Lists.newArrayList();
        // do not keep partition part of topic name
        Arrays.asList(topics).forEach(topic -> {
            String filtered = TopicName.get(topic).getPartitionedTopicName();
            if (!result.contains(filtered)) {
                result.add(filtered);/*from w ww.  j av a  2 s .c o m*/
            }
        });
        future.complete(result);
    }).exceptionally(ex -> {
        log.warn("Failed to getTopicsUnderNamespace namespace: {}.", namespace, ex.getMessage());
        future.completeExceptionally(ex);
        return null;
    });
    return future;
}

From source file:org.apache.bookkeeper.meta.MockLedgerManager.java

@Override
public CompletableFuture<Versioned<LedgerMetadata>> readLedgerMetadata(long ledgerId) {
    CompletableFuture<Versioned<LedgerMetadata>> promise = new CompletableFuture<>();
    executor.submit(() -> {/*  www  .  java 2  s.c o  m*/
        try {
            Versioned<LedgerMetadata> metadata = readMetadata(ledgerId);
            if (metadata == null) {
                executeCallback(
                        () -> promise.completeExceptionally(new BKException.BKNoSuchLedgerExistsException()));
            } else {
                executeCallback(() -> promise.complete(metadata));
            }
        } catch (Exception e) {
            LOG.error("Error reading metadata", e);
            executeCallback(() -> promise.completeExceptionally(new BKException.MetaStoreException()));
        }
    });
    return promise;
}

From source file:org.jaqpot.core.service.client.jpdi.JPDIClientImpl.java

@Override
public Future<Model> train(Dataset dataset, Algorithm algorithm, Map<String, Object> parameters,
        String predictionFeature, MetaInfo modelMeta, String taskId) {

    CompletableFuture<Model> futureModel = new CompletableFuture<>();

    TrainingRequest trainingRequest = new TrainingRequest();
    trainingRequest.setDataset(dataset);
    trainingRequest.setParameters(parameters);
    trainingRequest.setPredictionFeature(predictionFeature);
    //        String trainingRequestString = serializer.write(trainingRequest);

    final HttpPost request = new HttpPost(algorithm.getTrainingService());

    PipedOutputStream out = new PipedOutputStream();
    PipedInputStream in;//from  www . j a  v  a  2s  .c  o m
    try {
        in = new PipedInputStream(out);
    } catch (IOException ex) {
        futureModel.completeExceptionally(ex);
        return futureModel;
    }
    InputStreamEntity entity = new InputStreamEntity(in, ContentType.APPLICATION_JSON);
    entity.setChunked(true);

    request.setEntity(entity);
    request.addHeader("Accept", "application/json");

    Future futureResponse = client.execute(request, new FutureCallback<HttpResponse>() {

        @Override
        public void completed(final HttpResponse response) {
            futureMap.remove(taskId);
            int status = response.getStatusLine().getStatusCode();
            try {
                InputStream responseStream = response.getEntity().getContent();

                switch (status) {
                case 200:
                case 201:
                    TrainingResponse trainingResponse = serializer.parse(responseStream,
                            TrainingResponse.class);
                    Model model = new Model();
                    model.setId(randomStringGenerator.nextString(20));
                    model.setActualModel(trainingResponse.getRawModel());
                    model.setPmmlModel(trainingResponse.getPmmlModel());
                    model.setAdditionalInfo(trainingResponse.getAdditionalInfo());
                    model.setAlgorithm(algorithm);
                    model.setParameters(parameters);
                    model.setDatasetUri(dataset != null ? dataset.getDatasetURI() : null);

                    //Check if independedFeatures of model exist in dataset
                    List<String> filteredIndependedFeatures = new ArrayList<String>();

                    if (dataset != null && dataset.getFeatures() != null
                            && trainingResponse.getIndependentFeatures() != null)
                        for (String feature : trainingResponse.getIndependentFeatures()) {
                            for (FeatureInfo featureInfo : dataset.getFeatures()) {
                                if (feature.equals(featureInfo.getURI()))
                                    filteredIndependedFeatures.add(feature);
                            }
                        }

                    model.setIndependentFeatures(filteredIndependedFeatures);
                    model.setDependentFeatures(Arrays.asList(predictionFeature));
                    model.setMeta(modelMeta);

                    List<String> predictedFeatures = new ArrayList<>();
                    for (String featureTitle : trainingResponse.getPredictedFeatures()) {
                        Feature predictionFeatureResource = featureHandler.findByTitleAndSource(featureTitle,
                                "algorithm/" + algorithm.getId());
                        if (predictionFeatureResource == null) {
                            // Create the prediction features (POST /feature)
                            String predFeatID = randomStringGenerator.nextString(12);
                            predictionFeatureResource = new Feature();
                            predictionFeatureResource.setId(predFeatID);
                            predictionFeatureResource.setPredictorFor(predictionFeature);
                            predictionFeatureResource.setMeta(MetaInfoBuilder.builder()
                                    .addSources(
                                            /*messageBody.get("base_uri") + */"algorithm/" + algorithm.getId())
                                    .addComments("Feature created to hold predictions by algorithm with ID "
                                            + algorithm.getId())
                                    .addTitles(featureTitle).addSeeAlso(predictionFeature)
                                    .addCreators(algorithm.getMeta().getCreators()).build());
                            /* Create feature */
                            featureHandler.create(predictionFeatureResource);
                        }
                        predictedFeatures.add(baseURI + "feature/" + predictionFeatureResource.getId());
                    }
                    model.setPredictedFeatures(predictedFeatures);
                    futureModel.complete(model);
                    break;
                case 400:
                    String message = new BufferedReader(new InputStreamReader(responseStream)).lines()
                            .collect(Collectors.joining("\n"));
                    futureModel.completeExceptionally(new BadRequestException(message));
                    break;
                case 500:
                    message = new BufferedReader(new InputStreamReader(responseStream)).lines()
                            .collect(Collectors.joining("\n"));
                    futureModel.completeExceptionally(new InternalServerErrorException(message));
                    break;
                default:
                    message = new BufferedReader(new InputStreamReader(responseStream)).lines()
                            .collect(Collectors.joining("\n"));
                    futureModel.completeExceptionally(new InternalServerErrorException(message));
                }
            } catch (IOException | UnsupportedOperationException ex) {
                futureModel.completeExceptionally(ex);
            }
        }

        @Override
        public void failed(final Exception ex) {
            futureMap.remove(taskId);
            futureModel.completeExceptionally(ex);
        }

        @Override
        public void cancelled() {
            futureMap.remove(taskId);
            futureModel.cancel(true);
        }

    });

    serializer.write(trainingRequest, out);
    try {
        out.close();
    } catch (IOException ex) {
        futureModel.completeExceptionally(ex);
    }

    futureMap.put(taskId, futureResponse);
    return futureModel;
}