// Copyright 2004-2014 Jim Voris // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package com.qumasoft.clientapi; import com.qumasoft.qvcslib.AbstractProjectProperties; import com.qumasoft.qvcslib.ArchiveDirManagerProxy; import com.qumasoft.qvcslib.ArchiveInfoInterface; import com.qumasoft.qvcslib.CheckOutCommentManager; import com.qumasoft.qvcslib.requestdata.ClientRequestGetMostRecentActivityData; import com.qumasoft.qvcslib.requestdata.ClientRequestListClientProjectsData; import com.qumasoft.qvcslib.DirectoryManagerFactory; import com.qumasoft.qvcslib.LabelManager; import com.qumasoft.qvcslib.LogFileProxy; import com.qumasoft.qvcslib.LogfileInfo; import com.qumasoft.qvcslib.PasswordChangeListenerInterface; import com.qumasoft.qvcslib.QVCSConstants; import com.qumasoft.qvcslib.RemoteProjectProperties; import com.qumasoft.qvcslib.ServerManager; import com.qumasoft.qvcslib.ServerProperties; import com.qumasoft.qvcslib.response.ServerResponseChangePassword; import com.qumasoft.qvcslib.response.ServerResponseGetMostRecentActivity; import com.qumasoft.qvcslib.response.ServerResponseInterface; import com.qumasoft.qvcslib.response.ServerResponseListProjects; import com.qumasoft.qvcslib.response.ServerResponseListViews; import com.qumasoft.qvcslib.response.ServerResponseLogin; import com.qumasoft.qvcslib.response.ServerResponseProjectControl; import com.qumasoft.qvcslib.TransportProxyFactory; import com.qumasoft.qvcslib.TransportProxyListenerInterface; import com.qumasoft.qvcslib.TransportProxyType; import com.qumasoft.qvcslib.Utility; import com.qumasoft.qvcslib.WorkfileDigestManager; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.TreeMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; /** * A class that implements the {@link ClientAPI} interface. * @author Jim Voris */ class ClientAPIImpl implements ClientAPI, ChangeListener, PasswordChangeListenerInterface, TransportProxyListenerInterface { private static final String NOT_SUPPORTED = "Not Supported"; private static final long TEN_SECONDS = 10000L; private static final long ONE_HUNDRED_MILLISECONDS = 100L; ClientAPIImpl(ClientAPIContextImpl contextImpl) { this.clientAPIContextImpl = contextImpl; } /** * So no one can invoke the default constructor. */ private ClientAPIImpl() { this.clientAPIContextImpl = null; } /** * Create our logger object */ private static Logger logger = Logger.getLogger("com.qumasoft.clientapi"); /** * The name we use for the server... it really can be anything. */ private static final String SERVER_NAME = "QVCS Enterprise Server for ClientAPI"; /** * Where we get the results of our login request. */ private final AtomicReference<String> passwordResponse = new AtomicReference<>(QVCSConstants.QVCS_NO); /** * Count the number of logins so each one gets its own unique server name. */ private static AtomicInteger serverInstanceCounter = new AtomicInteger(0); /** * The current clientContextAPIImpl. */ private final ClientAPIContextImpl clientAPIContextImpl; /** * Get the list of projects from the QVCS-Enterprise server. The list of * projects returned will depend on the username/password supplied in the * clientAPIContext object. Only those projects visible to the given user * will be returned. A project is 'visible' to a user if that user has the * 'Get File' privilege. Typically, any QVCS-Enterprise user with a READER * role, or a DEVELOPER role will have the 'Get File' privilege. * * @return a List<String> of project names visible to the username defined * in the clientAPIContext. * @throws ClientAPIException if there are any problems. */ @Override public List<String> getProjectList() throws ClientAPIException { List<String> projectList = populateProjectList(); // End the operation. endOperation(); return projectList; } /** * Get the list of views for the a given project. To use this method, you * must set the name of the project in the clientAPIContext as well as the * other parameters needed for the getProjectList method. * * @param clientAPIContext the client API context object used to define the * username/password, server connection information, and project name. * @return a List<String> of view names. At the very least, this List will * include the 'Trunk' view. * @throws ClientAPIException if there are any problems, or if the requested * project is not found. */ @Override public List<String> getViewList() throws ClientAPIException { List<String> viewList = populateViewList(); // End the operation. endOperation(); return viewList; } /** * Get the list of directories for the given project/view. To use this * method, you must set the view name in the clientAPIContext, as well as * all the other parameters needed for the getProjectList method. * * @param clientAPIContext the client API context object used to define the * username/password, server connection information, project name, and view * name. * @return a List<String> of appended path strings for all the directories * of a project/view. Each string represents one directory. The empty string * "" is used to represent the root directory of the project/view. The * Strings for sub-directories are relative to the project root directory * and in QVCS-Enterprise terminology are called the directory's * 'appendedPath'. * @throws ClientAPIException if there are any problems. */ @Override public List<String> getProjectDirectoryList() throws ClientAPIException { List<String> directoryList = populateDirectoryList(); // End the operation. endOperation(); return directoryList; } /** * Get the list of {@link FileInfo} objects for the directory specified via * the {@link ClientAPIContext#setAppendedPath(java.lang.String)} attribute * on the clientAPIContext object. If you set the recurse flag to true (via * the {@link ClientAPIContext#setRecurseFlag(boolean)} attribute), then the * returned List will contain elements for the directory identified by the * appended path, as well as all the directories beneath that directory. * * @param clientAPIContext the client API context object used to define the * username/password, server connection information, project name, view * name, appended path, and optionally, the recurse flag. * @return a List of {@link FileInfo} objects; one per file. * @throws ClientAPIException if there are any problems. */ @Override public List<FileInfo> getFileInfoList() throws ClientAPIException { List<FileInfo> fileInfoList = populateFileInfoList(); // End the operation. endOperation(); return fileInfoList; } /** * Get the list of {@link RevisionInfo} objects for the file specified by * the clientAPIContext. * * @param clientAPIContext the client API context object used to define the * username/password, server connection information, project name, view * name, appended path, and the file name. * @return the List of {@link RevisionInfo} objects for the file specified * by the clientAPIContext. The List could be empty if the file is not under * version control. Be careful to identify the location of the file * correctly -- i.e. the project name, view name, appended path, and file * name must be correct in order for the server to find the file and report * its revision information. * @throws ClientAPIException if there are any problems. */ @Override public List<RevisionInfo> getRevisionInfoList() throws ClientAPIException { List<RevisionInfo> revisionInfoList = populateRevisionInfoList(); // End the operation. endOperation(); return revisionInfoList; } @Override public Date getMostRecentActivity() throws ClientAPIException { Date mostRecentActivity = fetchMostRecentActivity(); logger.log(Level.INFO, "Most recent activity for Project/View/Appended Path: [" + this.clientAPIContextImpl.getProjectName() + "/" + this.clientAPIContextImpl.getViewName() + "/" + this.clientAPIContextImpl.getAppendedPath() + ": " + mostRecentActivity.toString()); // End the operation. endOperation(); return mostRecentActivity; } @Override public String getPendingPassword(String serverName) { throw new UnsupportedOperationException(NOT_SUPPORTED); } @Override public void notifyLoginResult(ServerResponseLogin response) { synchronized (passwordResponse) { if (response.getLoginResult()) { if (response.getVersionsMatchFlag()) { passwordResponse.set(QVCSConstants.QVCS_YES); } else { logger.log(Level.WARNING, "Client jar file is out of date."); passwordResponse.set(QVCSConstants.QVCS_NO); } } else { passwordResponse.set(QVCSConstants.QVCS_NO); logger.log(Level.WARNING, "Login failure: [" + response.getFailureReason() + "]"); } passwordResponse.notifyAll(); } } @Override public void notifyPasswordChange(ServerResponseChangePassword response) { } @Override public void notifyTransportProxyListener(ServerResponseInterface message) { throw new UnsupportedOperationException(NOT_SUPPORTED); } @Override public void notifyUpdateComplete() { throw new UnsupportedOperationException(NOT_SUPPORTED); } @Override public void savePendingPassword(String serverName, String password) { throw new UnsupportedOperationException(NOT_SUPPORTED); } @SuppressWarnings("SleepWhileInLoop") @Override public void stateChanged(ChangeEvent changeEvent) { String msg = "Change Event: " + changeEvent.getSource().getClass().getName(); logger.log(Level.INFO, msg); Object source = changeEvent.getSource(); if (source instanceof ServerResponseListProjects) { msg = "Received list of projects for server: [" + SERVER_NAME + "]"; logger.log(Level.INFO, msg); synchronized (clientAPIContextImpl.getSyncObject()) { ServerResponseListProjects projectList = (ServerResponseListProjects) source; String[] projectNames = projectList.getProjectList(); clientAPIContextImpl.setProjectProperties(projectList.getPropertiesList()); for (String projectName : projectNames) { logger.log(Level.INFO, projectName); } clientAPIContextImpl.setServerProjectNames(projectNames); clientAPIContextImpl.getSyncObject().notifyAll(); } } else if (source instanceof ServerResponseListViews) { msg = "Received list of views for project: [" + clientAPIContextImpl.getProjectName() + "]"; logger.log(Level.INFO, msg); synchronized (clientAPIContextImpl.getSyncObject()) { ServerResponseListViews serverResponseListViews = (ServerResponseListViews) source; String[] viewNames = serverResponseListViews.getViewList(); for (String viewName : viewNames) { logger.log(Level.INFO, viewName); } clientAPIContextImpl.setProjectViewNames(viewNames); clientAPIContextImpl.getSyncObject().notifyAll(); } } else if (source instanceof ServerResponseProjectControl) { ServerResponseProjectControl projectControl = (ServerResponseProjectControl) source; if (projectControl.getAddFlag()) { String[] segments = projectControl.getDirectorySegments(); // Create appended path for this prospective directory manager String appendedPath = Utility.createAppendedPathFromSegments(segments); msg = "********************************************************** Received AppendedPath: [" + appendedPath + "]"; logger.log(Level.INFO, msg); // Add the appendedPath to the collection of appended paths for prospective directory // managers. We'll create a sync object that we can use to synchronize on that // directory manager, if we wind up needing to create a directory manager. // (At this point we are simply creating the entire set of appended paths // that exist for a given project... we will have to use some subset of that // collection, based on the appended path of the user's request). if (null == clientAPIContextImpl.getAppendedPathMap().get(appendedPath)) { clientAPIContextImpl.getAppendedPathMap().put(appendedPath, new Object()); } } } else if (source instanceof ArchiveDirManagerProxy) { ArchiveDirManagerProxy archiveDirManager = (ArchiveDirManagerProxy) source; String appendedPath = archiveDirManager.getAppendedPath(); logger.log(Level.INFO, "Directory: " + appendedPath); Object localSyncObject = null; while (localSyncObject == null) { localSyncObject = clientAPIContextImpl.getAppendedPathMap().get(appendedPath); if (localSyncObject == null) { try { // This can happen if we get the response from the server before // we've had a chance to populate the appendPathMap with an // entry for the given directory. logger.log(Level.INFO, "Did not find synchronization object for: [" + appendedPath + "]. Waiting for server response..."); Thread.sleep(ONE_HUNDRED_MILLISECONDS); } catch (InterruptedException e) { logger.log(Level.WARNING, Utility.expandStackTraceToString(e)); } } } if (localSyncObject != null) { synchronized (localSyncObject) { localSyncObject.notifyAll(); } } } else if (source instanceof ServerResponseGetMostRecentActivity) { msg = "Received most recent activity."; logger.log(Level.INFO, msg); ServerResponseGetMostRecentActivity serverResponseGetMostRecentActivity = (ServerResponseGetMostRecentActivity) source; logger.log(Level.INFO, "Project: [" + serverResponseGetMostRecentActivity.getProjectName() + "] View: [" + serverResponseGetMostRecentActivity.getViewName() + "] Appended path: " + "[" + serverResponseGetMostRecentActivity.getAppendedPath() + "]"); synchronized (clientAPIContextImpl.getSyncObject()) { clientAPIContextImpl.setMostRecentActivity(serverResponseGetMostRecentActivity.getMostRecentActivityDate()); clientAPIContextImpl.getSyncObject().notifyAll(); } } else { msg = "stateChanged received unexpected object of type [" + source.getClass().getName() + "]"; logger.log(Level.WARNING, msg); } } private void createDirectoryManager(String appendedPath) throws ClientAPIException { Object syncObject = new Object(); synchronized (syncObject) { ArchiveDirManagerProxy archiveDirManagerProxy = clientAPIContextImpl.getArchiveDirManagerProxyMap().get(appendedPath); if (archiveDirManagerProxy == null) { try { // Save the sync object for future use... clientAPIContextImpl.getAppendedPathMap().put(appendedPath, syncObject); // Create the directory manager proxy. archiveDirManagerProxy = new ArchiveDirManagerProxy(getProjectProperties(), clientAPIContextImpl.getServerProperties(), clientAPIContextImpl.getViewName(), clientAPIContextImpl.getUserName(), appendedPath); archiveDirManagerProxy.setFastNotify(true); archiveDirManagerProxy.addChangeListener(this); clientAPIContextImpl.getArchiveDirManagerProxyMap().put(appendedPath, archiveDirManagerProxy); archiveDirManagerProxy.startDirectoryManager(); // Wait for the response from the server. String msg = "Waiting for server response for: [" + appendedPath + "]"; logger.log(Level.FINE, msg); syncObject.wait(); msg = "Received server response for: [" + appendedPath + "]"; logger.log(Level.FINE, msg); } catch (InterruptedException e) { logger.log(Level.WARNING, Utility.expandStackTraceToString(e)); } } else { String msg = "Found existing directory manager for: [" + appendedPath + "]"; logger.log(Level.INFO, msg); } } } private void endOperation() { if (!clientAPIContextImpl.getPreserveStateFlag()) { TransportProxyFactory.getInstance().removeChangeListener(this); TransportProxyFactory.getInstance().removeChangedPasswordListener(this); ServerManager.getServerManager().removeChangeListener(this); clientAPIContextImpl.getTransportProxy().close(); clientAPIContextImpl.setLoggedInFlag(false); } } private AbstractProjectProperties getProjectProperties() { Properties remoteProperties = null; for (int i = 0; i < clientAPIContextImpl.getProjectProperties().length; i++) { if (0 == clientAPIContextImpl.getServerProjectNames()[i].compareTo(clientAPIContextImpl.getProjectName())) { remoteProperties = clientAPIContextImpl.getProjectProperties()[i]; String msg = "Matched project: " + clientAPIContextImpl.getServerProjectNames()[i] + "; Index: " + i; logger.log(Level.FINE, msg); break; } } return new RemoteProjectProperties(clientAPIContextImpl.getProjectName(), remoteProperties); } private void login() throws ClientAPIException { clientAPIContextImpl.setProjectProperties(null); clientAPIContextImpl.setServerProjectNames(null); clientAPIContextImpl.setProjectViewNames(null); clientAPIContextImpl.setAppendedPathMap(Collections.synchronizedMap(new TreeMap<String, Object>())); clientAPIContextImpl.setArchiveDirManagerProxyMap(Collections.synchronizedMap(new TreeMap<String, ArchiveDirManagerProxy>())); if (!clientAPIContextImpl.getLoggedInFlag()) { TransportProxyType transportType = TransportProxyFactory.RAW_SOCKET_PROXY; // The port we'll connect on... int port = clientAPIContextImpl.getPort(); // User name String userName = clientAPIContextImpl.getUserName(); // Hash the password... byte[] hashedPassword = Utility.getInstance().hashPassword(clientAPIContextImpl.getPassword()); // Initialize the workfile digest manager WorkfileDigestManager.getInstance().initialize(); // Initialize the checkout comment manager. CheckOutCommentManager.getInstance().initialize(); // Initialize the label manager String systemUserName = System.getProperty(clientAPIContextImpl.getUserName()); LabelManager.setUserName(systemUserName); LabelManager.getInstance().initialize(); // And force the login to the transport... TransportProxyFactory.getInstance().addChangeListener(this); TransportProxyFactory.getInstance().addChangedPasswordListener(this); ServerManager.getServerManager().addChangeListener(this); // Create a server properties instance ServerProperties serverProperties = new ServerProperties(); String serverName = SERVER_NAME + " Instance " + serverInstanceCounter.getAndIncrement(); serverProperties.setServerName(serverName); serverProperties.setServerIPAddress(clientAPIContextImpl.getServerIPAddress()); serverProperties.setClientPort(port); clientAPIContextImpl.setServerProperties(serverProperties); synchronized (passwordResponse) { try { logger.log(Level.INFO, "********************* Before login attempt to [" + serverProperties.getServerIPAddress() + "] *********************"); clientAPIContextImpl.setTransportProxy(TransportProxyFactory.getInstance().getTransportProxy(transportType, serverProperties, port, userName, hashedPassword, this, null)); // Wait 10 seconds for a response. passwordResponse.wait(TEN_SECONDS); logger.log(Level.INFO, "********************* After login attempt to [" + serverProperties.getServerIPAddress() + "] *********************"); } catch (InterruptedException e) { logger.log(Level.INFO, "Interrupted exception."); } } if (passwordResponse.get().equals(QVCSConstants.QVCS_YES)) { logger.log(Level.INFO, "Logged in to server."); clientAPIContextImpl.setLoggedInFlag(true); } else { throw new ClientAPIException("***************** Failed to login to server *****************"); } } } private List<String> populateDirectoryList() throws ClientAPIException { List<String> directoryList = new ArrayList<>(); // Populate the view list. (which populates the project list). populateViewList(); // Validate the input parameters. validateAPIContextGetDirectoryList(); // Make sure the requested view exists. boolean requestedViewFoundFlag = false; for (String viewName : clientAPIContextImpl.getProjectViewNames()) { if (0 == viewName.compareToIgnoreCase(clientAPIContextImpl.getViewName())) { requestedViewFoundFlag = true; break; } } if (!requestedViewFoundFlag) { throw new ClientAPIException("Requested view not found: " + clientAPIContextImpl.getViewName()); } // Save credentials for this server. DirectoryManagerFactory.getInstance().setServerUsername(clientAPIContextImpl.getServerProperties().getServerName(), clientAPIContextImpl.getUserName()); DirectoryManagerFactory.getInstance().setServerPassword(clientAPIContextImpl.getServerProperties().getServerName(), clientAPIContextImpl.getPassword()); // We need to get a directory manager for the root of the project so we'll get the additional // segments that exist for this project. We'll then have to use those to figure out the // set of directory managers that we need to build in order to satisfy the users request. logger.log(Level.INFO, "Getting directory list from server"); createDirectoryManager(""); Iterator<String> it = clientAPIContextImpl.getAppendedPathMap().keySet().iterator(); while (it.hasNext()) { directoryList.add(it.next()); } return directoryList; } private List<FileInfo> populateFileInfoList() throws ClientAPIException { // Populate the directory list (which will populate the project list, and the view list as well). populateDirectoryList(); // Validate the input parameters. validateAPIContextGetFileInfoList(); // Make sure the requested appended path exists. if (!clientAPIContextImpl.getAppendedPathMap().keySet().contains(this.clientAPIContextImpl.getAppendedPath())) { throw new ClientAPIException("Request appended path does not exist."); } // Create the collection of directories that the user is interested in... Set<String> candidateAppendedPaths = new HashSet<>(); candidateAppendedPaths.add(this.clientAPIContextImpl.getAppendedPath()); if (this.clientAPIContextImpl.getRecurseFlag()) { Iterator<String> it = clientAPIContextImpl.getAppendedPathMap().keySet().iterator(); while (it.hasNext()) { String candidate = it.next(); if (candidate.startsWith(clientAPIContextImpl.getAppendedPath())) { candidateAppendedPaths.add(candidate); } } } // Create the directory proxys for the set of directories the user is interested in. Iterator<String> it = candidateAppendedPaths.iterator(); while (it.hasNext()) { String interestingAppendedPath = it.next(); createDirectoryManager(interestingAppendedPath); } // Create the collection of FileInfo objects. Map<String, FileInfo> sortedFileInfo = new TreeMap<>(); Iterator<ArchiveDirManagerProxy> proxyIterator = this.clientAPIContextImpl.getArchiveDirManagerProxyMap().values().iterator(); while (proxyIterator.hasNext()) { ArchiveDirManagerProxy archiveDirManagerProxy = proxyIterator.next(); Iterator<ArchiveInfoInterface> archiveInfoIterator = archiveDirManagerProxy.getArchiveInfoCollection().values().iterator(); while (archiveInfoIterator.hasNext()) { ArchiveInfoInterface archiveInfo = archiveInfoIterator.next(); sortedFileInfo.put(archiveInfo.getShortWorkfileName(), new FileInfoImpl(archiveInfo, archiveDirManagerProxy.getAppendedPath())); } } return new ArrayList<>(sortedFileInfo.values()); } private List<String> populateProjectList() throws ClientAPIException { List<String> projectList; // Validate the input parameters. validateAPIContextGetProjectList(); // Login to the server. login(); // Wait for the project list from the server. logger.log(Level.INFO, "Client API waiting for project list from server..."); waitForProjectList(); projectList = new ArrayList<>(clientAPIContextImpl.getServerProjectNames().length); projectList.addAll(Arrays.asList(clientAPIContextImpl.getServerProjectNames())); return projectList; } private List<RevisionInfo> populateRevisionInfoList() throws ClientAPIException { // Populate the file info list (which will populate the project list, and the view list as well, and the directory info list). populateFileInfoList(); // Validate the input parameters. validateAPIContextGetRevisionInfoList(); // Make sure the requested file actually exists on the server. LogFileProxy logFileProxy = (LogFileProxy) clientAPIContextImpl.getArchiveDirManagerProxyMap().get(clientAPIContextImpl .getAppendedPath()).getArchiveInfo(clientAPIContextImpl.getFileName()); if (logFileProxy == null) { throw new ClientAPIException("Requested file [" + clientAPIContextImpl.getFileName() + "] not found in directory: [" + clientAPIContextImpl.getAppendedPath() + "]"); } // Wait for revision detail from the server. logger.log(Level.INFO, "Client API waiting for revision detail from server..."); LogfileInfo logFileInfo = logFileProxy.getLogfileInfo(); // And populate the list with the revision info. List<RevisionInfo> revisionInfoList = new ArrayList<>(logFileProxy.getRevisionCount()); for (int i = 0; i < logFileProxy.getRevisionCount(); i++) { RevisionInfoImpl revisionInfoImpl = new RevisionInfoImpl(logFileInfo, logFileInfo.getRevisionInformation().getRevisionHeader(i)); revisionInfoList.add(revisionInfoImpl); } return revisionInfoList; } private List<String> populateViewList() throws ClientAPIException { List<String> viewList; // Populate the project list... populateProjectList(); // Validate the input parameters. validateAPIContextGetViewList(); // Make sure the requested project exists. boolean requestedProjectFoundFlag = false; for (String projectName : clientAPIContextImpl.getServerProjectNames()) { if (0 == projectName.compareToIgnoreCase(clientAPIContextImpl.getProjectName())) { requestedProjectFoundFlag = true; break; } } if (!requestedProjectFoundFlag) { throw new ClientAPIException("Requested project not found: " + clientAPIContextImpl.getProjectName()); } waitForViewList(); viewList = new ArrayList<>(clientAPIContextImpl.getProjectViewNames().length); viewList.addAll(Arrays.asList(clientAPIContextImpl.getProjectViewNames())); return viewList; } private void validateAPIContextGetDirectoryList() throws ClientAPIException { } private void validateAPIContextGetFileInfoList() throws ClientAPIException { // Make sure the user supplied an appended path. if (this.clientAPIContextImpl.getAppendedPath() == null) { throw new ClientAPIException("You must define an appended path"); } } private void validateAPIContextGetProjectList() throws ClientAPIException { // Make sure the user supplied the required parameters for a getProjectList request. if (this.clientAPIContextImpl.getUserName() == null || this.clientAPIContextImpl.getUserName().length() == 0) { throw new ClientAPIException("You must define a user name"); } if (this.clientAPIContextImpl.getPassword() == null || this.clientAPIContextImpl.getPassword().length() == 0) { throw new ClientAPIException("You must supply a password"); } if (this.clientAPIContextImpl.getServerIPAddress() == null || this.clientAPIContextImpl.getServerIPAddress().length() == 0) { throw new ClientAPIException("You must supply the server IP address"); } if (this.clientAPIContextImpl.getPort() == null) { throw new ClientAPIException("You must define the server port."); } } private void validateAPIContextGetRevisionInfoList() throws ClientAPIException { if (this.clientAPIContextImpl.getFileName() == null || this.clientAPIContextImpl.getFileName().length() == 0) { throw new ClientAPIException("You must supply a filename."); } } private void validateAPIContextGetViewList() throws ClientAPIException { // Make sure they supplied a project name as well. if (this.clientAPIContextImpl.getProjectName() == null || this.clientAPIContextImpl.getProjectName().length() == 0) { throw new ClientAPIException("You must supply a project name."); } } private void waitForProjectList() { ClientRequestListClientProjectsData projectsData = new ClientRequestListClientProjectsData(); projectsData.setServerName(SERVER_NAME); synchronized (clientAPIContextImpl.getSyncObject()) { try { clientAPIContextImpl.getTransportProxy().write(projectsData); clientAPIContextImpl.getSyncObject().wait(); } catch (InterruptedException e) { logger.log(Level.INFO, "Interrupted exception waiting for project list."); } } } private void waitForViewList() { synchronized (clientAPIContextImpl.getSyncObject()) { try { TransportProxyFactory.getInstance().requestViewList(clientAPIContextImpl.getServerProperties(), this.clientAPIContextImpl.getProjectName()); clientAPIContextImpl.getSyncObject().wait(); } catch (InterruptedException e) { logger.log(Level.INFO, "Interrupted exception waiting for view list."); } } } private Date fetchMostRecentActivity() throws ClientAPIException { Date mostRecentActivity = null; // Login to the server. login(); ClientRequestGetMostRecentActivityData request = new ClientRequestGetMostRecentActivityData(); request.setProjectName(this.clientAPIContextImpl.getProjectName()); request.setViewName(this.clientAPIContextImpl.getViewName()); request.setAppendedPath(this.clientAPIContextImpl.getAppendedPath()); synchronized (clientAPIContextImpl.getSyncObject()) { try { clientAPIContextImpl.getTransportProxy().write(request); clientAPIContextImpl.getSyncObject().wait(); mostRecentActivity = clientAPIContextImpl.getMostRecentActivity(); } catch (InterruptedException e) { logger.log(Level.INFO, "Interrupted exception waiting for most recent activity."); } } return mostRecentActivity; } }