001/* 002 * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the Eclipse Public License v1.0 006 * which accompanies this distribution, and is available at 007 * http://www.eclipse.org/legal/epl-v10.html 008 * 009 * Contributors: 010 * bstefanescu 011 * ataillefer 012 */ 013package org.nuxeo.ecm.automation.client.jaxrs.spi; 014 015import static org.nuxeo.ecm.automation.client.Constants.CTYPE_AUTOMATION; 016import static org.nuxeo.ecm.automation.client.Constants.CTYPE_ENTITY; 017 018import java.io.IOException; 019 020import org.nuxeo.ecm.automation.client.AdapterFactory; 021import org.nuxeo.ecm.automation.client.AdapterManager; 022import org.nuxeo.ecm.automation.client.AutomationClient; 023import org.nuxeo.ecm.automation.client.LoginCallback; 024import org.nuxeo.ecm.automation.client.LoginInfo; 025import org.nuxeo.ecm.automation.client.Session; 026import org.nuxeo.ecm.automation.client.TokenCallback; 027import org.nuxeo.ecm.automation.client.jaxrs.spi.auth.BasicAuthInterceptor; 028import org.nuxeo.ecm.automation.client.jaxrs.spi.auth.TokenAuthInterceptor; 029import org.nuxeo.ecm.automation.client.jaxrs.spi.marshallers.PojoMarshaller; 030import org.nuxeo.ecm.automation.client.model.OperationRegistry; 031 032/** 033 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 034 * @author <a href="mailto:ataillefer@nuxeo.com">Antoine Taillefer</a> 035 */ 036public abstract class AbstractAutomationClient implements AutomationClient { 037 038 private static final Object SHARED_REGISTRY_SYNCHRONIZER = new Object(); 039 040 // Use an operation registry shared by the multiple client sessions for 041 // performance reasons 042 private static volatile OperationRegistry sharedRegistry; 043 044 private static long sharedRegistryUpdateTimestamp = 0L; 045 046 private static long sharedRegistryExpirationDelay = 60000L; 047 048 protected final AdapterManager adapterManager = new AdapterManager(); 049 050 protected String url; 051 052 protected volatile OperationRegistry registry; 053 054 protected RequestInterceptor requestInterceptor; 055 056 protected AbstractAutomationClient(String url) { 057 this.url = url.endsWith("/") ? url : url + "/"; 058 } 059 060 /** 061 * Gets access to this request interceptor 062 */ 063 public RequestInterceptor getRequestInterceptor() { 064 return requestInterceptor; 065 } 066 067 /** 068 * Can be used for intercepting requests before they are being sent to the server. 069 */ 070 @Override 071 public void setRequestInterceptor(RequestInterceptor interceptor) { 072 requestInterceptor = interceptor; 073 } 074 075 @Override 076 public String getBaseUrl() { 077 return url; 078 } 079 080 public void setBasicAuth(String username, String password) { 081 setRequestInterceptor(new BasicAuthInterceptor(username, password)); 082 } 083 084 protected OperationRegistry getRegistry() { 085 return registry; 086 } 087 088 @Override 089 public void registerAdapter(AdapterFactory<?> factory) { 090 adapterManager.registerAdapter(factory); 091 } 092 093 @SuppressWarnings("unchecked") 094 @Override 095 public <T> T getAdapter(Session session, Class<T> adapterType) { 096 return adapterManager.getAdapter(session, adapterType); 097 } 098 099 protected OperationRegistry connect(Connector connector) throws IOException { 100 Request req = new Request(Request.GET, url); 101 req.put("Accept", CTYPE_AUTOMATION); 102 // TODO handle authorization failure 103 return (OperationRegistry) connector.execute(req); 104 } 105 106 public synchronized void shutdown() { 107 adapterManager.clear(); 108 url = null; 109 registry = null; 110 } 111 112 public Session getSession() throws IOException { 113 Connector connector = newConnector(); 114 if (requestInterceptor != null) { 115 connector = new ConnectorHandler(connector, requestInterceptor); 116 } 117 if (registry == null) { // not yet connected 118 if (System.currentTimeMillis() - sharedRegistryUpdateTimestamp < sharedRegistryExpirationDelay) { 119 registry = sharedRegistry; 120 } else { 121 synchronized (SHARED_REGISTRY_SYNCHRONIZER) { 122 // duplicate the test to avoid reentrance 123 if (System.currentTimeMillis() - sharedRegistryUpdateTimestamp < sharedRegistryExpirationDelay) { 124 registry = sharedRegistry; 125 } else { 126 // retrieve the registry 127 registry = connect(connector); 128 sharedRegistry = registry; 129 sharedRegistryUpdateTimestamp = System.currentTimeMillis(); 130 } 131 } 132 } 133 } 134 return login(connector); 135 } 136 137 public Session getSession(final String username, final String password) throws IOException { 138 return getSession(new BasicAuthInterceptor(username, password)); 139 } 140 141 public Session getSession(final String token) throws IOException { 142 return getSession(new TokenAuthInterceptor(token)); 143 } 144 145 protected Session getSession(RequestInterceptor interceptor) throws IOException { 146 setRequestInterceptor(interceptor); 147 return getSession(); 148 } 149 150 public Session getSession(TokenCallback cb) throws IOException { 151 String token = cb.getLocalToken(); 152 if (token == null) { 153 token = cb.getRemoteToken(cb.getTokenParams()); 154 cb.saveToken(token); 155 } 156 return getSession(token); 157 } 158 159 @Override 160 public Session getSession(LoginCallback loginCb) { 161 throw new UnsupportedOperationException(); 162 } 163 164 protected Session login(Connector connector) throws IOException { 165 Request request = new Request(Request.POST, url + getRegistry().getPath("login")); 166 request.put("Accept", CTYPE_ENTITY); 167 LoginInfo login = (LoginInfo) connector.execute(request); 168 return createSession(connector, login); 169 } 170 171 protected Session createSession(final Connector connector, final LoginInfo login) { 172 return new DefaultSession(this, connector, login == null ? LoginInfo.ANONYNMOUS : login); 173 } 174 175 public void asyncExec(Runnable runnable) { 176 throw new UnsupportedOperationException("Async execution not supported"); 177 } 178 179 protected abstract Connector newConnector(); 180 181 /** 182 * @since 5.7 183 */ 184 public void setSharedRegistryExpirationDelay(long delay) { 185 sharedRegistryExpirationDelay = delay; 186 } 187 188 @Override 189 public void registerPojoMarshaller(Class clazz) { 190 JsonMarshalling.addMarshaller(PojoMarshaller.forClass(clazz)); 191 } 192}