1

The Symbol table is only queried through tooling API. Do we have an option to set setQueryOptions when doing that, or similar way of setting batch size to do that.

Because when running query() and queryMore(), in bigger orgs, the query seems to take a huge amount of time when queried for SymbolTable.

I need to query for SymbolTable in batches so that the result can be streamingly showed.

UPDATE :

public static <T> List<T> queryRecords(String query, PartnerConnection partnerConnection, ToolingConnection toolingConnection, boolean usePartner)
        throws com.sforce.ws.ConnectionException {
    if (usePartner) {
        List<T> sObjectList = new ArrayList<>();
        QueryResult qResult;
        partnerConnection.setQueryOptions(100);
        qResult = partnerConnection.query(query);
        boolean done = false;
        if (qResult.getSize() > 0) {
            System.out.println("Logged-in user can see a total of "
                    + qResult.getSize() + " contact records.");
            while (!done) {
                com.sforce.soap.partner.sobject.SObject[] records = qResult.getRecords();
                for (com.sforce.soap.partner.sobject.SObject record : records) {
                    sObjectList.add((T) record);
                }

                if (qResult.isDone()) {
                    done = true;
                } else {
                    qResult = partnerConnection.queryMore(qResult.getQueryLocator());
                }
            }
        } else {
            System.out.println("No records found.");
        }
        System.out.println("Query successfully executed.");

        return sObjectList;
    } else {
        List<T> sObjectList = new ArrayList<>();
        com.sforce.soap.tooling.QueryResult qResult = toolingConnection.query(query);
        boolean done = false;
        if (qResult.getSize() > 0) {
            System.out.println("Logged-in user can see a total of "
                    + qResult.getSize() + " contact records.");
            while (!done) {
                SObject[] records = qResult.getRecords();
                for (SObject record : records) {
                    sObjectList.add((T) record);
                }
                if (qResult.isDone()) {
                    done = true;
                } else {
                    qResult = toolingConnection.queryMore(qResult.getQueryLocator());
                }
            }
        } else {
            System.out.println("No records found.");
        }
        System.out.println("Query successfully executed.");

        return sObjectList;

    }
}

I am not using any REST call or SOAP, Directly using ToolingAPI jar and PartnerAPI jar. Do i need to change this to use : Sforce-Query-Options as per @sfdcfox

Update 2

Finally wrote this code which works well :

public static Map<String, SymbolTable> generateSymbolTable(String partnerURL, String toolingURL, Cookie[] cookies,
                                                           OutputStream outputStream, Gson gson, HttpServletResponse response) throws IOException, ConnectionException, com.sforce.ws.ConnectionException {


    String accessToken = null;
    String instanceUrlForQuery = null;
    for (Cookie cookie : cookies) {
        if (cookie.getName().equals("ACCESS_TOKEN")) {
            accessToken = cookie.getValue();
        }
        if (cookie.getName().equals("INSTANCE_URL")) {
            String instanceUrl = cookie.getValue();
            instanceUrlForQuery = instanceUrl;
            partnerURL = instanceUrl + partnerURL;
            toolingURL = instanceUrl + toolingURL;
        }
    }

    String path = "/services/data/v41.0/tooling/query/?q=";

    Map<String, SymbolTable> stringSymbolTableMap = new HashMap<>();

    String apexClassBodytooling = "Select+SymbolTable+From+ApexClass";
    HttpClient httpclient=new HttpClient();
    GetMethod getMethod=new GetMethod(instanceUrlForQuery+path+apexClassBodytooling);
    getMethod.setRequestHeader("Authorization", "Bearer " +accessToken);
    getMethod.setRequestHeader("Sforce-Query-Options","batchSize=200");

    httpclient.executeMethod(getMethod);
    if(getMethod.getStatusCode() == HttpStatus.SC_OK ){
        try{
            boolean done = false;
            JSONObject jsonResponse = new JSONObject(new JSONTokener(new InputStreamReader(getMethod.getResponseBodyAsStream())));
            if ((Integer) jsonResponse.get("size") > 0) {
                while (!done) {
                    for (Object records : ((JSONArray) jsonResponse.get("records"))) {
                        ClassStructure classStructure = new ClassStructure();
                        Object symbolTable = ((JSONObject) records).get("SymbolTable");
                        if(!JSONObject.NULL.equals(symbolTable)) {
                            Object methods = ((JSONObject) symbolTable).get("methods");
                            Object className = ((JSONObject) symbolTable).get("name");
                            classStructure.setClassName(className.toString());
                            List<String> methodList = new ArrayList<>();
                            for (Object eachMethod : ((JSONArray) methods)) {
                                Object name = ((JSONObject) eachMethod).get("name");
                                methodList.add(name.toString());

                            }
                            classStructure.setMethodsNames(methodList);
                            outputStream.write(gson.toJson(classStructure).getBytes());
                            outputStream.flush();
                        }
                    }

                    if (jsonResponse.get("done").equals("true")) {
                        done = true;
                    } else {
                        getMethod=new GetMethod(instanceUrlForQuery+jsonResponse.get("nextRecordsUrl").toString());
                        getMethod.setRequestHeader("Authorization", "Bearer " +accessToken);
                        getMethod.setRequestHeader("Sforce-Query-Options","batchSize=200");
                        httpclient.executeMethod(getMethod);
                        jsonResponse = new JSONObject(new JSONTokener(new InputStreamReader(getMethod.getResponseBodyAsStream())));
                    }
                }
            }

        }catch(Exception e){
            e.printStackTrace();
        }
    }

The only issue I see when it tries to query Symbol table it takes almost 25 seconds to return the result for 200 records. Is there a way to even optimise this, like Query only required fields from Symbol Table like the Methods and Properties. etc

0

For now, you can't. There is no QueryOptions header in the Tooling API WSDL or in the documentation. Your choices are either switch to the REST API, or live with the limitations of using the SOAP API.

  • I tried changing to rest api, but before that I was trying to query in workbench, for rest api i wrote : /services/data/v41.0/tooling/query/?q=SELECT+Id,Name,SymbolTable+FROM+APEXCLASS+LIMIT+400 and in Headers : I added: Content-Type: application/json; charset=UTF-8 Accept: application/json Sforce-Query-Options : batchSize=200 Image link But it gives exception as : Got HTML at: /services/data/v41.0/tooling/query/?q=SELECT+Id,Name,SymbolTable+FROM+APEXCLASS+LIMIT+4 – Nagendra K S May 22 '18 at 6:43
  • @RahulJain The HTTP protocol specifies a very specific pattern for headers: <header-name>: <header-value>. The extra space before : causes the server to see the header of Sforce-Query-Options , not Sforce-Query-Options. – sfdcfox May 22 '18 at 6:47
  • I have updated the code in my question, this works well, but is there a way we can optimise it more, I think the batch size cannot be reduced. But is there a way by which we can query for only required attributes from SymbolTable. @sfdcfox – Nagendra K S May 22 '18 at 17:56
  • @RahulJain Nope, that's as compact as you can get, unfortunately. Requesting a value smaller than 200 isn't allowed, and SymbolTable can't be further decomposed in SOQL. – sfdcfox May 22 '18 at 17:58
  • The only problem I see is that as the query takes almost 25 seconds to give result for 200 records, the hosted application on heroku might fail as heroku times out the call if the request is more than 30 seconds. I am building an application with intellisense, but I have no idea how to overcome this edge scenario. – Nagendra K S May 22 '18 at 18:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.