/*
* (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
*
* 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.
*
* Contributors:
* bstefanescu
* ataillefer
*/
package org.nuxeo.ecm.automation.client.jaxrs.spi;
import static org.nuxeo.ecm.automation.client.Constants.CTYPE_AUTOMATION;
import static org.nuxeo.ecm.automation.client.Constants.CTYPE_ENTITY;
import java.io.IOException;
import org.nuxeo.ecm.automation.client.AdapterFactory;
import org.nuxeo.ecm.automation.client.AdapterManager;
import org.nuxeo.ecm.automation.client.AutomationClient;
import org.nuxeo.ecm.automation.client.LoginCallback;
import org.nuxeo.ecm.automation.client.LoginInfo;
import org.nuxeo.ecm.automation.client.Session;
import org.nuxeo.ecm.automation.client.TokenCallback;
import org.nuxeo.ecm.automation.client.jaxrs.spi.auth.BasicAuthInterceptor;
import org.nuxeo.ecm.automation.client.jaxrs.spi.auth.TokenAuthInterceptor;
import org.nuxeo.ecm.automation.client.jaxrs.spi.marshallers.PojoMarshaller;
import org.nuxeo.ecm.automation.client.model.OperationRegistry;
/**
* @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
* @author <a href="mailto:ataillefer@nuxeo.com">Antoine Taillefer</a>
*/
public abstract class AbstractAutomationClient implements AutomationClient {
private static final Object SHARED_REGISTRY_SYNCHRONIZER = new Object();
// Use an operation registry shared by the multiple client sessions for
// performance reasons
private static volatile OperationRegistry sharedRegistry;
private static long sharedRegistryUpdateTimestamp = 0L;
private static long sharedRegistryExpirationDelay = 60000L;
protected final AdapterManager adapterManager = new AdapterManager();
protected String url;
protected volatile OperationRegistry registry;
protected RequestInterceptor requestInterceptor;
protected AbstractAutomationClient(String url) {
this.url = url.endsWith("/") ? url : url + "/";
}
/**
* Gets access to this request interceptor
*/
public RequestInterceptor getRequestInterceptor() {
return requestInterceptor;
}
/**
* Can be used for intercepting requests before they are being sent to the server.
*/
@Override
public void setRequestInterceptor(RequestInterceptor interceptor) {
requestInterceptor = interceptor;
}
@Override
public String getBaseUrl() {
return url;
}
public void setBasicAuth(String username, String password) {
setRequestInterceptor(new BasicAuthInterceptor(username, password));
}
protected OperationRegistry getRegistry() {
return registry;
}
@Override
public void registerAdapter(AdapterFactory<?> factory) {
adapterManager.registerAdapter(factory);
}
@SuppressWarnings("unchecked")
@Override
public <T> T getAdapter(Session session, Class<T> adapterType) {
return adapterManager.getAdapter(session, adapterType);
}
protected OperationRegistry connect(Connector connector) throws IOException {
Request req = new Request(Request.GET, url);
req.put("Accept", CTYPE_AUTOMATION);
// TODO handle authorization failure
return (OperationRegistry) connector.execute(req);
}
public synchronized void shutdown() {
adapterManager.clear();
url = null;
registry = null;
}
public Session getSession() throws IOException {
Connector connector = newConnector();
if (requestInterceptor != null) {
connector = new ConnectorHandler(connector, requestInterceptor);
}
if (registry == null) { // not yet connected
if (System.currentTimeMillis() - sharedRegistryUpdateTimestamp < sharedRegistryExpirationDelay) {
registry = sharedRegistry;
} else {
synchronized (SHARED_REGISTRY_SYNCHRONIZER) {
// duplicate the test to avoid reentrance
if (System.currentTimeMillis() - sharedRegistryUpdateTimestamp < sharedRegistryExpirationDelay) {
registry = sharedRegistry;
} else {
// retrieve the registry
registry = connect(connector);
sharedRegistry = registry;
sharedRegistryUpdateTimestamp = System.currentTimeMillis();
}
}
}
}
return login(connector);
}
public Session getSession(final String username, final String password) throws IOException {
return getSession(new BasicAuthInterceptor(username, password));
}
public Session getSession(final String token) throws IOException {
return getSession(new TokenAuthInterceptor(token));
}
protected Session getSession(RequestInterceptor interceptor) throws IOException {
setRequestInterceptor(interceptor);
return getSession();
}
public Session getSession(TokenCallback cb) throws IOException {
String token = cb.getLocalToken();
if (token == null) {
token = cb.getRemoteToken(cb.getTokenParams());
cb.saveToken(token);
}
return getSession(token);
}
@Override
public Session getSession(LoginCallback loginCb) {
throw new UnsupportedOperationException();
}
protected Session login(Connector connector) throws IOException {
Request request = new Request(Request.POST, url + getRegistry().getPath("login"));
request.put("Accept", CTYPE_ENTITY);
LoginInfo login = (LoginInfo) connector.execute(request);
return createSession(connector, login);
}
protected Session createSession(final Connector connector, final LoginInfo login) {
return new DefaultSession(this, connector, login == null ? LoginInfo.ANONYNMOUS : login);
}
public void asyncExec(Runnable runnable) {
throw new UnsupportedOperationException("Async execution not supported");
}
protected abstract Connector newConnector();
/**
* @since 5.7
*/
public void setSharedRegistryExpirationDelay(long delay) {
sharedRegistryExpirationDelay = delay;
}
@Override
public void registerPojoMarshaller(Class clazz) {
JsonMarshalling.addMarshaller(PojoMarshaller.forClass(clazz));
}
}