package com.perforce.api; import java.io.*; import java.util.*; /* * Copyright (c) 2001, Perforce Software, All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** * Representation of a source control client. This class can be used to retrieve * and hold p4 client information. It's class methods can be used to list <a * href="#getClients()">all loaded clients</a> or list those <a * href="#lookupClient(java.lang.String)">beginning with a particular prefix</a>. * Before these class methods are called, the class method <a * href="#loadClients()">loadClients()</a> must be called to ensure the client * list is up to date. * * @author <a href="mailto:david@markley.cc">David Markley</a> * @version $Date: 2002/08/05 $ $Revision: #4 $ */ public final class Client extends Mapping { private String root = ""; private String options = ""; private final static int DECAY_TIMEOUT = 86400000; private static HashDecay clients = null; private static long load_time = 0; /** * Default no-argument constructor. */ public Client() { super(); getCache(); } /** * Constructor that is passed the client name. */ public Client(String name) { this((Env) null, name); } /** * Constructor that is passed the client name. */ public Client(Env env, String name) { this(); setEnv(env); setName(name); } private synchronized static HashDecay setCache() { if(null == clients) { clients = new HashDecay(DECAY_TIMEOUT); clients.start(); } if(clients.isEmpty()) load_time = 0; return clients; } public HashDecay getCache() { return setCache(); } /** * Sets the options for the Client. * * @param options * Options of the client. */ public void setOptions(String options) { this.options = options; } /** Returns the Client options. */ public String getOptions() { return options; } /** * Sets the root for the Client. * * @param root * Root of the client. */ public void setRoot(String root) { this.root = root; } /** Returns the Client root. */ public String getRoot() { return root; } /** * Returns a Client with the specified name, or null if not found. It is * important to keep in mind that the Client returned may not hold valid * information, other than its name. To ensure the Client has valid * information, the sync() method must be called. * * @param name * Name of the client to find. */ public static synchronized Client getClient(Env env, String name) { Client c; if(null == name || name.trim().equals("")) return null; if(null == (c = (Client) setCache().get(name))) c = new Client(name); if(null != env) c.setEnv(env); c.sync(); synchronized(clients) { clients.put(name, c); } return c; } /** * Returns a Client with the specified name, or null if not found. * * @see #getClient(Env, String) */ public static synchronized Client getClient(String name) { return getClient(null, name); } /** * Returns a list of clients that begin with the specified prefix. * * @param env * P4 environment to use. * @param prefix * Prefix for all clients to be returned. * @return List of clients matching the prefix. */ public static Enumeration lookupClient(Env env, String prefix) { loadClients(env); return lookupMappings(clients, prefix); } /** * Returns a list of clients that begin with the specified prefix. * * @param prefix * Prefix for all clients to be returned * @return List of clients matching the prefix. */ public static Enumeration lookupClient(String prefix) { loadClients(null); return lookupMappings(clients, prefix); } /** * Returns a list of all the clients currently loaded. * * @return List of all clients currently loaded. * @deprecated */ public static Enumeration getClients() { loadClients(null); return clients.keys(); } /** * @param env * Environment to use when working with P4 * @return Enumeration of all clients currently loaded. */ public static Enumeration getClients(Env env) { return Utils.getEnumeration(getClientIterator(env)); } /** * @param env * Environment to use when working with P4 * @return Iterator for all clients currently loaded. */ public static Iterator getClientIterator(Env env) { loadClients(env); Enumeration en = clients.elements(); TreeSet ts = new TreeSet(); while(en.hasMoreElements()) { ts.add(en.nextElement()); } return ts.iterator(); } /** * Loads a list of all the clients into an internal class Hashtable. This * method will only be called by the class itself if the Hashtable is empty. * Users should call this method if they believe the p4 client information * needs to be brought up to date. * * @see java.util.Hashtable */ public static void loadClients() { loadClients(null); } /** * Loads a list of all the clients into an internal class Hashtable. * * @see #loadClients() * @param env * Source conrol environment to use. */ public static void loadClients(Env env) { String l, name; Client c; String[] cmd = { "p4", "clients" }; StringTokenizer st; setCache(); synchronized(clients) { if((clients.getDelay() * 0.5) > ((new Date()).getTime() - load_time)) return; try { P4Process p = new P4Process(env); p.exec(cmd); while(null != (l = p.readLine())) { if(!l.startsWith("Client")) { continue; } try { st = new StringTokenizer(l.trim()); if(6 > st.countTokens()) { continue; } st.nextToken(); name = st.nextToken(); if(null == (c = (Client) clients.get(name))) { c = new Client(name); c.setEnv(env); } else { c.refreshUpdateTime(); continue; } st.nextToken(); // Skip modtime for now. st.nextToken(); // Skip 'root' text. c.setRoot(st.nextToken()); if(null != (st.nextToken("'"))) { c.setDescription(st.nextToken()); } else { c.setDescription(""); } clients.put(c.getName(), c); } catch(NoSuchElementException ex) { // We'll just skip this one. } } load_time = (new Date()).getTime(); } catch(Exception ex) { ex.printStackTrace(System.out); System.out.flush(); } } } /** * Stores the client information back into p4, creating the client if it * didn't already exist. * * @deprecated Use {@link #commit() commit()} instead. */ public void store() throws CommitException { this.commit(); } public void commit() throws CommitException { String[] cmd = { "p4", "client", "-i" }; String l; int cnt = 0; try { P4Process p = new P4Process(getEnv()); p.exec(cmd); p.println("Client: " + getName()); p.println("Owner: " + getOwner()); p.println("Root: " + getRoot()); p.println("Description: " + getDescription()); p.println("View:"); p.println(getView()); p.println("Options:"); p.println(getOptions()); p.flush(); p.outClose(); while(null != (l = p.readLine())) { if(0 == cnt++) continue; } p.close(); } catch(Exception ex) { throw new CommitException(ex.getMessage()); } } public void sync() { sync(getName()); } /** * Synchronizes the Client with the latest information from P4. This method * forces the Client to contain the latest, correct information if it didn't * already. * * @param name * Name of the Client to synchronize. */ public void sync(String name) { if(!outOfSync(60000)) return; String description = ""; String l; String[] cmd = { "p4", "client", "-o", "name" }; cmd[3] = name; setName(name); try { P4Process p = new P4Process(getEnv()); p.exec(cmd); while(null != (l = p.readLine())) { if(l.startsWith("#")) { continue; } if(l.startsWith("Client:")) { setName(l.substring(8).trim()); } else if(l.startsWith("Owner:")) { setOwner(l.substring(7).trim()); } else if(l.startsWith("Root:")) { setRoot(l.substring(6).trim()); } else if(l.startsWith("Options:")) { setOptions(l.substring(9).trim()); } else if(l.startsWith("Description:")) { while(null != (l = p.readLine())) { if(!l.startsWith("\t")) break; description += l + "\n"; } setDescription(description); } else if(l.startsWith("View:")) { while(null != (l = p.readLine())) { if(!(l.startsWith("\t") || l.startsWith(" ") || l.startsWith("//"))) break; addView(l); } } } p.close(); inSync(); } catch(IOException ex) { Debug.out(Debug.ERROR, ex); } refreshUpdateTime(); } public String toXML() { StringBuffer sb = new StringBuffer("<client name=\""); sb.append(getName()); sb.append("\" owner=\""); sb.append(getOwner()); sb.append("\" root=\""); sb.append(getRoot()); sb.append("\" options=\""); sb.append(getOptions()); sb.append("\">"); sb.append(super.toXML()); sb.append("</client>"); return sb.toString(); } /** * Used for testing. * * @deprecated Actually in use, but this keeps it out of the docs. */ public static void main(String[] argv) { if(argv.length < 2) { System.err.println("Usage: p4.Client <port> <user> [<password>]"); System.exit(-1); } System.out.println("Clients on " + argv[0] + ":"); Env environ = new Env(); environ.setPort(argv[0]); environ.setUser(argv[1]); if(3 == argv.length) { environ.setPassword(argv[2]); } Enumeration en = getClients(environ); Client c; while(en.hasMoreElements()) { c = (Client) en.nextElement(); System.out.println(c); } System.exit(0); } }