Revision: 3273
Author: ferguson.sebastian
Date: Mon Feb  8 14:05:30 2010
Log: Moved Thick client things from Architect Enterprise
http://code.google.com/p/power-architect/source/detail?r=3273

Added:
 /trunk/src/ca/sqlpower/architect/enterprise
 /trunk/src/ca/sqlpower/architect/enterprise/ArchitectClientSideSession.java
 /trunk/src/ca/sqlpower/architect/enterprise/ProjectLocation.java
/trunk/src/ca/sqlpower/architect/swingui/action/RemoveServerProjectAction.java

=======================================
--- /dev/null
+++ /trunk/src/ca/sqlpower/architect/enterprise/ArchitectClientSideSession.java Mon Feb 8 14:05:30 2010
@@ -0,0 +1,719 @@
+package ca.sqlpower.architect.enterprise;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.SwingUtilities;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.NameValuePair;
+import org.apache.http.StatusLine;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.CookieStore;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.ResponseHandler;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.entity.FileEntity;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.BasicCookieStore;
+import org.apache.http.impl.client.BasicResponseHandler;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.util.EntityUtils;
+import org.apache.log4j.Logger;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import ca.sqlpower.architect.ArchitectProject;
+import ca.sqlpower.architect.ArchitectSession;
+import ca.sqlpower.architect.ArchitectSessionContext;
+import ca.sqlpower.architect.ArchitectSessionImpl;
+import ca.sqlpower.architect.swingui.ArchitectSwingSessionContext;
+import ca.sqlpower.dao.HttpMessageSender;
+import ca.sqlpower.dao.MessageSender;
+import ca.sqlpower.dao.SPPersistenceException;
+import ca.sqlpower.dao.SPPersisterListener;
+import ca.sqlpower.dao.SPSessionPersister;
+import ca.sqlpower.dao.helper.generated.SPPersisterHelperFactoryImpl;
+import ca.sqlpower.dao.json.SPJSONMessageDecoder;
+import ca.sqlpower.dao.json.SPJSONPersister;
+import ca.sqlpower.dao.session.SessionPersisterSuperConverter;
+import ca.sqlpower.enterprise.client.SPServerInfo;
+import ca.sqlpower.sql.DataSourceCollection;
+import ca.sqlpower.sql.DatabaseListChangeEvent;
+import ca.sqlpower.sql.DatabaseListChangeListener;
+import ca.sqlpower.sql.JDBCDataSource;
+import ca.sqlpower.sql.Olap4jDataSource;
+import ca.sqlpower.sql.PlDotIni;
+import ca.sqlpower.sql.SPDataSource;
+import ca.sqlpower.sql.SpecificDataSourceCollection;
+import ca.sqlpower.sqlobject.SQLObjectException;
+import ca.sqlpower.swingui.event.SessionLifecycleEvent;
+import ca.sqlpower.swingui.event.SessionLifecycleListener;
+import ca.sqlpower.util.SQLPowerUtils;
+import ca.sqlpower.util.UserPrompter.UserPromptOptions;
+import ca.sqlpower.util.UserPrompter.UserPromptResponse;
+
+public class ArchitectClientSideSession extends ArchitectSessionImpl {
+
+ private static Logger logger = Logger.getLogger(ArchitectClientSideSession.class);
+       private static CookieStore cookieStore = new BasicCookieStore();
+
+       public static final String MONDRIAN_SCHEMA_REL_PATH = null;
+
+       private final ProjectLocation projectLocation;
+       private final HttpClient outboundHttpClient;
+       private final SPSessionPersister sessionPersister;
+       private final SPJSONPersister jsonPersister;
+       private final Updater updater;
+ private final DataSourceCollectionUpdater dataSourceCollectionUpdater = new DataSourceCollectionUpdater();
+
+       private DataSourceCollection <JDBCDataSource> dataSourceCollection;
+
+       // -
+
+       public ArchitectClientSideSession(ArchitectSessionContext context,
+ String name, ProjectLocation projectLocation) throws SQLObjectException {
+               super(context, name);
+
+               this.projectLocation = projectLocation;
+
+               outboundHttpClient = 
createHttpClient(projectLocation.getServiceInfo());
+
+               getWorkspace().setUUID(projectLocation.getUUID());
+               getWorkspace().setName(projectLocation.getName());
+
+ MessageSender <JSONObject> messageSender = new Sender(outboundHttpClient, projectLocation.getServiceInfo(), projectLocation.getUUID());
+               jsonPersister = new SPJSONPersister(messageSender);
+
+               dataSourceCollection = getDataSourceCollection();
+
+ sessionPersister = new SPSessionPersister("inbound-" + projectLocation.getUUID(), getWorkspace(), + new SPPersisterHelperFactoryImpl(null, new SessionPersisterSuperConverter(dataSourceCollection, getWorkspace())));
+               sessionPersister.setSession(this);
+
+ updater = new Updater(projectLocation.getUUID(), new SPJSONMessageDecoder(sessionPersister));
+       }
+
+       // -
+
+       @Override
+    public boolean close() {
+       logger.debug("Closing Client Session");
+       try {
+               //TODO: Figure out how to de-register the session &c.
+ //HttpUriRequest request = new HttpDelete(getServerURI(projectLocation.getServiceInfo(),
+               //              "session/" + getWorkspace().getUUID()));
+                       //outboundHttpClient.execute(request, new 
BasicResponseHandler());
+               } catch (Exception e) {
+                       try {
+                               logger.error(e);
+
+ createUserPrompter("Cannot access the server to close the server session",
+                                               UserPromptType.MESSAGE,
+                                               UserPromptOptions.OK,
+                                               UserPromptResponse.OK,
+                                               UserPromptResponse.OK, "OK");
+
+                       } catch (Throwable t) {
+                               //do nothing here because we failed on logging 
the error.
+                       }
+               }
+
+        outboundHttpClient.getConnectionManager().shutdown();
+        updater.interrupt();
+
+        if (dataSourceCollection != null) {
+            dataSourceCollectionUpdater.detach(dataSourceCollection);
+        }
+
+        return super.close();
+    }
+
+       public DataSourceCollection <JDBCDataSource> getDataSourceCollection () 
{
+               if (dataSourceCollection != null) {
+                       return dataSourceCollection;
+               }
+
+               ResponseHandler<DataSourceCollection<JDBCDataSource>> 
plIniHandler =
+            new ResponseHandler<DataSourceCollection<JDBCDataSource>>() {
+ public DataSourceCollection<JDBCDataSource> handleResponse(HttpResponse response)
+                    throws ClientProtocolException, IOException {
+                if (response.getStatusLine().getStatusCode() != 200) {
+                    throw new IOException(
+ "Server error while reading data sources: " + response.getStatusLine());
+                }
+                PlDotIni plIni;
+                try {
+                                       plIni = new PlDotIni(
+ getServerURI(projectLocation.getServiceInfo(), "/jdbc"), + getServerURI(projectLocation.getServiceInfo(), MONDRIAN_SCHEMA_REL_PATH));
+                    plIni.read(response.getEntity().getContent());
+ logger.debug("Data source collection has URI " + plIni.getServerBaseURI());
+                } catch (URISyntaxException e) {
+                    throw new RuntimeException(e);
+                }
+
+ return new SpecificDataSourceCollection<JDBCDataSource>(plIni, JDBCDataSource.class);
+            }
+        };
+        try {
+ dataSourceCollection = executeServerRequest(outboundHttpClient, projectLocation.getServiceInfo(), "/data-sources/", plIniHandler);
+        } catch (Exception ex) {
+            throw new RuntimeException(ex);
+        }
+
+        dataSourceCollectionUpdater.attach(dataSourceCollection);
+
+        return dataSourceCollection;
+       }
+
+       public void startUpdaterThread() {
+               updater.start();
+
+               final SPPersisterListener listener = new SPPersisterListener(
+                               new 
SPPersisterHelperFactoryImpl(sessionPersister,
+ new SessionPersisterSuperConverter(dataSourceCollection, getWorkspace())));
+
+               SQLPowerUtils.listenToHierarchy(getWorkspace(), listener);
+
+ addSessionLifecycleListener(new SessionLifecycleListener<ArchitectSession>() {
+                       public void 
sessionClosing(SessionLifecycleEvent<ArchitectSession> e) {
+                               
SQLPowerUtils.unlistenToHierarchy(getWorkspace(), listener);
+                       }
+               });
+       }
+
+       public void persistProjectToServer() throws SPPersistenceException {
+               final SPPersisterListener tempListener = new 
SPPersisterListener(
+                               new SPPersisterHelperFactoryImpl(jsonPersister,
+ new SessionPersisterSuperConverter(dataSourceCollection, getWorkspace())));
+               tempListener.persistObject(getWorkspace().getRootObject(), 0);
+       }
+
+       public ArchitectProject getSystemWorkspace() {
+               for (ArchitectSession session : 
this.getContext().getSessions()) {
+                       if (session.getWorkspace().getUUID().equals("system")) {
+                               return session.getWorkspace();
+                       }
+               }
+               return null;
+       }
+
+
+       @Override
+       public void runInForeground(Runnable runner) {
+               // If we're in a SwingContext, run on the Swing Event Dispatch 
thread.
+ // XXX: This is a bit of a quickfix and I think a better way to possibly fix
+               // this could be to have WabitServerSession implement 
WabitSession, and
+               // use a delegate session to delegate most of the server calls 
(instead
+ // of extending WabitSessionImpl). Then if it's in a swing context, it would
+               // have a WabitSwingSession instead.
+               if (getContext() instanceof ArchitectSwingSessionContext) {
+                       SwingUtilities.invokeLater(runner);
+               } else {
+                       super.runInForeground(runner);
+               }
+       }
+
+       /**
+        * Exposes the shared cookie store so we don't spawn useless sessions
+        * through the client.
+        */
+       public static CookieStore getCookieStore() {
+               return cookieStore;
+       }
+
+       public ProjectLocation getProjectLocation() {
+               return projectLocation;
+       }
+
+       // -
+
+ public static List<ProjectLocation> getWorkspaceNames(SPServerInfo serviceInfo)
+       throws IOException, URISyntaxException, JSONException {
+       HttpClient httpClient = createHttpClient(serviceInfo);
+       try {
+ HttpUriRequest request = new HttpGet(getServerURI(serviceInfo, "/jcr/projects")); + String responseBody = httpClient.execute(request, new BasicResponseHandler());
+               List<ProjectLocation> workspaces = new 
ArrayList<ProjectLocation>();
+               JSONArray response = new JSONArray(responseBody);
+               logger.debug("Workspace list:\n" + responseBody);
+               for (int i = 0; i < response.length(); i++) {
+                       JSONObject workspace = (JSONObject) response.get(i);
+                       workspaces.add(new ProjectLocation(
+                                       workspace.getString("uuid"),
+                                       workspace.getString("name"),
+                                       serviceInfo));
+               }
+               return workspaces;
+       } finally {
+               httpClient.getConnectionManager().shutdown();
+       }
+    }
+
+ public static ProjectLocation createNewServerSession(SPServerInfo serviceInfo) throws URISyntaxException, ClientProtocolException, IOException, JSONException {
+       HttpClient httpClient = createHttpClient(serviceInfo);
+       try {
+ HttpUriRequest request = new HttpGet(getServerURI(serviceInfo, "/jcr/projects/new"));
+
+ String responseBody = httpClient.execute(request, new BasicResponseHandler());
+               JSONObject response = new JSONObject(responseBody);
+               logger.debug("New Workspace:" + responseBody);
+               return new ProjectLocation(
+                                       response.getString("uuid"),
+                                       response.getString("name"),
+                                       serviceInfo);
+       } finally {
+               httpClient.getConnectionManager().shutdown();
+       }
+    }
+
+ public static void deleteServerWorkspace(ProjectLocation projectLocation) throws URISyntaxException, ClientProtocolException, IOException {
+       SPServerInfo serviceInfo = projectLocation.getServiceInfo();
+       HttpClient httpClient = createHttpClient(serviceInfo);
+
+       try {
+               executeServerRequest(httpClient, 
projectLocation.getServiceInfo(),
+                               "/jcr/" + projectLocation.getUUID() + "/delete",
+                               new BasicResponseHandler());
+
+       } finally {
+               httpClient.getConnectionManager().shutdown();
+       }
+    }
+
+ private static <T> T executeServerRequest(HttpClient httpClient, SPServerInfo serviceInfo, + String contextRelativePath, ResponseHandler<T> responseHandler)throws IOException, URISyntaxException { + HttpUriRequest request = new HttpGet(getServerURI(serviceInfo, contextRelativePath));
+        return httpClient.execute(request, responseHandler);
+    }
+
+ private static URI getServerURI(SPServerInfo serviceInfo, String contextRelativePath) throws URISyntaxException {
+        logger.debug("Getting server URI for: " + serviceInfo);
+        String contextPath = serviceInfo.getPath();
+ URI serverURI = new URI("http", null, serviceInfo.getServerAddress(), serviceInfo.getPort(),
+                contextPath + contextRelativePath, null, null);
+        logger.debug("Created URI " + serverURI);
+        return serverURI;
+    }
+
+       public static HttpClient createHttpClient(SPServerInfo serviceInfo) {
+               HttpParams params = new BasicHttpParams();
+        HttpConnectionParams.setConnectionTimeout(params, 2000);
+        DefaultHttpClient httpClient = new DefaultHttpClient(params);
+        httpClient.setCookieStore(cookieStore);
+        httpClient.getCredentialsProvider().setCredentials(
+ new AuthScope(serviceInfo.getServerAddress(), AuthScope.ANY_PORT), + new UsernamePasswordCredentials(serviceInfo.getUsername(), serviceInfo.getPassword()));
+        return httpClient;
+       }
+
+ public static ArchitectClientSideSession openServerSession(ArchitectSessionContext context, ProjectLocation projectLoc)
+       throws SQLObjectException {
+ final ArchitectClientSideSession session = new ArchitectClientSideSession(context, "", projectLoc);
+       // TODO
+       //context.registerChildSession(session);
+               //session.startUpdaterThread();
+               return session;
+    }
+
+ public static List<ArchitectClientSideSession> openServerSessions(ArchitectSessionContext context, SPServerInfo serverInfo) + throws IOException, URISyntaxException, JSONException, SQLObjectException { + List<ArchitectClientSideSession> openedSessions = new ArrayList<ArchitectClientSideSession>(); + for (ProjectLocation workspaceLoc : ArchitectClientSideSession.getWorkspaceNames(serverInfo)) {
+                       openedSessions.add(openServerSession(context, 
workspaceLoc));
+               }
+        return openedSessions;
+    }
+
+ // Contained classes --------------------------------------------------------------------
+
+       /**
+        * Sends outgoing JSON
+        */
+       private class Sender extends HttpMessageSender<JSONObject> {
+
+               private JSONArray message;
+
+ public Sender(HttpClient httpClient, SPServerInfo serverInfo, String rootUUID) {
+                       super(httpClient, serverInfo, rootUUID);
+                       message = new JSONArray();
+               }
+
+               public void clear() {
+                       message = new JSONArray();
+               }
+
+               public void flush() throws SPPersistenceException {
+                       try {
+                               URI serverURI = getServerURI();
+                               HttpPost postRequest = new HttpPost(serverURI);
+                               postRequest.setEntity(new 
StringEntity(message.toString()));
+                               postRequest.setHeader("Content-Type", 
"application/json");
+                               HttpUriRequest request = postRequest;
+                       getHttpClient().execute(request, new 
ResponseHandler<Void>() {
+                                       public Void handleResponse(HttpResponse 
response)
+                                                       throws 
ClientProtocolException, IOException {
+                                               StatusLine statusLine = 
response.getStatusLine();
+                                               if (statusLine.getStatusCode() 
>= 400) {
+                                                       throw new 
ClientProtocolException(
+                                                                       "HTTP Post 
request returned an error: " +
+                                                                       "Code = " + 
statusLine.getStatusCode() + ", " +
+                                                                       "Reason = 
" + statusLine.getReasonPhrase());
+                                               }
+                                               return null;
+                                       }
+                       });
+                       } catch (ClientProtocolException e) {
+                               throw new SPPersistenceException(null, e);
+                       } catch (IOException e) {
+                               throw new SPPersistenceException(null, e);
+                       } catch (URISyntaxException e) {
+                               throw new SPPersistenceException(null, e);
+                       } finally {
+                               clear();
+                       }
+               }
+
+               public void send(JSONObject content) throws 
SPPersistenceException {
+                       message.put(content);
+               }
+
+               public URI getServerURI() throws URISyntaxException {
+                       String contextPath = getServerInfo().getPath();
+ return new URI("http", null, getServerInfo().getServerAddress(), getServerInfo().getPort(), + contextPath + "/project/" + getProjectLocation().getUUID(), null, null);
+               }
+       }
+
+
+       /**
+        * Polls this session's server for updates until interrupted. There 
should
+        * be exactly one instance of this class per ArchitectServerSession.
+        */
+       private class Updater extends Thread {
+
+               /**
+                * How long we will pause after an update error before 
attempting to
+                * contact the server again.
+                */
+               private long retryDelay = 1000;
+
+               private final SPJSONMessageDecoder jsonDecoder;
+
+               /**
+                * Used by the Updater to handle inbound HTTP updates
+                */
+               private final HttpClient inboundHttpClient;
+
+               private volatile boolean cancelled;
+
+               /**
+                * Creates, but does not start, the updater thread.
+                *
+                * @param projectUUID
+ * the ID of the workspace this updater is responsible for. This is
+                *            used in creating the thread's name.
+                */
+               Updater(String projectUUID, SPJSONMessageDecoder jsonDecoder) {
+                       super("updater-" + projectUUID);
+                       this.jsonDecoder = jsonDecoder;
+                       inboundHttpClient = 
createHttpClient(projectLocation.getServiceInfo());
+               }
+
+               public void interrupt() {
+                       logger.debug("Updater Thread interrupt sent");
+                       super.interrupt();
+                       cancelled = true;
+               }
+
+               @Override
+               public void run() {
+                       logger.info("Updater thread starting");
+
+                       // the path to contact on the server for update events
+                       int currentRevision = 0;
+ final String contextRelativePath = "/project/" + getWorkspace().getUUID() +
+                                                                                         
 "?oldRevisionNo=" + currentRevision;
+
+                       try {
+                               while (!this.isInterrupted() && !cancelled) {
+                                       try {
+
+                                               final String jsonArray = 
executeServerRequest(
+                                                               
inboundHttpClient, projectLocation.getServiceInfo(),
+                                                               
contextRelativePath, new BasicResponseHandler());
+
+                                               System.out.println(jsonArray);
+                               runInForeground(new Runnable() {
+                                                       public void run() {
+                                                               try {
+                                                                       
jsonDecoder.decode(jsonArray);
+                                                               } catch 
(SPPersistenceException e) {
+                                                                       
logger.error("Update from server failed!", e);
+                                                                       
createUserPrompter(
+ "Architect failed to apply an update that was just received from the Enterprise Server.\n"
+                                                                                       + 
"The error was:"
+                                                                                       + 
"\n" + e.getMessage(),
+                                                                               
        UserPromptType.MESSAGE, UserPromptOptions.OK,
+                                                                                       
UserPromptResponse.OK, UserPromptResponse.OK, "OK");
+                                                                       // TODO 
discard session and reload
+                                                               }
+                                                       }
+                                               });
+                                       } catch (Exception ex) {
+ logger.error("Failed to contact server. Will retry in " + retryDelay + " ms.", ex);
+                                               Thread.sleep(retryDelay);
+                                       }
+                               }
+                       } catch (InterruptedException ex) {
+                               logger.info("Updater thread exiting normally due to 
interruption.");
+                       }
+
+                       inboundHttpClient.getConnectionManager().shutdown();
+               }
+       }
+
+ private class DataSourceCollectionUpdater implements DatabaseListChangeListener, PropertyChangeListener {
+
+       /**
+ * If true this updater is currently posting properties to the server. If + * properties are being posted to the server and an event comes in because + * of a change during posting the updater should not try to repost the message
+        * it is currently trying to post.
+        */
+       private boolean postingProperties = false;
+
+ public void attach(DataSourceCollection<JDBCDataSource> dsCollection) {
+            dsCollection.addDatabaseListChangeListener(this);
+            for (SPDataSource ds : dsCollection.getConnections()) {
+                ds.addPropertyChangeListener(this);
+            }
+        }
+
+ public void detach(DataSourceCollection<JDBCDataSource> dsCollection) {
+            dsCollection.removeDatabaseListChangeListener(this);
+            for (SPDataSource ds : dsCollection.getConnections()) {
+                ds.removePropertyChangeListener(this);
+            }
+        }
+
+        /**
+ * Handles the addition of a new database entry, relaying its current + * state to the server. Also begins listening to the new data source as
+         * would have happened if the new data source existed before
+         * {...@link #attach(DataSourceCollection)} was invoked.
+         */
+        public void databaseAdded(DatabaseListChangeEvent e) {
+            SPDataSource newDS = e.getDataSource();
+            newDS.addPropertyChangeListener(this);
+
+ List<NameValuePair> properties = new ArrayList<NameValuePair>(); + for (Map.Entry<String, String> ent : newDS.getPropertiesMap().entrySet()) { + properties.add(new BasicNameValuePair(ent.getKey(), ent.getValue()));
+            }
+
+            postPropertiesToServer(newDS, properties);
+        }
+
+        /**
+         * Handles changes to individual data sources by relaying their new
+         * state to the server.
+         * <p>
+ * <b>Implementation note:</b> Presently, all properties for the data + * source are sent back to the server every time one of them changes.
+         * This is not the desired behaviour, but without rethinking the
+         * SPDataSource event system, there is little else we can do: the
+ * property change events tell us JavaBeans property names, but in order + * to send incremental updates, we's need to know the pl.ini property
+         * key names.
+         *
+         * @param evt
+ * The event describing the change. Its source must be the
+         *            data source object which was modified.
+         */
+        public void propertyChange(PropertyChangeEvent evt) {
+            SPDataSource ds = (SPDataSource) evt.getSource();
+            ds.addPropertyChangeListener(this);
+
+ // Updating all properties is less than ideal, but a property change event does
+            // not tell us what the "pl.ini" key for the property is.
+ List<NameValuePair> properties = new ArrayList<NameValuePair>(); + for (Map.Entry<String, String> ent : ds.getPropertiesMap().entrySet()) { + properties.add(new BasicNameValuePair(ent.getKey(), ent.getValue()));
+            }
+
+            postPropertiesToServer(ds, properties);
+        }
+
+        /**
+ * Modifies the properties of the given data source on the server. If
+         * the given data source does not exist on the server, it will be
+         * created with all of the given properties.
+         *
+         * @param ds
+         *            The data source to update on the server.
+         * @param properties
+ * The properties to update. No properties will be removed + * from the server, and only the given properties will be
+         *            updated or created.
+         */
+        private void postPropertiesToServer(SPDataSource ds,
+                List<NameValuePair> properties) {
+               if (postingProperties) return;
+
+ HttpClient httpClient = createHttpClient(projectLocation.getServiceInfo());
+            try {
+ final ResponseHandler<Void> responseHandler = new ResponseHandler<Void>() {
+                       public Void handleResponse(HttpResponse response)
+                       throws ClientProtocolException, IOException {
+
+                               if (response.getStatusLine().getStatusCode() != 
200) {
+                                       throw new ClientProtocolException(
+ "Failed to create/update data source on server. Reason:\n" +
+                                                       
EntityUtils.toString(response.getEntity()));
+                               } else {
+                                       // success!
+                                       return null;
+                               }
+
+                       }
+               };
+
+               if (ds instanceof Olap4jDataSource
+                               && ((Olap4jDataSource) ds).getMondrianSchema() 
!= null
+ && ((Olap4jDataSource) ds).getMondrianSchema().getScheme().equals("file")) { + //Pushing the mondrian schema to the server and updating the schema location to a server schema
+                       Olap4jDataSource olapDS = ((Olap4jDataSource) ds);
+                       File schemaFile = new File(olapDS.getMondrianSchema());
+
+                       if (!schemaFile.exists())
+                               logger.error("Schema file " + 
schemaFile.getAbsolutePath() +
+                                               " does not exist for data source 
" + ds.getName());
+
+                       HttpPost request = new HttpPost(
+                                       
getServerURI(projectLocation.getServiceInfo(),
+                                                       
MONDRIAN_SCHEMA_REL_PATH + schemaFile.getName()));
+
+                       request.setEntity(new FileEntity(schemaFile, 
"text/xml"));
+                       httpClient.execute(request, responseHandler);
+
+                       //updating new data source to point to the server's 
schema.
+                       for (int i = properties.size() - 1; i >= 0; i--) {
+                               NameValuePair pair = properties.get(i);
+ if (pair.getName().equals(Olap4jDataSource.MONDRIAN_SCHEMA)) {
+                                       properties.add(new BasicNameValuePair(
+                                                       
Olap4jDataSource.MONDRIAN_SCHEMA,
+                                                       SPDataSource.SERVER + 
schemaFile.getName()));
+                                       properties.remove(pair);
+                                       break;
+                               }
+                       }
+
+                       try {
+                               postingProperties = true;
+ olapDS.setMondrianSchema(new URI(SPDataSource.SERVER + schemaFile.getName()));
+                       } finally {
+                               postingProperties = false;
+                       }
+               }
+
+                HttpPost request = new HttpPost(dataSourceURI(ds));
+
+                request.setEntity(new UrlEncodedFormEntity(properties));
+                               httpClient.execute(request, responseHandler);
+
+            } catch (Exception ex) {
+                throw new RuntimeException(ex);
+            } finally {
+                httpClient.getConnectionManager().shutdown();
+            }
+        }
+
+        /**
+ * Handles deleting of a database entry by requesting that the server
+         * deletes it. Also unlistens to the data source to prevent memory
+         * leaks.
+         */
+        public void databaseRemoved(DatabaseListChangeEvent e) {
+ HttpClient httpClient = createHttpClient(projectLocation.getServiceInfo());
+            try {
+                SPDataSource removedDS = e.getDataSource();
+
+ HttpDelete request = new HttpDelete(dataSourceURI(removedDS));
+
+ final ResponseHandler<Void> responseHandler = new ResponseHandler<Void>() {
+                    public Void handleResponse(HttpResponse response)
+                            throws ClientProtocolException, IOException {
+
+ if (response.getStatusLine().getStatusCode() != 200) {
+                            throw new ClientProtocolException(
+ "Failed to delete data source on server. Reason:\n" + + EntityUtils.toString(response.getEntity()));
+                        } else {
+                            // success!
+                            return null;
+                        }
+
+                    }
+                };
+                               httpClient.execute(request, responseHandler);
+
+                               if (removedDS instanceof Olap4jDataSource
+                                               && ((Olap4jDataSource) 
removedDS).getMondrianSchema() != null) {
+                                       URI serverURI = ((Olap4jDataSource) 
removedDS).getMondrianSchema();
+                                       logger.debug("Server URI for deletion is 
" + serverURI);
+                                       HttpDelete schemaRequest = new 
HttpDelete(serverURI);
+                                       httpClient.execute(schemaRequest, 
responseHandler);
+                               }
+
+            } catch (Exception ex) {
+                throw new RuntimeException(ex);
+            } finally {
+                httpClient.getConnectionManager().shutdown();
+            }
+        }
+
+        /**
+ * Returns the URI that references the given data source on the server.
+         *
+         * @param ds
+         *            The data source whose server URI to return.
+ * @return An absolute URI for the given data source on this session's
+         *         Architect server.
+         */
+ private URI dataSourceURI(SPDataSource ds) throws URISyntaxException {
+            String type;
+            if (ds instanceof JDBCDataSource) {
+                type = "jdbc";
+            } else if (ds instanceof Olap4jDataSource) {
+                type = "olap4j";
+            } else {
+                throw new UnsupportedOperationException(
+ "Data source type " + ds.getClass() + " is not known");
+            }
+
+            return getServerURI(projectLocation.getServiceInfo(),
+                    "data-sources/" + type + "/" + ds.getName());
+        }
+    }
+
+}
=======================================
--- /dev/null
+++ /trunk/src/ca/sqlpower/architect/enterprise/ProjectLocation.java Mon Feb 8 14:05:30 2010
@@ -0,0 +1,41 @@
+package ca.sqlpower.architect.enterprise;
+
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.Immutable;
+
+import ca.sqlpower.enterprise.client.SPServerInfo;
+
+...@immutable
+public class ProjectLocation {
+
+       private final String uuid;
+       private final String name;
+       private final SPServerInfo serviceInfo;
+
+       public ProjectLocation(
+                       @Nonnull String uuid,
+                       @Nonnull String name,
+                       @Nonnull SPServerInfo serviceInfo) {
+
+               this.uuid = uuid;
+               this.name = name;
+               this.serviceInfo = serviceInfo;
+       }
+
+       public @Nonnull String getName() {
+               return name;
+       }
+
+       public @Nonnull String getUUID() {
+               return uuid;
+       }
+
+       public @Nonnull SPServerInfo getServiceInfo() {
+               return serviceInfo;
+       }
+
+       @Override
+       public String toString() {
+               return serviceInfo + ", uuid=" + uuid;
+       }
+}
=======================================
--- /dev/null
+++ /trunk/src/ca/sqlpower/architect/swingui/action/RemoveServerProjectAction.java Mon Feb 8 14:05:30 2010
@@ -0,0 +1,26 @@
+package ca.sqlpower.architect.swingui.action;
+
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+
+import ca.sqlpower.architect.enterprise.ArchitectClientSideSession;
+import ca.sqlpower.architect.enterprise.ProjectLocation;
+
+public class RemoveServerProjectAction extends AbstractAction {
+
+       private final ProjectLocation location;
+
+       public RemoveServerProjectAction (ProjectLocation location) {
+               this.location = location;
+       }
+
+       public void actionPerformed(ActionEvent e) {
+               try {
+                       
ArchitectClientSideSession.deleteServerWorkspace(location);
+               } catch (Exception ex) {
+ throw new RuntimeException("A problem has occured while deleting the a workspace", ex);
+               }
+       }
+}

Reply via email to