简体   繁体   中英

How to listen to async server side Java Controller?

Googling this I came across numerous examples, one of which was $q from Angular, to avoid recursively hitting my server side to check if request is complete with data or not.

I came across this link : Recursive $http.get in for loop

Which suggested to use $q, but I dont think that will resolve my issue.

JS:

function OrderFormController($scope, $http, $q) {

    $('#loaderImage').show();

    $http.get('/utilities/longProcess')
        .success(function(data, status, headers, config) {
            if(data != 'Still Processing'){
                console.log('Data from server');
                $scope.sampleJSON = data.pmdStructureWrapper;
                $scope.sampleJSONDuplicates = data.pmdDuplicates;
                $scope.$watch('sampleJSON', setTimeout(function() {
                    $('.panel-body li').each(function() {
                        if ($.trim($(this).text()) === "") {
                            $(this).hide();
                        }
                    });
                }, 1000));
            $('#loaderImage').hide();
            }else{
                console.log('rejected');
                $q.reject(data);
            }
        })
        .error(function(data, status, header, config) {

        });
}

Controller:

@RequestMapping("/utilities/longProcess")
public CompletableFuture<String> asyncLongProcess(HttpServletResponse response, HttpServletRequest request) {
    HttpSession session = request.getSession();
    return CompletableFuture.supplyAsync(() -> session.getAttribute("CACHED_RESULT"))
            .thenComposeAsync(obj -> {
                if (obj == null) {
                    if(session.getAttribute("BACKGROUND_PROCESSING") == null) {
                        session.setAttribute("BACKGROUND_PROCESSING", true);
                        CompletableFuture.supplyAsync(() -> callURL(response, request))
                                .thenAccept(result -> session.setAttribute("CACHED_RESULT", result));
                    }
                    return CompletableFuture.completedFuture("Still Processing");
                }

                return CompletableFuture.completedFuture(obj.toString());
            });
}

HTML :

<body ng-app ng-controller="OrderFormController">
<header>
    <div class="container">
        <div id="branding">
            <h1><span class="highlight">Review  </span> Result</h1>
            <form class="form-logout" role="form" action="/logout">
                <input type="submit" value="Logout" id="logout" class="btn btn-primary btn-lg pull-right">
            </form>
        </div>

    </div>
</header>

<img src="../img/spinner.gif" id="loaderImage" style='display:none'>
<div class="col-md-12 col-lg-12" ng-cloak="">
    <div class="panel with-nav-tabs panel-default">
        <div class="panel-heading">
            <ul class="nav nav-tabs">
                <li class="active"><a href="#tab1default" data-toggle="tab">Classes</a></li>
                <li><a href="#tab2default" data-toggle="tab">Triggers</a></li>
                <li><a href="#tab3default" data-toggle="tab">Visualforce pages</a></li>
                <li><a href="#tab4default" data-toggle="tab">Duplicate Codes</a></li>
            </ul>
        </div>
        <div class="panel-body">
            <div class="tab-content">
                <div class="tab-pane fade in active" id="tab1default">
                    <ul class="col-md-12 col-lg-12">
                        <li ng-click="showErrorDetails(key)" class="col-sm-12 col-md-4 col-lg-4 eachClassCell"
                            ng-repeat='(key,value) in sampleJSON'>
                            <div ng-if="key.indexOf('.cls') > -1">
                                <div title="{{key}}" class="classNameLabel">{{key}}</div>
                                <div title="Error count" class="errorContainer">
                                    <span class="errorCount">{{value.pmdStructures.length}}</span>
                                    <span class="errorMeter"
                                          ng-repeat="eachClass in value.pmdStructures | limitTo: 10"></span>
                                </div>
                            </div>
                        </li>
                    </ul>
                </div>
                <div class="tab-pane fade" id="tab2default">
                    <ul class="col-md-12 col-lg-12">
                        <li ng-click="showErrorDetails(key)" class="col-sm-12 col-md-4 col-lg-4 eachClassCell"
                            ng-repeat='(key,value) in sampleJSON'>
                            <div ng-if="key.indexOf('.trigger') > -1">
                                <div title="{{key}}" class="classNameLabel">{{key}}</div>
                                <div title="Error count" class="errorContainer">
                                    <span class="errorCount">{{value.pmdStructures.length}}</span>
                                    <span class="errorMeter"
                                          ng-repeat="eachClass in value.pmdStructures | limitTo: 10"></span>
                                </div>
                            </div>
                        </li>
                    </ul>
                </div>
                <div class="tab-pane fade" id="tab3default">
                    <ul class="col-md-12 col-lg-12">
                        <li ng-click="showErrorDetails(key)" class="col-sm-12 col-md-4 col-lg-4 eachClassCell"
                            ng-repeat='(key,value) in sampleJSON'>
                            <div ng-if="key.indexOf('.page') > -1">
                                <div title="{{key}}" class="classNameLabel">{{key}}</div>
                                <div title="Error count" class="errorContainer">
                                    <span class="errorCount">{{value.pmdStructures.length}}</span>
                                    <span class="errorMeter"
                                          ng-repeat="eachClass in value.pmdStructures | limitTo: 10"></span>
                                </div>
                            </div>
                        </li>
                    </ul>
                </div>
                <div class="tab-pane fade" id="tab4default">
                    <ul class="col-md-12 col-lg-12">
                        <li ng-click="showDuplicateDetails(eachValue)" class="col-sm-12 col-md-4 col-lg-4 eachClassCell"
                            ng-repeat='eachValue in sampleJSONDuplicates'>
                            <div title="{{eachValue.duplicationInFile}}" class="classNameLabel">
                                {{eachValue.duplicationInFile}}
                            </div>
                        </li>
                    </ul>
                </div>
            </div>
        </div>
    </div>
</div>
<form>


</form>
<footer>
    <p>Salesforce Free Code Review, Copyright &copy; 2017</p>
    </p>
</footer>
<script type="application/javascript">


</script>
</body>

What this does is to avoid timeout, it sends a Still Processing message to the client, as the timeout limit is 30000ms, I know the first thing that comes to mind is do a lazy loading with some 30 data per request, but believe me I cant.

I have built a code review tool, so when the URL gets hit, it runs a code review on all the classes with a background thread and then returns the response in CompletableFuture after some 120 - 150 seconds. This response is unknown to the client until the page is refreshed again.

Even after refreshing the page, as the data is over 7MB the page takes almost 13-35 seconds to display the result.

The code works fine in my local, but when I hosted in heroku, which is shared server, I started getting timeouts. There are two issues :

1) How can angularJS detect changes in response of the RestAPI ?(As there will be two responses sent, first the immediate on "Still Processing" and second the real result. This is one which I have to consume).

2) How to lazy load the data which is already in JSON format from the Controller?

在此处输入图片说明

Update 1:

I tried using OutputStream too:

Controller :

@RequestMapping("/utilities/longProcessStream")
public StreamingResponseBody asyncLongProcessStream(HttpServletResponse response, HttpServletRequest request) {
    return new StreamingResponseBody() {
        @Override
        public void writeTo(OutputStream outputStream) throws IOException {
            PMDController.this.callURL(response, request, outputStream);
        }
    };
}

But now the response changes to XML and the JS console.log('Data from server'); line is not printed untill full response is gathered from the server side.

When the response changes to XML how can I parse that to JSON format as my full JS depends on JSON?

After streaming why angular displaying result after full request is completed?

I found a library which reads StreamingBodyResponse, they internally change blob type to JSON which is exactly what i needed :

@RequestMapping("/utilities/longProcessStream")
public StreamingResponseBody asyncLongProcessStream(HttpServletResponse response, HttpServletRequest request) {
    response.addHeader("Content-Type", MediaType.APPLICATION_JSON);
    return new StreamingResponseBody() {
        @Override
        public void writeTo(OutputStream outputStream) throws IOException {
            PMDController.this.callURL(response, request, outputStream);
        }
    };
}

JS:

$('#loaderImage').show();

$scope.sampleJSONClass = {};
$scope.sampleJSONTrigger = {};
$scope.sampleJSONPages = {};
oboe('/utilities/longProcessStream')
    .done(function(data) {
        var dataFromServer = data.pmdStructureWrapper;
        if (Object.keys(dataFromServer)[0].endsWith('.cls')) {
            $scope.sampleJSONClass[Object.keys(dataFromServer)[0]] = Object.values(dataFromServer)[0];
        }
        if (Object.keys(dataFromServer)[0].endsWith('.trigger')) {
            $scope.sampleJSONTrigger[Object.keys(dataFromServer)[0]] = Object.values(dataFromServer)[0];
        }
        if (Object.keys(dataFromServer)[0].endsWith('.page')) {
            $scope.sampleJSONPages[Object.keys(dataFromServer)[0]] = Object.values(dataFromServer)[0];
        }
        $scope.$apply();
        $('#loaderImage').hide();
    })
    .fail(function() {
        console.log('error');
    });

This works perfectly.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM