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 environment. This information is typically
* passed to a <code>P4Process</code> instance by the
* <code>SourceControlObject</code> instances. It can also be set in the
* {@link P4Process#getBase() base} P4Process instance. This will cause it to be
* used as the default environment for all command execution.
* <P>
* Values for the environment can be easily loaded from a
* {@link java.util.Properties Properties} file. This makes configuration of the
* environment much simpler.
*
* @see java.util.Properties
* @author <a href="mailto:david@markley.cc">David Markley</a>
* @version $Date: 2002/05/16 $ $Revision: #5 $
*/
public class Env {
private boolean envp_valid = false;
private String[] envp;
private Hashtable environ;
private Properties props;
private String p4_exe; // Full path to the P4 executable.
private String sep_path = null;
private String sep_file = null;
private long threshold = 10000;
/** Default, no-argument constructor. */
public Env() {
super();
environ = new Hashtable();
environ.put("P4USER", "robot");
environ.put("P4CLIENT", "robot-client");
environ.put("P4PORT", "localhost:1666");
environ.put("P4PASSWD", "");
environ.put("PATH", "C:\\Program Files\\Perforce");
environ.put("CLASSPATH", "/usr/share/java/p4.jar");
environ.put("SystemDrive", "C:");
environ.put("SystemRoot", "C:\\WINNT");
environ.put("PATHEXT", ".COM;.EXE;.BAT;.CMD");
setFromProperties(new Properties(System.getProperties()));
}
/**
* Constructs an environment from a properties file.
*
* @param propfile
* full path to a properties file.
*/
public Env(String propfile) throws PerforceException {
this();
setFromProperties(propfile);
}
/**
* Constructor that uses another environment as its basis. This is useful
* for cloning environments and then changing a few attributes.
*
* @param base
* Environment to be copied into the new environment.
*/
public Env(Env base) {
this();
this.environ = (Hashtable) base.environ.clone();
this.props = (Properties) base.props.clone();
this.p4_exe = base.getExecutable();
}
/**
* Constructor that uses a set of <code>Properties</code> to set up the
* environment.
*
* @see #setFromProperties(Properties)
* @param props
* Used to construct the environment.
*/
public Env(Properties props) {
this();
setFromProperties(props);
}
/**
* Allows the user to set any environment variable. If the variable name
* starts with 'P4', the value can not be set to null. It will instead be
* set to the empty string. For all other variable, supplying a null value
* will remove that variable form the environment.
*
* @param name
* environment variable name
* @param value
* environment variable value
*/
public void setenv(String name, String value) {
if(null == name)
return;
synchronized(environ) {
if(null == value) {
if(name.startsWith("P4")) {
environ.put(name, "");
} else {
environ.remove(name);
}
} else {
environ.put(name, value);
}
}
envp_valid = false;
}
/**
* Returns the value for the named environment variable.
*
* @param name
* environment variable name
*/
public String getenv(String name) {
synchronized(environ) {
return (String) environ.get(name);
}
}
/**
* Returns the environment in a <code>String</code> array.
*/
public String[] getEnvp() {
String var;
if(!envp_valid) {
synchronized(environ) {
envp = new String[environ.size()];
Enumeration en = environ.keys();
int i = 0;
while(en.hasMoreElements()) {
var = (String) en.nextElement();
envp[i++] = var + "=" + environ.get(var);
}
}
envp_valid = true;
}
return envp;
}
/**
* Checks the environment to see if it is valid. To check the validity of
* the environment, the user information is accessed. This ensures that the
* server can be contacted and that the password is set properly.
* <p>
* If the environment is valid, this method will return quietly. Otherwise,
* it will throw a <code>PerforceException</code> with a message regarding
* the failure.
*/
public void checkValidity() throws PerforceException {
String[] msg = { "Connect to server failed; check $P4PORT", "Perforce password (P4PASSWD) invalid or unset.",
"Can't create a new user - over license quota." };
int msgndx = -1, i, cnt = 0;
P4Process p = null;
String l;
String[] cmd = { "p4", "user", "-o" };
try {
p = new P4Process(this);
p.exec(cmd);
while(null != (l = p.readLine())) {
cnt++;
for(i = 0; i < msg.length; i++) {
if(-1 != l.indexOf(msg[i]))
msgndx = i;
}
}
p.close();
} catch(IOException ex) {
if(null != p) {
try {
p.close();
} catch(Exception ignex) { /* Ignored Exception */
}
}
}
if(-1 != msgndx)
throw new PerforceException(msg[msgndx]);
if(0 == cnt)
throw new PerforceException("No output from p4 user -o");
}
/**
* Returns a <code>Vector</code> containing the property value list, as
* split up by the commas. This is used to get the values for a property in
* the form of:
* <p>
* some.property.key=val1,val2,val3
* <p>
* Will always return a <code>Vector</code>, even if it is empty.
*
* @param key
* the property key
* @param defaultValue
* a default value
*/
public Vector getPropertyList(String key, String defaultValue) {
return getPropertyList(key, defaultValue, ",");
}
/**
* Returns a <code>Vector</code> containing the property value list, as
* split up by the specified delimeter. This is used to get the values for a
* property in the form of:
* <p>
* some.property.key=val1,val2,val3
* <p>
* Will always return a <code>Vector</code>, even if it is empty.
*
* @param key
* the property key
* @param defaultValue
* a default value
* @param delimeter
* string that seperates the values
*/
public Vector getPropertyList(String key, String defaultValue, String delimeter) {
Vector v = new Vector();
String val, tok;
StringTokenizer st;
val = getProperty(key, defaultValue);
st = new StringTokenizer(val, delimeter);
while(st.hasMoreTokens()) {
v.addElement(st.nextToken());
}
return v;
}
/**
* Returns a new <code>Properties</code> instance that is set using the
* environments properties as its default.
*/
public Properties getProperties() {
return new Properties(props);
}
/**
* Searches for the property with the specified key in this property list.
* If the key is not found in this property list, the default property list,
* and its defaults, recursively, are then checked. The method returns the
* default value argument if the property is not found.
*
* @param key
* the property key
* @param defaultValue
* a default value
* @return the value in this property list with the specified key value
* @see java.util.Properties
*/
public String getProperty(String key, String defaultValue) {
if(null == props)
return defaultValue;
return props.getProperty(key, defaultValue);
}
/**
* Searches for the property with the specified key in this property list.
* If the key is not found in this property list, the default property list,
* and its defaults, recursively, are then checked. The method returns null
* if the property is not found.
*
* @param key
* the property key
* @return the value in this property list with the specified key value
* @see java.util.Properties
*/
public String getProperty(String key) {
return getProperty(key, null);
}
/**
* Calls the hashtable method put. Provided for parallelism with the
* getProperty method. Enforces use of strings for property keys and values.
*
* @param key
* the key to be placed into this property list.
* @param value
* the value corresponding to key.
* @see java.util.Properties#setProperty(String,String)
*/
public String setProperty(String key, String value) {
String val = (String) props.setProperty(key, value);
if(!key.startsWith("p4.")) {
return val;
}
if(key.equals("p4.user")) {
setUser(value);
} else if(key.equals("p4.client")) {
setClient(value);
} else if(key.equals("p4.port")) {
setPort(value);
} else if(key.equals("p4.password")) {
setPassword(value);
} else if(key.equals("p4.executable")) {
setExecutable(value);
} else if(key.equals("p4.sysdrive")) {
setSystemDrive(value);
} else if(key.equals("p4.sysroot")) {
setSystemRoot(value);
} else if(key.equals("p4.threshold")) {
try {
setServerTimeout(Integer.valueOf(value).intValue());
} catch(Exception ex) { /* Ignored Exception */
}
}
return val;
}
/**
* Sets the environment using the specified properties file.
*
* @see #setFromProperties(Properties)
* @param propfile
* Path to a properties file.
*/
public void setFromProperties(String propfile) throws PerforceException {
Properties props = new Properties(System.getProperties());
if(null != propfile) {
try {
props.load(new BufferedInputStream(new FileInputStream(propfile)));
System.setProperties(props);
} catch(Exception e) {
System.err.println("Unable to load properties.");
e.printStackTrace(System.err);
throw new PerforceException("Unable to load properties from " + propfile);
}
}
setFromProperties(props);
}
/**
* Uses a set of <code>Properties</code> to set up the environment. The
* properties that are used used by this method are:
*
* <table border="1"> <thead>
* <tr>
* <th>Property</th>
* <th>Value Set</th>
* </tr>
* </thead><tbody>
* <tr>
* <td>p4.user</td>
* <td>P4USER</td>
* </tr>
* <tr>
* <td>p4.client</td>
* <td>P4CLIENT</td>
* </tr>
* <tr>
* <td>p4.port</td>
* <td>P4PORT</td>
* </tr>
* <tr>
* <td>p4.password</td>
* <td>P4PASSWORD</td>
* </tr>
* <tr>
* <td>p4.executable</td>
* <td>Executable</td>
* </tr>
* <tr>
* <td>p4.sysdrive</td>
* <td>SystemDrive</td>
* </tr>
* <tr>
* <td>p4.sysroot</td>
* <td>SystemRoot</td>
* </tr>
* <tr>
* <td>p4.threshold</td>
* <td>Server Timeout Threshold</td>
* </tr>
* </tbody></table>
*
* @param props
* Used to construct the environment.
*/
public void setFromProperties(Properties props) {
this.props = props;
sep_path = getProperty("path.separator", ";");
sep_file = getProperty("file.separator", "/");
setUser(getProperty("p4.user", "robot"));
setClient(getProperty("p4.client", "robot-client"));
setPort(getProperty("p4.port", "localhost:1666"));
setPassword(getProperty("p4.password", ""));
setExecutable(getProperty("p4.executable", "P4"));
setSystemDrive(getProperty("p4.sysdrive", "C:"));
setSystemRoot(getProperty("p4.sysroot", "C:\\WINNT"));
try {
setServerTimeout(Integer.valueOf(getProperty("p4.threshold", "10000")).intValue());
} catch(Exception ex) { /* Ignored Exception */
}
String os = props.getProperty("os.name");
if(null == os) {
return;
}
if(os.startsWith("Windows")) {
String windir = props.getProperty("com.ms.windir");
if(null != windir) {
appendPath(windir.substring(0, 1) + "\\Program Files\\Perforce");
setSystemDrive(windir.substring(0, 1));
setSystemRoot(windir);
}
}
}
/**
* Sets the P4USER in the class information.
*
* @param user
* P4USER value.
*/
public void setUser(String user) {
if(null == user)
return;
environ.put("P4USER", user);
props.setProperty("p4.user", user);
envp_valid = false;
}
/** Returns the P4USER. */
public String getUser() {
return (String) environ.get("P4USER");
}
/**
* Sets the P4CLIENT in the class information.
*
* @param user
* P4CLIENT value.
*/
public void setClient(String client) {
if(null == client)
return;
environ.put("P4CLIENT", client);
props.setProperty("p4.client", client);
envp_valid = false;
}
/** Returns the P4CLIENT. */
public String getClient() {
return (String) environ.get("P4CLIENT");
}
/**
* Sets the P4PORT in the class information.
*
* @param user
* P4PORT value.
*/
public void setPort(String port) {
if(null == port)
return;
environ.put("P4PORT", port);
props.setProperty("p4.port", port);
envp_valid = false;
}
/** Returns the P4PORT. */
public String getPort() {
return (String) environ.get("P4PORT");
}
/**
* Sets the P4PASSWD in the class information.
*
* @param user
* P4PASSWD value.
*/
public void setPassword(String password) {
if(null == password)
return;
environ.put("P4PASSWD", password);
props.setProperty("p4.password", password);
envp_valid = false;
}
/** Returns the P4PASSWORD. */
public String getPassword() {
return (String) environ.get("P4PASSWD");
}
/**
* Sets the PATH in the class information.
*
* @param path
* PATH value.
*/
public void setPath(String path) {
if(null == path)
return;
environ.put("PATH", path);
props.setProperty("p4.path", path);
envp_valid = false;
}
/**
* Append the path element to the existing path. If the path element given
* is already in the path, no change is made.
*
* @param path
* the path element to be appended.
*/
public void appendPath(String path) {
String tok;
if(null == path)
return;
String orig_path = (String) environ.get("PATH");
if(null == sep_path || null == orig_path) {
setPath(path);
return;
}
StringTokenizer st = new StringTokenizer(orig_path, sep_path);
StringBuffer sb = new StringBuffer();
while(st.hasMoreTokens()) {
tok = (String) st.nextToken();
if(tok.equals(path))
return;
sb.append(tok);
sb.append(sep_path);
}
sb.append(path);
setPath(path);
}
/** Returns the PATH. */
public String getPath() {
return (String) environ.get("PATH");
}
/**
* Sets the SystemDrive in the class information. This is only meaningful
* under Windows.
*
* @param user
* SystemDrive value.
*/
public void setSystemDrive(String drive) {
if(null == drive)
return;
environ.put("SystemDrive", drive);
props.setProperty("p4.sysdrive", drive);
envp_valid = false;
}
/**
* Sets the SystemRoot in the class information. This is only meaningful
* under Windows.
*
* @param user
* SystemRoot value.
*/
public void setSystemRoot(String root) {
if(null == root)
return;
environ.put("SystemRoot", root);
props.setProperty("p4.sysroot", root);
envp_valid = false;
}
/**
* Sets up the path to reach the p4 executable. The full path passed in must
* contain the executable or at least end in the system's file separator
* character. This gotten from the file.separator property. For example:
*
* <pre>
* p4.executable=/usr/bin/p4 # This will work
* p4.executable=/usr/bin/ # This will work
* <font color=Red>p4.executable=/usr/bin # This won't work</font>
* </pre>
*
* @param exe
* Full path to the p4 executable.
*/
public void setExecutable(String exe) {
int pos;
if(null == exe)
return;
p4_exe = exe;
if(null == sep_file) {
sep_file = System.getProperties().getProperty("file.separator", "\\");
}
if(-1 == (pos = exe.lastIndexOf(sep_file)))
return;
if(null == sep_path) {
sep_path = System.getProperties().getProperty("path.separator", ";");
}
appendPath(exe.substring(0, pos));
props.setProperty("p4.executable", p4_exe);
envp_valid = false;
}
/** Returns the path to the executable. */
public String getExecutable() {
return p4_exe;
}
/** Set the server timeout threshold. */
public void setServerTimeout(long threshold) {
this.threshold = threshold;
props.setProperty("p4.threshold", String.valueOf(threshold));
}
/** Return the server timeout threshold. */
public long getServerTimeout() {
return threshold;
}
public String toString() {
String[] envp = getEnvp();
StringBuffer sb = new StringBuffer();
for(int i = 0; i < envp.length; i++) {
sb.append(envp[i]);
sb.append("\n");
}
return sb.toString();
}
/**
* Returns an XML representation of the environment.
*/
public String toXML() {
StringBuffer sb = new StringBuffer("<env");
sb.append(" user=\"");
sb.append(getUser());
sb.append("\" client=\"");
sb.append(getClient());
sb.append("\" port=\"");
sb.append(getPort());
sb.append("\" password=\"");
sb.append(getPassword());
sb.append("\" sysdrive=\"");
sb.append(environ.get("SystemDrive"));
sb.append("\" sysroot=\"");
sb.append(environ.get("SystemRoot"));
sb.append("\" threshold=\"");
sb.append(threshold);
sb.append("\"><executable>");
sb.append(getExecutable());
sb.append("</executable><path>");
sb.append(getPath());
sb.append("</path></env>");
return sb.toString();
}
}