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; 026import java.util.function.Supplier; 027 028import org.nuxeo.ecm.automation.client.AdapterFactory; 029import org.nuxeo.ecm.automation.client.AdapterManager; 030import org.nuxeo.ecm.automation.client.AutomationClient; 031import org.nuxeo.ecm.automation.client.LoginCallback; 032import org.nuxeo.ecm.automation.client.LoginInfo; 033import org.nuxeo.ecm.automation.client.Session; 034import org.nuxeo.ecm.automation.client.TokenCallback; 035import org.nuxeo.ecm.automation.client.jaxrs.spi.auth.BasicAuthInterceptor; 036import org.nuxeo.ecm.automation.client.jaxrs.spi.auth.TokenAuthInterceptor; 037import org.nuxeo.ecm.automation.client.jaxrs.spi.marshallers.PojoMarshaller; 038import org.nuxeo.ecm.automation.client.model.OperationRegistry; 039 040/** 041 * @author <a href="mailto:[email protected]">Bogdan Stefanescu</a> 042 * @author <a href="mailto:[email protected]">Antoine Taillefer</a> 043 */ 044public abstract class AbstractAutomationClient implements AutomationClient { 045 046 private static final Object SHARED_REGISTRY_SYNCHRONIZER = new Object(); 047 048 // Use an operation registry shared by the multiple client sessions for 049 // performance reasons 050 private static volatile OperationRegistry sharedRegistry; 051 052 private static long sharedRegistryUpdateTimestamp = 0L; 053 054 private static long sharedRegistryExpirationDelay = 60000L; 055 056 protected final AdapterManager adapterManager = new AdapterManager(); 057 058 protected String url; 059 060 protected Supplier<String> urlSupplier; 061 062 protected volatile OperationRegistry registry; 063 064 protected RequestInterceptor requestInterceptor; 065 066 protected AbstractAutomationClient(String url) { 067 this.url = url.endsWith("/") ? url : url + "/"; 068 } 069 070 /** @since 10.10 */ 071 protected AbstractAutomationClient(Supplier<String> urlSupplier) { 072 this.urlSupplier = urlSupplier; 073 } 074 075 /** 076 * Gets access to this request interceptor 077 */ 078 public RequestInterceptor getRequestInterceptor() { 079 return requestInterceptor; 080 } 081 082 /** 083 * Can be used for intercepting requests before they are being sent to the server. 084 */ 085 @Override 086 public void setRequestInterceptor(RequestInterceptor interceptor) { 087 requestInterceptor = interceptor; 088 } 089 090 @Override 091 public String getBaseUrl() { 092 return urlSupplier == null ? url : urlSupplier.get(); 093 } 094 095 public void setBasicAuth(String username, String password) { 096 setRequestInterceptor(new BasicAuthInterceptor(username, password)); 097 } 098 099 protected OperationRegistry getRegistry() { 100 return registry; 101 } 102 103 @Override 104 public void registerAdapter(AdapterFactory<?> factory) { 105 adapterManager.registerAdapter(factory); 106 } 107 108 @SuppressWarnings("unchecked") 109 @Override 110 public <T> T getAdapter(Session session, Class<T> adapterType) { 111 return adapterManager.getAdapter(session, adapterType); 112 } 113 114 protected OperationRegistry connect(Connector connector) throws IOException { 115 Request req = new Request(Request.GET, getBaseUrl()); 116 req.put("Accept", CTYPE_AUTOMATION); 117 // TODO handle authorization failure 118 return (OperationRegistry) connector.execute(req); 119 } 120 121 public synchronized void shutdown() { 122 adapterManager.clear(); 123 url = null; 124 urlSupplier = null; 125 registry = null; 126 } 127 128 public Session getSession() throws IOException { 129 Connector connector = newConnector(); 130 if (requestInterceptor != null) { 131 connector = new ConnectorHandler(connector, requestInterceptor); 132 } 133 if (registry == null) { // not yet connected 134 if (System.currentTimeMillis() - sharedRegistryUpdateTimestamp < sharedRegistryExpirationDelay) { 135 registry = sharedRegistry; 136 } else { 137 synchronized (SHARED_REGISTRY_SYNCHRONIZER) { 138 // duplicate the test to avoid reentrance 139 if (System.currentTimeMillis() - sharedRegistryUpdateTimestamp < sharedRegistryExpirationDelay) { 140 registry = sharedRegistry; 141 } else { 142 // retrieve the registry 143 registry = connect(connector); 144 sharedRegistry = registry; 145 sharedRegistryUpdateTimestamp = System.currentTimeMillis(); 146 } 147 } 148 } 149 } 150 return login(connector); 151 } 152 153 public Session getSession(final String username, final String password) throws IOException { 154 return getSession(new BasicAuthInterceptor(username, password)); 155 } 156 157 public Session getSession(final String token) throws IOException { 158 return getSession(new TokenAuthInterceptor(token)); 159 } 160 161 protected Session getSession(RequestInterceptor interceptor) throws IOException { 162 setRequestInterceptor(interceptor); 163 return getSession(); 164 } 165 166 public Session getSession(TokenCallback cb) throws IOException { 167 String token = cb.getLocalToken(); 168 if (token == null) { 169 token = cb.getRemoteToken(cb.getTokenParams()); 170 cb.saveToken(token); 171 } 172 return getSession(token); 173 } 174 175 @Override 176 public Session getSession(LoginCallback loginCb) { 177 throw new UnsupportedOperationException(); 178 } 179 180 protected Session login(Connector connector) throws IOException { 181 Request request = new Request(Request.POST, getBaseUrl() + getRegistry().getPath("login")); 182 request.put("Accept", CTYPE_ENTITY); 183 LoginInfo login = (LoginInfo) connector.execute(request); 184 return createSession(connector, login); 185 } 186 187 protected Session createSession(final Connector connector, final LoginInfo login) { 188 return new DefaultSession(this, connector, login == null ? LoginInfo.ANONYNMOUS : login); 189 } 190 191 public void asyncExec(Runnable runnable) { 192 throw new UnsupportedOperationException("Async execution not supported"); 193 } 194 195 protected abstract Connector newConnector(); 196 197 /** 198 * @since 5.7 199 */ 200 public void setSharedRegistryExpirationDelay(long delay) { 201 sharedRegistryExpirationDelay = delay; 202 } 203 204 @Override 205 public void registerPojoMarshaller(Class clazz) { 206 JsonMarshalling.addMarshaller(PojoMarshaller.forClass(clazz)); 207 } 208}