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