/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenFlexo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openflexo.fps;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;
import java.util.Enumeration;
import java.util.InvalidPropertiesFormatException;
import java.util.Properties;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.netbeans.lib.cvsclient.Client;
import org.netbeans.lib.cvsclient.admin.StandardAdminHandler;
import org.netbeans.lib.cvsclient.command.CommandAbortedException;
import org.netbeans.lib.cvsclient.command.CommandException;
import org.netbeans.lib.cvsclient.command.GlobalOptions;
import org.netbeans.lib.cvsclient.command.update.UpdateCommand;
import org.netbeans.lib.cvsclient.connection.AbstractConnection;
import org.netbeans.lib.cvsclient.connection.AuthenticationException;
import org.netbeans.lib.cvsclient.connection.StandardScrambler;
import org.netbeans.lib.cvsclient.event.CVSAdapter;
import org.netbeans.lib.cvsclient.event.EnhancedMessageEvent;
import org.netbeans.lib.cvsclient.event.MessageEvent;
import org.openflexo.foundation.DataModification;
import org.openflexo.foundation.Inspectors;
import org.openflexo.fps.dm.CVSModuleDiscovered;
import org.openflexo.fps.dm.CVSModuleForgotten;
import org.openflexo.fps.dm.HasCVSExplored;
import org.openflexo.fps.dm.WillCVSExplore;
import org.openflexo.kvc.ChoiceList;
import org.openflexo.localization.FlexoLocalization;
import org.openflexo.toolbox.FileUtils;
import org.openflexo.xmlcode.StringConvertable;
import org.openflexo.xmlcode.StringEncoder;
import org.openflexo.xmlcode.StringEncoder.Converter;
public class CVSRepository extends FPSObject implements CVSExplorable {
private static final Logger logger = Logger.getLogger(CVSRepository.class.getPackage().getName());
private String name;
private String userName;
private String hostName;
private String repository;
private int port = -1;
private String passwd;
private String encodedPasswd;
private boolean storePassword = false;
private ConnectionType _connectionType;
private static final transient String NAME = "Name";
private static final transient String USER_NAME = "UserName";
private static final transient String HOST_NAME = "HostName";
private static final transient String REPOSITORY = "Repository";
private static final transient String PORT = "Port";
private static final transient String CONNECTION_TYPE = "ConnectionType";
private static final transient String ENCODED_PASSWD = "EncodedPassword";
private static final transient String PASSWD = "Password";
public CVSRepository() {
_modules = new Vector<CVSModule>();
}
public CVSRepository(Properties props) {
this();
name = props.getProperty(NAME);
userName = props.getProperty(USER_NAME);
hostName = props.getProperty(HOST_NAME);
repository = props.getProperty(REPOSITORY);
if (props.getProperty(PORT) != null) {
try {
port = Integer.parseInt(props.getProperty(PORT));
} catch (NumberFormatException e) {
logger.warning("Could not decode " + props.getProperty(PORT) + " as port number");
}
}
if (props.getProperty(PASSWD) != null) {
passwd = props.getProperty(PASSWD);
storePassword = true;
}
if (props.getProperty(ENCODED_PASSWD) != null) {
encodedPasswd = props.getProperty(ENCODED_PASSWD);
storePassword = true;
}
_connectionType = ConnectionType.get(props.getProperty(CONNECTION_TYPE));
}
public CVSRepository(File propFile) {
this(getProperties(propFile));
}
@Override
public CVSExplorable getParent() {
// A repository has a 'null' parent
return null;
}
@Override
public CVSRepository getCVSRepository() {
return this;
}
private static Properties getProperties(File propFile) {
Properties returned = new Properties();
try {
returned.loadFromXML(new FileInputStream(propFile));
} catch (InvalidPropertiesFormatException e) {
e.printStackTrace();
logger.warning("Could not load " + propFile.getAbsolutePath());
} catch (FileNotFoundException e) {
e.printStackTrace();
logger.warning("Could not load " + propFile.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
logger.warning("Could not load " + propFile.getAbsolutePath());
}
return returned;
}
public Properties getProperties() {
Properties properties = new Properties();
properties.setProperty(NAME, name != null ? name : "");
properties.setProperty(USER_NAME, userName != null ? userName : "");
properties.setProperty(HOST_NAME, hostName != null ? hostName : "");
properties.setProperty(REPOSITORY, repository != null ? repository : "");
properties.setProperty(PORT, "" + port);
properties.setProperty(CONNECTION_TYPE, _connectionType.getUnlocalizedStringRepresentation());
if (storePassword) {
if (_connectionType == ConnectionType.PServer && encodedPasswd != null) {
properties.setProperty(ENCODED_PASSWD, encodedPasswd);
}
if (_connectionType == ConnectionType.SSH && passwd != null) {
properties.setProperty(PASSWD, passwd);
}
}
return properties;
}
public String getCVSRoot() {
return ":" + getConnectionType().getUnlocalizedStringRepresentation() + ":" + (getUserName() != null ? getUserName() : "") + "@"
+ getHostName() + ":" + getRepository();
}
public ConnectionType getConnectionType() {
return _connectionType;
}
public void setConnectionType(ConnectionType connectionType) {
_connectionType = connectionType;
}
public String getEncodedPasswd() {
return encodedPasswd;
}
public void setEncodedPasswd(String encodedPasswd) {
this.encodedPasswd = encodedPasswd;
setChanged();
}
public void setPassword(String passwd, boolean scramble) {
if (scramble) {
this.passwd = passwd;
this.encodedPasswd = StandardScrambler.getInstance().scramble(passwd);
} else {
this.passwd = passwd;
}
setChanged();
notifyObservers(new DataModification("password", null, passwd));
}
public void setPassword(String passwd) {
setPassword(passwd, getConnectionType() == ConnectionType.PServer);
}
public String getPassword() {
if (passwd == null) {
return encodedPasswd;
}
return passwd;
}
public String getHostName() {
return hostName;
}
public void setHostName(String hostName) {
this.hostName = hostName;
setChanged();
}
public String getRepository() {
return repository;
}
public void setRepository(String repository) {
this.repository = repository;
setChanged();
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
setChanged();
}
@Override
public String getName() {
return name;
}
@Override
public void setName(String aName) {
if (name == null || !name.equals(aName)) {
if (name != null && getCVSRepositoryLocationFile().exists()) {
getCVSRepositoryLocationFile().delete();
}
name = aName;
setChanged();
}
}
private Vector<CVSModule> _modules;
@Override
public Vector<CVSModule> getCVSModules() {
return _modules;
}
public void setCVSModules(Vector<CVSModule> modules) {
_modules = modules;
setChanged();
}
public void addToCVSModules(CVSModule module) {
_modules.add(module);
setChanged();
notifyObservers(new CVSModuleDiscovered(module));
}
public void removeFromCVSModules(CVSModule module) {
_modules.remove(module);
setChanged();
notifyObservers(new CVSModuleForgotten(module));
}
private class ModuleRetriever extends CVSAdapter {
Vector<CVSModule> _knownModules;
private ModuleRetriever(Vector<CVSModule> knownModules) {
_knownModules = knownModules;
}
/**
* Called when the server wants to send a message to be displayed to the user. The message is only for information purposes and
* clients can choose to ignore these messages if they wish.
*
* @param e
* the event
*/
@Override
public void messageSent(MessageEvent e) {
// logger.info("messageSent(MessageEvent) "+e+" "+e.getMessage());
if (e instanceof EnhancedMessageEvent) {
return;
} else {
int start = e.getMessage().indexOf("`");
int stop = e.getMessage().indexOf("'");
if (start >= 0 && stop >= start) {
String foundModule = e.getMessage().substring(start + 1, stop);
if (!foundModule.equals("CVSROOT")) {
// logger.info("Found "+foundModule+" ! Add it.");
CVSModule existingModule = null;
for (CVSModule m : _knownModules) {
if (m.getModuleName().equals(foundModule)) {
existingModule = m;
}
}
if (existingModule == null) {
addToCVSModules(new CVSModule(foundModule, CVSRepository.this));
} else {
_knownModules.remove(existingModule);
}
}
}
}
}
}
private File _repositoryExploringDirectory;
@Override
public File getRepositoryExploringDirectory() {
return _repositoryExploringDirectory;
}
/**
* Little hack described on http://www.netbeans.org/issues/show_bug.cgi?id=35239
*
* @throws IOException
* @throws AuthenticationException
* @throws CommandException
* @throws CommandAbortedException
*/
protected void _retrieveModules() throws IOException, CommandAbortedException, CommandException, AuthenticationException {
logger.info("retrieveModules");
// Delete existing directory
if (_repositoryExploringDirectory != null && _repositoryExploringDirectory.exists()) {
FileUtils.recursiveDeleteFile(_repositoryExploringDirectory);
}
// Create an empty temporary folder
File aTempFile = null;
aTempFile = File.createTempFile("CVSRepositoryExploring." + Integer.toHexString(hashCode()) + ".", "");
String aTempFileName = aTempFile.getAbsolutePath();
aTempFile.delete();
_repositoryExploringDirectory = new File(aTempFileName);
_repositoryExploringDirectory.mkdir();
// makes a CVS sub-folder
File cvsDir = new File(_repositoryExploringDirectory, "CVS");
cvsDir.mkdir();
// Create CVS/Entries file with "D\n" content.
File cvsEntries = new File(cvsDir, "Entries");
try {
FileUtils.saveToFile(cvsEntries, "D\n");
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
// Create CVS/Root file with repository content.
File cvsRoot = new File(cvsDir, "Root");
FileUtils.saveToFile(cvsRoot, getCVSRoot() + "\n");
// Create CVS/Repository file with repository content.
File cvsRepository = new File(cvsDir, "Repository");
FileUtils.saveToFile(cvsRepository, ".\n");
// Run "cvs -n update -d" command. It will list the
// files and folders in the trunk on the server.
UpdateCommand updt = new UpdateCommand();
updt.setBuildDirectories(true); // build directories '-d'
AbstractConnection connection = CVSConnection.initConnection(this);
GlobalOptions globalOptions = new GlobalOptions();
globalOptions.setDoNoChanges(true); // no changes on files '-n'
globalOptions.setCVSRoot(getCVSRoot()); // Je sais pas ????
StandardAdminHandler adminHandler = new StandardAdminHandler();
Client client = new Client(connection, adminHandler);
client.setLocalPath(_repositoryExploringDirectory.getCanonicalPath());
Vector<CVSModule> knownModules = (Vector<CVSModule>) getCVSModules().clone();
ModuleRetriever retriever = new ModuleRetriever(knownModules);
client.getEventManager().addCVSListener(retriever);
client.getEventManager().addCVSListener(CVSConsole.getCVSConsole());
CVSConsole.getCVSConsole().commandLog("cvs " + updt.getCVSCommand());
logger.info("Command " + updt.getCVSCommand());
try {
client.executeCommand(updt, globalOptions);
} finally {
try {
client.getConnection().close();
} catch (IOException e) {
logger.warning("Fermeture de la connexion impossible" + e.getMessage());
}
// FileUtils.recursiveDeleteFile(aTempDir);
}
for (CVSModule m : knownModules) {
removeFromCVSModules(m);
}
logger.info("Retrieve modules DONE");
_isConnected = true;
}
private boolean _isConnected = false;
public boolean isConnected() {
return _isConnected;
}
@Override
public boolean isEnabled() {
return isConnected() || _explorer != null && !_explorer.isError();
}
public void disconnect() {
for (CVSModule m : (Vector<CVSModule>) getCVSModules().clone()) {
removeFromCVSModules(m);
}
_isConnected = false;
}
public enum ConnectionType implements StringConvertable, ChoiceList {
PServer, SSH;
public String getUnlocalizedStringRepresentation() {
if (this == PServer) {
return "pserver";
} else if (this == SSH) {
return "ssh";
}
return "???";
}
public String getStringRepresentation() {
return FlexoLocalization.localizedForKey(getUnlocalizedStringRepresentation());
}
@Override
public StringEncoder.Converter getConverter() {
return connectionTypeConverter;
}
public static StringEncoder.Converter connectionTypeConverter = new Converter<ConnectionType>(ConnectionType.class) {
@Override
public ConnectionType convertFromString(String value) {
for (ConnectionType cs : values()) {
if (cs.getStringRepresentation().equals(value)) {
return cs;
}
}
return null;
}
@Override
public String convertToString(ConnectionType value) {
return value.getStringRepresentation();
}
};
public static ConnectionType get(String connectionTypeName) {
for (Enumeration e = availableValues().elements(); e.hasMoreElements();) {
ConnectionType temp = (ConnectionType) e.nextElement();
if (temp.getUnlocalizedStringRepresentation().equals(connectionTypeName)) {
return temp;
}
}
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Could not find ConnectionType named " + connectionTypeName);
}
return null;
}
private static Vector<ConnectionType> _availableValues = null;
public static Vector<ConnectionType> availableValues() {
if (_availableValues == null) {
_availableValues = new Vector<ConnectionType>();
for (ConnectionType o : values()) {
_availableValues.add(o);
}
}
return _availableValues;
}
@Override
public Vector<ConnectionType> getAvailableValues() {
return availableValues();
}
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
@Override
public String getInspectorName() {
return Inspectors.FPS.CVS_REPOSITORY_INSPECTOR;
}
@Override
public String getClassNameKey() {
return "cvs_repository";
}
public boolean getStorePassword() {
return storePassword;
}
public void setStorePassword(boolean storePassword) {
this.storePassword = storePassword;
setChanged();
}
private File getCVSRepositoryLocationFile() {
if (_cvsRepositoryList == null) {
return null;
}
if (_cvsRepositoryList.getStoredRepositoryDirectory() == null) {
return null;
}
return new File(_cvsRepositoryList.getStoredRepositoryDirectory(), getName() + ".cvs");
}
protected void saveCVSRepositoryLocation() {
if (_cvsRepositoryList == null) {
return;
}
if (_cvsRepositoryList.getStoredRepositoryDirectory() == null) {
return;
}
if (!_cvsRepositoryList.getStoredRepositoryDirectory().exists()) {
_cvsRepositoryList.getStoredRepositoryDirectory().mkdirs();
}
saveCVSRepositoryLocation(getCVSRepositoryLocationFile());
}
protected void saveCVSRepositoryLocation(File fileToSave) {
if (!fileToSave.exists()) {
boolean b = false;
try {
b = fileToSave.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
if (!b) {
try {
fileToSave.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}
try {
getProperties().storeToXML(new FileOutputStream(fileToSave), "CVS repository location stored on " + new Date());
} catch (IOException e) {
e.printStackTrace();
logger.warning("Could not save " + fileToSave.getAbsolutePath());
}
}
private CVSRepositoryList _cvsRepositoryList;
public CVSRepositoryList getCVSRepositoryList() {
return _cvsRepositoryList;
}
public void setCVSRepositoryList(CVSRepositoryList cvsRepositoryList) {
_cvsRepositoryList = cvsRepositoryList;
}
@Override
public void setChanged() {
saveCVSRepositoryLocation();
super.setChanged();
}
@Override
public final void delete() {
super.delete();
getCVSRepositoryLocationFile().delete();
getCVSRepositoryList().removeFromCVSRepositories(this);
deleteObservers();
}
public CVSModule getModuleNamed(String name) {
if (name.lastIndexOf('/') > -1) {
String parentModuleName = name.substring(0, name.lastIndexOf('/'));
CVSModule parentModule = getModuleNamed(parentModuleName);
logger.info("Parent module: " + parentModule.getFullQualifiedModuleName());
return parentModule.getModuleNamed(name.substring(name.lastIndexOf('/') + 1));
}
for (CVSModule module : _modules) {
if (module.getModuleName().equals(name)) {
return module;
}
}
// Not found, create it
CVSModule returned;
addToCVSModules(returned = new CVSModule(name, CVSRepository.this));
return returned;
}
@Override
public boolean equals(Object object) {
if (object instanceof CVSRepository) {
CVSRepository r = (CVSRepository) object;
return getName().equals(r.getName()) && getHostName().equals(r.getHostName()) && getConnectionType() == r.getConnectionType()
&& getPort() == r.getPort() && getUserName().equals(r.getUserName());
} else {
return super.equals(object);
}
}
@Override
public boolean isContainedIn(FPSObject obj) {
if (obj instanceof CVSRepositoryList) {
return ((CVSRepositoryList) obj).getCVSRepositories().contains(this);
}
return obj == this;
}
private CVSExplorer _explorer;
@Override
public CVSExplorer getCVSExplorer(CVSExplorerListener explorerListener) {
if (_explorer == null) {
_explorer = new CVSExplorer(this, explorerListener);
}
return _explorer;
}
public CVSExplorer exploreRepository(CVSExplorerListener explorerListener) {
_explorer = null;
CVSExplorer returned = getCVSExplorer(explorerListener);
returned.explore();
return returned;
}
@Override
public void notifyWillExplore() {
setChanged();
notifyObservers(new WillCVSExplore());
}
@Override
public void notifyHasExplored() {
setChanged();
notifyObservers(new HasCVSExplored());
}
@Override
public CVSExplorer explore(CVSExplorerListener explorerListener) {
return exploreRepository(explorerListener);
}
}