As Adam points out, and the Tooling API docs, you need to use a MetadataContainer. From the Tooling API Developer's Guide (pg.12)
To edit, save, or compile Apex classes, use ApexClassMember
Here is the reason: Apex must be compiled to ensure that all syntax and references are valid. For example, if I patch an ApexClass's name field, another class that uses that field (MyClass.MyStaticMethod()) is on longer valid. Salesforce could compile for you behind the scenes, but this poses several problems.
- Salesforce would then be doing more (much more) than the intended field patch, reducing the visibility into what is actually going on.
- A compile may take a long time. It wouldn't be good for the patch to take 5 minutes to return.
That is where the ContainerAsyncRequest comes in. It is explicitly saying, "I want to compile and save these changes to my org, and it may take a little while."
I agree the guide could provide more information on this point, and the describe metadata is misleading.
Creation is a little different though. A class will (most often) not contain complex references when it is created and (more importantly) no other classes will be referencing it. Also, an ApexClassMember must have an existing ApexClass to reference, so we must create it though the actual ApexClass
In terms of creating a trigger, it is the same as creating a class except that you need the TableEnumOrId. The table enum is the name of the sObject you want to trigger on. For example:
A post to the following resource with the following data will create an Apex Trigger.
https://<instance>/services/data/v29.0/tooling/sobjects/ApexTrigger
Body: "trigger aa on Account (before insert) {}"
TableEnumOrId: "Account"
I'm not sure why you were getting the invalid object or...
error. I was able to create a trigger on Lead by replacing Account in the TableEnumOrId field AND in the body.
EDIT:
I just tried again in Summer'13 and got the same error message. The reason why you are getting that error is because Lead (or Account) is not accessible in the Tooling API. In other words, when a trigger is created via the Tooling API it tries to reference the object that it is triggering on. When this happens, it throws that error because the object is no accessible in the Tooling API. This has been fixed in Winter'14.
When a login is done via https://login.salesforce.com, one of the return values of the login is the instance that the username corresponds to such as https://na15.salesforce.com (as part of the overall returned URL). In terms of the Java API you are using, that means that setAuthEndpoint
can use the fixed login URL but setServiceEndpoint
has to be given a value returned from the login process.
Some service endpoints are directly available from the LoginResult
, but the Force.com IDE obtains the tooling one by replacing the "u" with a "T" in a rather fragile looking way here.
So your code might look something like this:
ConnectorConfig partnerConfig = new ConnectorConfig();
partnerConfig.setManualLogin(true);
PartnerConnection partnerConnection = com.sforce.soap.partner.Connector.newConnection(
partnerConfig);
LoginResult lr = partnerConnection.login(
props.getProperty("force.user"),
props.getProperty("force.password") + props.getProperty("force.token")
);
ConnectorConfig toolingConfig = new ConnectorConfig();
toolingConfig.setSessionId(lr.getSessionId());
toolingConfig.setServiceEndpoint(lr.getServerUrl().replace('u', 'T'));
SoapConnection toolingConnection = com.sforce.soap.tooling.Connector.newConnection(
toolingConfig);
Best Answer
For Sandbox and Dev Orgs the answer is Yes, using both SOAP and REST flavours of Tooling API.
Example here is written in scala, but in java it would not be much different. Both use JVM any way. The create() class bit is lines 66-74.
Whether it is a good idea or not (to use Tooling over Metadata API) depends on your use case. Tooling API has some limitations (as well as advantages) compared to Metadata API and may not be suitable in some cases.
SFDC SOAP (and REST) web services do not care which languages their clients use.
Example of create() call on this page of current version (v32) of Tooling API documentation claims to be in Java, but in reality it looks suspiciously like .NET. Either way in Java this example would look very much the same.