/*
* Copyright © 2013. Palomino Labs (http://palominolabs.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.palominolabs.crm.sf.soap;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import com.palominolabs.crm.sf.soap.jaxwsstub.apex.ApexPortType;
import com.palominolabs.crm.sf.soap.jaxwsstub.apex.ExecuteAnonymousResult;
import com.sun.xml.ws.api.message.Header;
import com.sun.xml.ws.api.message.HeaderList;
import com.sun.xml.ws.developer.JAXWSProperties;
import com.sun.xml.ws.developer.WSBindingProvider;
import javax.annotation.Nonnull;
import javax.annotation.concurrent.ThreadSafe;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.ws.WebServiceException;
/**
* Lightweight wrapper around the binding for the Apex WSDL.
*/
@ThreadSafe
final class ApexConnectionImpl extends AbstractSalesforceConnection implements ApexConnection {
private final MetricRegistry metricRegistry;
ApexConnectionImpl(@Nonnull CallSemaphore semaphore, @Nonnull ConnectionBundleImpl bundle,
MetricRegistry metricRegistry) {
super(semaphore, bundle);
this.metricRegistry = metricRegistry;
}
@Override
public synchronized ExecuteAnonResult executeAnonymous(String code) throws ApiException {
return new ExecuteAnonymousOp().execute(code);
}
private abstract class ApexOperation<Tin, Tout> extends ApiOperation<Tin, Tout, ApexPortType> {
private final Timer timer = metricRegistry.timer(MetricRegistry.name(getClass(), "request"));
@Nonnull
@Override
Tout executeImpl(@Nonnull ConfiguredBinding<ApexPortType> apexPortTypeConfiguredBinding, @Nonnull Tin param)
throws ApiException {
Timer.Context context = timer.time();
try {
ApexConnectionImpl.this.acquireSemaphore();
try {
return executeOp(apexPortTypeConfiguredBinding.getBinding(), param);
} finally {
ApexConnectionImpl.this.releaseSemaphore();
}
} catch (WebServiceException e) {
throw getNewExceptionWithCause("Call failed", e);
} finally {
context.stop();
}
}
@Nonnull
abstract Tout executeOp(@Nonnull ApexPortType binding, @Nonnull Tin param) throws ApiException;
@Nonnull
@Override
ConfiguredBinding<ApexPortType> getBinding() throws ApiException {
return connBundle.getApexBinding();
}
@Override
void releaseBinding(@Nonnull ApexPortType binding) {
connBundle.acceptReleasedApexBinding(binding);
}
@Nonnull
ApiException getNewExceptionWithCause(@Nonnull String message, @Nonnull Throwable cause) {
// only called by synchronized methods, so don't need synchronization here
return ApiException.getNewWithCause(message, ApexConnectionImpl.this.getUsername(), cause);
}
}
private class ExecuteAnonymousOp extends ApexOperation<String, ExecuteAnonResult> {
@Nonnull
@Override
ExecuteAnonResult executeOp(@Nonnull ApexPortType binding, @Nonnull String param) throws ApiException {
ExecuteAnonymousResult result = binding.executeAnonymous(param);
HeaderList headers = (HeaderList) ((WSBindingProvider) binding).getResponseContext()
.get(JAXWSProperties.INBOUND_HEADER_LIST_PROPERTY);
String debugLog = null;
for (Header header : headers) {
if (!"DebuggingInfo".equals(header.getLocalPart())) {
continue;
}
try {
final XMLStreamReader reader = header.readHeader();
try {
if (!reader.hasNext()) {
throw ApiException.getNew("Unexpected debug log format", getUsername());
}
int eventType = reader.next();
if (XMLStreamConstants.START_ELEMENT != eventType ||
!"DebuggingInfo".equals(reader.getLocalName())) {
throw ApiException.getNew("Unexpected debug log format", getUsername());
}
if (!reader.hasNext()) {
throw ApiException.getNew("Unexpected debug log format", getUsername());
}
eventType = reader.next();
if (XMLStreamConstants.START_ELEMENT != eventType ||
!"debugLog".equals(reader.getLocalName())) {
throw ApiException.getNew("Unexpected debug log format", getUsername());
}
if (!reader.hasNext()) {
throw ApiException.getNew("Unexpected debug log format", getUsername());
}
eventType = reader.next();
if (eventType == XMLStreamConstants.END_ELEMENT) {
debugLog = "";
} else {
if (XMLStreamConstants.CHARACTERS != eventType ||
!"debugLog".equals(reader.getLocalName())) {
throw ApiException.getNew("Unexpected debug log format", getUsername());
}
StringBuilder sb = new StringBuilder();
do {
sb.append(reader.getText());
} while (reader.hasNext() && reader.next() == XMLStreamConstants.CHARACTERS);
debugLog = sb.toString();
}
} finally {
reader.close();
}
} catch (XMLStreamException e) {
throw getNewExceptionWithCause("Error extracting header", e);
}
}
if (debugLog == null) {
throw ApiException.getNew("No debug log header", getUsername());
}
return new ExecuteAnonResult(result, debugLog);
}
}
}