/* * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Florent Guillaume */ package org.eclipse.ecr.core.storage.sql.net; import java.io.ObjectInputStream; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import javax.transaction.xa.Xid; import org.apache.commons.httpclient.Header; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.ProtocolException; import org.apache.commons.httpclient.methods.PostMethod; import org.eclipse.ecr.core.storage.Credentials; import org.eclipse.ecr.core.storage.StorageException; import org.eclipse.ecr.core.storage.sql.Mapper; import org.eclipse.ecr.core.storage.sql.RepositoryDescriptor; import org.eclipse.ecr.core.storage.sql.RepositoryImpl; import org.eclipse.ecr.core.storage.sql.Mapper.Identification; import org.eclipse.ecr.core.storage.sql.RepositoryDescriptor.ServerDescriptor; import org.nuxeo.common.utils.XidImpl; /** * Mapper sending calls to a remote {@link NetServer}. */ public class MapperClient implements InvocationHandler { public static Mapper getMapper(RepositoryImpl repository, Credentials credentials) throws StorageException { MapperClient handler = new MapperClient(repository, credentials); Mapper mapper = (Mapper) Proxy.newProxyInstance( MapperClient.class.getClassLoader(), new Class<?>[] { Mapper.class }, handler); synchronized (repository) { handler.repositoryId = repository.repositoryId; Identification id = mapper.getIdentification(); handler.identification = id; repository.repositoryId = id.repositoryId; } return mapper; } private enum Eof { VALUE; // used as singleton } public static final Object EOF = Eof.VALUE; protected String repositoryId; protected Identification identification; protected final String url; protected final HttpClient httpClient; protected final Header httpPrincipalHeader; // TODO should be replaced by an identification context in mapper protected MapperClient(RepositoryImpl repository, Credentials credentials) { httpClient = repository.getHttpClient(); RepositoryDescriptor desc = repository.getRepositoryDescriptor(); url = getUrl(desc); httpPrincipalHeader = getHttpPrincipalHeader(credentials); } protected static Header getHttpPrincipalHeader(Credentials credentials) { String username = "[unknown]"; if (credentials != null) { username = credentials.getUserName(); } return new Header("X-Nuxeo-Principal", username); } protected static String getUrl(RepositoryDescriptor repositoryDescriptor) { ServerDescriptor sd = repositoryDescriptor.connect.get(0); return sd.getUrl() + '/' + RepositoryImpl.SERVER_PATH_VCS; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); // special cases if (Mapper.GET_IDENTIFICATION.equals(methodName)) { if (identification != null) { return identification; } // else fall through (send to remote) } else if ("getTableSize".equals(methodName)) { return Integer.valueOf(getTableSize((String) args[0])); } else if ("createDatabase".equals(methodName)) { createDatabase(); return null; } // copying the transaction id implementation object that may not be // known by the class loader on server side. if (args != null) { for (int i = 0; i < args.length; i++) { if (args[i] instanceof Xid) { args[i] = new XidImpl((Xid) args[i]); } } } // send through network // this is decoded by NetServlet String postUrl = url; if (identification != null) { postUrl += '?' + MapperServlet.PARAM_RID + '=' + identification.repositoryId + '&' + MapperServlet.PARAM_MID + '=' + identification.mapperId; } else if (repositoryId != null) { postUrl += '?' + MapperServlet.PARAM_RID + '=' + repositoryId; } PostMethod m = new PostMethod(postUrl); m.setRequestHeader(httpPrincipalHeader); try { ObjectWriterRequestEntity writer = new ObjectWriterRequestEntity(); writer.add(methodName, args); m.setRequestEntity(writer); int status = httpClient.executeMethod(m); if (status != HttpStatus.SC_OK) { throw new ProtocolException(String.valueOf(status)); } String cs = m.getResponseCharSet(); if (cs != null && !cs.equals("ISO-8859-1")) { throw new RuntimeException("Bad encoding: " + cs); } Object res = new ObjectInputStream(m.getResponseBodyAsStream()).readObject(); if (res instanceof Throwable) { Throwable t = (Throwable) res; throw new StorageException("Remote exception: " + t, t); } else { return res; } } catch (StorageException e) { throw e; } catch (Exception e) { throw new StorageException(e); } finally { m.releaseConnection(); } } public int getTableSize(String tableName) { return 5; // TODO get from remote } public void createDatabase() { // do not create, remote did it } }