0001: /*
0002: * Copyright 2001-2006 C:1 Financial Services GmbH
0003: *
0004: * This software is free software; you can redistribute it and/or
0005: * modify it under the terms of the GNU Lesser General Public
0006: * License Version 2.1, as published by the Free Software Foundation.
0007: *
0008: * This software is distributed in the hope that it will be useful,
0009: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0010: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0011: * Lesser General Public License for more details.
0012: *
0013: * You should have received a copy of the GNU Lesser General Public
0014: * License along with this library; if not, write to the Free Software
0015: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
0016: */
0017:
0018: package de.finix.contelligent.revision.netbeans;
0019:
0020: import java.io.BufferedReader;
0021: import java.io.BufferedWriter;
0022: import java.io.File;
0023: import java.io.FileInputStream;
0024: import java.io.FileOutputStream;
0025: import java.io.InputStreamReader;
0026: import java.io.OutputStreamWriter;
0027: import java.io.Writer;
0028: import java.util.ArrayList;
0029: import java.util.Arrays;
0030: import java.util.HashSet;
0031: import java.util.Iterator;
0032: import java.util.LinkedList;
0033: import java.util.List;
0034: import java.util.Map;
0035: import java.util.Set;
0036: import java.util.StringTokenizer;
0037:
0038: import javax.transaction.Status;
0039:
0040: import org.apache.log4j.Logger;
0041: import org.netbeans.lib.cvsclient.Client;
0042: import org.netbeans.lib.cvsclient.admin.StandardAdminHandler;
0043: import org.netbeans.lib.cvsclient.command.BuildableCommand;
0044: import org.netbeans.lib.cvsclient.command.Command;
0045: import org.netbeans.lib.cvsclient.command.FileInfoContainer;
0046: import org.netbeans.lib.cvsclient.command.GlobalOptions;
0047: import org.netbeans.lib.cvsclient.command.KeywordSubstitutionOptions;
0048: import org.netbeans.lib.cvsclient.command.add.AddCommand;
0049: import org.netbeans.lib.cvsclient.command.checkout.CheckoutCommand;
0050: import org.netbeans.lib.cvsclient.command.commit.CommitCommand;
0051: import org.netbeans.lib.cvsclient.command.remove.RemoveCommand;
0052: import org.netbeans.lib.cvsclient.command.update.UpdateCommand;
0053: import org.netbeans.lib.cvsclient.connection.AuthenticationException;
0054: import org.netbeans.lib.cvsclient.connection.Connection;
0055: import org.netbeans.lib.cvsclient.connection.PServerConnection;
0056: import org.netbeans.lib.cvsclient.connection.StandardScrambler;
0057: import org.netbeans.lib.cvsclient.event.CVSListener;
0058: import org.netbeans.lib.cvsclient.event.FileAddedEvent;
0059: import org.netbeans.lib.cvsclient.event.FileInfoEvent;
0060: import org.netbeans.lib.cvsclient.event.FileRemovedEvent;
0061: import org.netbeans.lib.cvsclient.event.FileUpdatedEvent;
0062: import org.netbeans.lib.cvsclient.event.MessageEvent;
0063: import org.netbeans.lib.cvsclient.event.ModuleExpansionEvent;
0064: import org.netbeans.lib.cvsclient.event.TerminationEvent;
0065:
0066: import de.finix.contelligent.CallData;
0067: import de.finix.contelligent.Component;
0068: import de.finix.contelligent.ComponentPath;
0069: import de.finix.contelligent.Contelligent;
0070: import de.finix.contelligent.Session;
0071: import de.finix.contelligent.Type;
0072: import de.finix.contelligent.core.CallDataImpl;
0073: import de.finix.contelligent.core.ContelligentImpl;
0074: import de.finix.contelligent.core.ThreadFactory;
0075: import de.finix.contelligent.core.security.ContelligentSecurityManager;
0076: import de.finix.contelligent.event.ComponentEvent;
0077: import de.finix.contelligent.event.ContelligentEvent;
0078: import de.finix.contelligent.event.EventQueueTXListener;
0079: import de.finix.contelligent.event.PrincipalGroupEvent;
0080: import de.finix.contelligent.event.TypeEvent;
0081: import de.finix.contelligent.logging.LoggingService;
0082: import de.finix.contelligent.util.StringUtils;
0083: import de.finix.contelligent.util.ThreadedMem;
0084: import de.finix.contelligent.xml.export.ComponentXMLPolicy;
0085: import de.finix.contelligent.xml.export.ComponentXMLWriter;
0086: import de.finix.contelligent.xml.export.LocalXMLExport;
0087: import de.finix.contelligent.xml.export.SingleComponentXMLPolicy;
0088: import de.finix.contelligent.xml.export.TypeXMLWriter;
0089:
0090: /**
0091: * Depends on the <A href="http://javacvs.netbeans.org/library/index.html">Java
0092: * CVS client library</A> from the <A
0093: * href="http://javacvs.netbeans.org/index.html">JavaCVS Netbeans</A> project.
0094: *
0095: * This listener collects all events from successfully committed transactions
0096: * and commits any changes resulting from type-, component- or principal-events
0097: * into a CVS repository using a :pserver: connection. The format used is the
0098: * same as used by {@link LocalXMLExport}. ('exploded structure') <BR>
0099: * Note that the cvs part of this implementation is not synchronized. It is
0100: * assumed that only one cvs command gets executed at any time, for example the
0101: * result of the last command is stored in an instance-variable. <BR>
0102: * Currently their is no possibility to rollback any commits, if an error occurs
0103: * a mail gets send to the administrator. <BR>
0104: * FIXME: - escape any file called 'CVS' - maybe implement commit/rollback
0105: */
0106: final public class CVSLibConnector implements EventQueueTXListener,
0107: CVSListener, Runnable {
0108:
0109: final static Logger log = LoggingService
0110: .getLogger(CVSLibConnector.class);
0111:
0112: final private LinkedList events = new LinkedList(); // a list of lists of
0113:
0114: // ContelligentEvent
0115: // instances
0116:
0117: final private Thread thread;
0118:
0119: final private long sleep;
0120:
0121: final private String managerName;
0122:
0123: final private File moduleDir, componentsDir, filesDir, typesDir,
0124: rolesDir, usersDir;
0125:
0126: final private ComponentXMLPolicy xmlPolicy;
0127:
0128: final private GlobalOptions globalOptions;
0129:
0130: final private Client cvsClient;
0131:
0132: final private String host, user, encodedPassword, repository;
0133:
0134: final private int port;
0135:
0136: // is set by listener method {@link #commandTerminated}.
0137: private boolean lastCommandHadErrors = false;
0138:
0139: // is used by listener method {@link #messageSent}.
0140: private StringBuffer messages = new StringBuffer(256);
0141:
0142: /**
0143: * Creates a new <code>CVSLibConnector</code> instance. Currently only
0144: * :pserver: connections are supported.
0145: *
0146: * @param manager
0147: * only events with this origin get processed (only relevant for
0148: * component-events)
0149: * @param threadName
0150: * a <code>String</code> value
0151: * @param threadSleep
0152: * a <code>long</code> value
0153: * @param workDir
0154: * a <code>String</code> value
0155: * @param hostName
0156: * a <code>String</code> value
0157: * @param portNumber
0158: * the port to use, if null the default is used (2401)
0159: * @param userName
0160: * a <code>String</code> value
0161: * @param password
0162: * the password to use, must be passed in clear text
0163: * @param repositoryPath
0164: * the path of the repository on the server, for example
0165: * /opt/cvsroot
0166: * @param moduleName
0167: * a <code>String</code> value
0168: * @exception Exception
0169: * if an error occurs
0170: */
0171: public CVSLibConnector(String manager, String threadName,
0172: long threadSleep, String workDir, String hostName,
0173: String portNumber, String userName, String password,
0174: String repositoryPath, String moduleName) throws Exception {
0175: log.info("<init> - starting initialization ...");
0176: managerName = manager;
0177: sleep = threadSleep;
0178: host = hostName;
0179: port = (portNumber == null || portNumber.trim().length() == 0) ? PServerConnection.DEFAULT_PORT
0180: : Integer.parseInt(portNumber);
0181: user = userName;
0182: encodedPassword = StandardScrambler.getInstance().scramble(
0183: password);
0184: repository = repositoryPath;
0185:
0186: File wDir = new File(workDir);
0187: wDir.mkdirs();
0188: moduleDir = new File(wDir, moduleName);
0189: componentsDir = new File(moduleDir,
0190: LocalXMLExport.COMPONENTS_SUBDIR);
0191: filesDir = new File(moduleDir, LocalXMLExport.FILES_SUBDIR);
0192: typesDir = new File(moduleDir, LocalXMLExport.TYPES_SUBDIR);
0193: rolesDir = new File(moduleDir, LocalXMLExport.ROLES_SUBDIR);
0194: usersDir = new File(moduleDir, LocalXMLExport.USERS_SUBDIR);
0195:
0196: String cvsRoot;
0197:
0198: if (port != PServerConnection.DEFAULT_PORT) {
0199: cvsRoot = (":pserver:" + user + "@" + host + ":" + port + repository);
0200: } else {
0201: cvsRoot = (":pserver:" + user + "@" + host + ":" + repository);
0202: }
0203: log.info("<init> - ... using CVSROOT=`" + cvsRoot + "' ...");
0204:
0205: globalOptions = new GlobalOptions();
0206: globalOptions.setCVSRoot(cvsRoot);
0207: globalOptions.setVeryQuiet(true); // globalOptions.setModeratelyQuiet(true);
0208:
0209: Connection con = getConnection();
0210: con.verify(); // test connection
0211:
0212: cvsClient = new Client(con, new StandardAdminHandler());
0213: cvsClient.setLocalPath(moduleDir.getPath());
0214: cvsClient.getEventManager().addCVSListener(this );
0215: BuildableCommand cmd = null;
0216: if (moduleDir.isDirectory()) { // make a 'cvs update'
0217: cmd = new UpdateCommand();
0218: ((UpdateCommand) cmd).setPruneDirectories(false);
0219: ((UpdateCommand) cmd).setBuildDirectories(true);
0220: } else { // make a 'cvs checkout'
0221: cvsClient.setLocalPath(wDir.getPath());
0222: cmd = new CheckoutCommand(true, moduleName);
0223: ((CheckoutCommand) cmd).setPruneDirectories(false);
0224: }
0225: log.info("<init> - ... executing '" + commandToString(cmd)
0226: + "' ...");
0227: cmd.setBuilder(null);
0228: List cmdList = new ArrayList(1);
0229: cmdList.add(cmd);
0230: executeCommands(cmdList);
0231: // any other command except checkout works in module dir
0232: cvsClient.setLocalPath(moduleDir.getPath());
0233:
0234: if (!componentsDir.isDirectory() || !filesDir.isDirectory()
0235: || !typesDir.isDirectory() || !rolesDir.isDirectory()
0236: || !usersDir.isDirectory()) {
0237: throw new Exception(
0238: "CVSLibConnector: bad repository structure!");
0239: }
0240:
0241: xmlPolicy = new SingleComponentXMLPolicy(filesDir);
0242:
0243: log.info("<init> - ... starting thread ...");
0244: thread = ThreadFactory.getInstance().createThread(threadName,
0245: this );
0246: thread.setDaemon(true);
0247: thread.start();
0248: log.info("<init> - ... initialization done.");
0249: }
0250:
0251: public CVSLibConnector(Map propertyMap) throws Exception {
0252: this ((String) propertyMap.get("managerName"),
0253: (String) propertyMap.get("threadName"),
0254: Long.parseLong((String) propertyMap.get("interval")),
0255: (String) propertyMap.get("workdir"),
0256: (String) propertyMap.get("cvs-host"),
0257: (String) propertyMap.get("cvs-port"),
0258: (String) propertyMap.get("cvs-user"),
0259: (String) propertyMap.get("cvs-password"),
0260: (String) propertyMap.get("cvs-repository"),
0261: (String) propertyMap.get("cvs-module"));
0262: }
0263:
0264: public void onEvents(List eventList) {
0265: // this listener should only receive event via before/after-completion
0266: // methods!
0267: log.warn("onEvents() - called!");
0268: }
0269:
0270: public void onEventsBeforeCompletion(List eventList) {
0271: // FIXME: write event-log into database
0272: }
0273:
0274: public void onEventsAfterCompletion(int status, List eventList) {
0275: if (status == Status.STATUS_COMMITTED) {
0276: if (eventList != null && !eventList.isEmpty()) {
0277: synchronized (events) {
0278: events.addLast(eventList);
0279: }
0280: } else {
0281: log
0282: .debug("onEventsAfterCompletion() - ignoring empty event-list.");
0283: }
0284: } else {
0285: log
0286: .debug("onEventsAfterCompletion() - transaction-status!=COMMITTED => ignoring events.");
0287: }
0288: }
0289:
0290: public void run() {
0291: while (true) {
0292: int size = 0;
0293: synchronized (events) {
0294: size = events.size();
0295: }
0296: for (int i = 0; i < size; i++) {
0297: List eventList = null;
0298: synchronized (events) {
0299: eventList = (List) events.removeFirst();
0300: }
0301: processEvents(eventList); // sends mail to admin if an error
0302: // occurs
0303: }
0304: try {
0305: Thread.sleep(sleep); // in ms
0306: } catch (InterruptedException e) {
0307: log.debug("InterruptedException => break out of loop.");
0308: break;
0309: }
0310: }
0311: }
0312:
0313: public void stop() {
0314: thread.interrupt();
0315: }
0316:
0317: // ----------------------------------------------------------------------------------------------
0318: // revision-control
0319: // ----------------------------------------------------------------------------------------------
0320:
0321: private void processEvents(List eventList) {
0322: boolean debugEnabled = log.isDebugEnabled();
0323: List filesToCommit = new LinkedList();
0324: List cvsCommands = null;
0325: try {
0326: cvsCommands = createCommandList(eventList, filesToCommit);
0327: if (filesToCommit.size() > 0) {
0328: CommitCommand cmd = new CommitCommand();
0329: cmd.setBuilder(null);
0330: cmd.setRecursive(true);
0331: cmd.setFiles((File[]) filesToCommit
0332: .toArray(new File[filesToCommit.size()]));
0333: cvsCommands.add(cmd);
0334: }
0335: } catch (Exception e) {
0336: log
0337: .error(
0338: "processEvents() - Exception while creating commands: ",
0339: e);
0340: }
0341:
0342: executeCommands(cvsCommands);
0343: }
0344:
0345: private void executeCommands(List cvsCommands) {
0346: boolean debugEnabled = log.isDebugEnabled();
0347: Command currentCmd = null;
0348: Iterator commands = cvsCommands.iterator();
0349: Connection con = null;
0350: try {
0351: while (commands.hasNext()) {
0352: currentCmd = (Command) commands.next();
0353: con = getConnection();
0354: con.open();
0355: cvsClient.setConnection(con);
0356: messages.delete(0, messages.length()); // clear msg-buffer
0357: cvsClient.executeCommand(currentCmd, globalOptions);
0358: if (lastCommandHadErrors) { // this flag is set by listener
0359: // method {@link
0360: // #commandTerminated}.
0361: log.error("Command '" + commandToString(currentCmd)
0362: + "' returned with error:\n"
0363: + messages.toString());
0364: messages.insert(0, "Command '"
0365: + commandToString(currentCmd)
0366: + "' returned with error!\n");
0367: } else {
0368: if (debugEnabled)
0369: log
0370: .debug("... successfully executed '"
0371: + commandToString(currentCmd)
0372: + "' ...");
0373: }
0374: }
0375: } catch (AuthenticationException e) {
0376: log.error("AuthenticationException while executing '"
0377: + commandToString(currentCmd) + "': " + e);
0378: Throwable t = ((AuthenticationException) e)
0379: .getUnderlyingThrowable();
0380: log.error("-> underlying exception: ", t);
0381: messages.insert(0,
0382: "AuthenticationException while executing '"
0383: + commandToString(currentCmd) + "'!\n");
0384: } catch (Exception e) {
0385: log.error("Exception while executing '"
0386: + commandToString(currentCmd) + "': ", e);
0387: messages.insert(0, "Exception while executing '"
0388: + commandToString(currentCmd) + "'!\n");
0389: } finally {
0390: if (con != null) {
0391: try {
0392: con.close();
0393: } catch (Exception ignore) {
0394:
0395: }
0396: }
0397: }
0398: }
0399:
0400: /**
0401: * Describe <code>createCommandList</code> method here.
0402: *
0403: * @param eventList
0404: * a <code>List</code> value
0405: * @return a <code>List</code> value
0406: * @exception Exception
0407: * if an error occurs
0408: */
0409: private List createCommandList(List eventList, List filesToCommit)
0410: throws Exception {
0411: final boolean debugEnabled = log.isDebugEnabled();
0412:
0413: // create session (maybe some components need calldata
0414: // during export)
0415: Contelligent contelligent = ContelligentImpl.getInstance();
0416: Session session = contelligent.beginSession(
0417: ContelligentSecurityManager.getInstance().getGuest(),
0418: contelligent.getRootComponentManager());
0419: try {
0420: CallData callData = new CallDataImpl(session);
0421: ThreadedMem.setCallData(callData);
0422:
0423: List cvsCommands = new LinkedList(); // list of Command instances
0424: Iterator events = eventList.iterator();
0425: while (events.hasNext()) {
0426: Object evt = events.next();
0427: if (debugEnabled)
0428: log.debug("processing event '" + evt + "' ...");
0429: if (evt instanceof ComponentEvent) {
0430: ComponentEvent event = (ComponentEvent) evt;
0431: Component component = event.getComponent();
0432: String origin = event.getOrigin();
0433: if (!managerName.equals(origin)) {
0434: if (debugEnabled) {
0435: log
0436: .debug("ignoring event from manager '"
0437: + origin
0438: + "' (only events from '"
0439: + managerName
0440: + "' get processed.)");
0441: }
0442: } else {
0443: switch (event.getType()) {
0444: case ContelligentEvent.COMPONENT_CHANGE_EVENT:
0445: createOrUpdateComponent(component, false,
0446: cvsCommands, filesToCommit);
0447: break;
0448: case ContelligentEvent.COMPONENT_REMOVE_EVENT:
0449: deleteComponent(event.getTargetPath(),
0450: cvsCommands, filesToCommit);
0451: break;
0452: case ContelligentEvent.COMPONENT_ADD_EVENT:
0453: createOrUpdateComponent(component, true,
0454: cvsCommands, filesToCommit);
0455: break;
0456: default:
0457: if (debugEnabled)
0458: log.debug("ignoring component-event '"
0459: + event + "' ...");
0460: break;
0461: }
0462: }
0463: } else if (evt instanceof TypeEvent) {
0464: TypeEvent event = (TypeEvent) evt;
0465: Type type = event.getTypeInstance();
0466: switch (event.getType()) {
0467: case ContelligentEvent.TYPE_CHANGED_EVENT:
0468: createOrUpdateType(type, false, cvsCommands,
0469: filesToCommit);
0470: break;
0471: case ContelligentEvent.TYPE_DELETED_EVENT:
0472: deleteType(event.getTypeName(), cvsCommands,
0473: filesToCommit);
0474: break;
0475: case ContelligentEvent.TYPE_CREATION_EVENT:
0476: createOrUpdateType(type, true, cvsCommands,
0477: filesToCommit);
0478: break;
0479: default:
0480: if (debugEnabled)
0481: log.debug("ignoring type-event '" + event
0482: + "' ...");
0483: break;
0484: }
0485: } else if (evt instanceof PrincipalGroupEvent) {
0486: PrincipalGroupEvent event = (PrincipalGroupEvent) evt;
0487: switch (event.getType()) {
0488: case ContelligentEvent.PRINCIPALGROUP_CREATE_EVENT:
0489: createOrUpdatePrincipalGroup(
0490: event.getGroupId(),
0491: event.isUserGroup(), true, cvsCommands,
0492: filesToCommit);
0493: break;
0494: case ContelligentEvent.PRINCIPALGROUP_CHANGE_EVENT:
0495: createOrUpdatePrincipalGroup(
0496: event.getGroupId(),
0497: event.isUserGroup(), false,
0498: cvsCommands, filesToCommit);
0499: break;
0500: case ContelligentEvent.PRINCIPALGROUP_REMOVE_EVENT:
0501: deletePrincipalGroup(event.getGroupId(), event
0502: .isUserGroup(), cvsCommands,
0503: filesToCommit);
0504: break;
0505: default:
0506: if (debugEnabled)
0507: log.debug("ignoring principalgroup-event '"
0508: + event + "' ...");
0509: break;
0510: }
0511: } else {
0512: if (debugEnabled)
0513: log.debug("ignoring event '" + evt + "' ...");
0514: continue;
0515: }
0516: }
0517: return cvsCommands;
0518: } finally {
0519: if (session != null) {
0520: contelligent.invalidateSession(session);
0521: }
0522: }
0523: }
0524:
0525: /**
0526: * Describe <code>deleteComponent</code> method here.
0527: *
0528: * @param componentPath
0529: * a <code>ComponentPath</code> value
0530: * @param filesToCommit
0531: * a <code>List</code> value
0532: * @exception Exception
0533: * if an error occurs
0534: */
0535: private void deleteComponent(ComponentPath componentPath,
0536: List cmdList, List filesToCommit) throws Exception {
0537: String name = componentPath.toString();
0538: File file = new File(componentsDir, name + ".xml");
0539: createRemoveCommand(cmdList, file, false);
0540: filesToCommit.add(file);
0541:
0542: File dir = new File(componentsDir, name);
0543: if (dir.isDirectory()) { // if component is container:
0544: createRemoveCommand(cmdList, dir, true /* cascadeDelete */);
0545: filesToCommit.add(dir);
0546: }
0547: // now handle binaries:
0548: File metaFile = getMetaFile(componentPath);
0549: if (metaFile.exists()) {
0550: File[] filesToRemove = getFilesFromMeta(metaFile);
0551: if (filesToRemove != null) {
0552: createRemoveCommand(cmdList, filesToRemove, false);
0553: filesToCommit.addAll(Arrays.asList(filesToRemove));
0554: }
0555: createRemoveCommand(cmdList, metaFile, false);
0556: filesToCommit.add(metaFile);
0557: }
0558: }
0559:
0560: /**
0561: * Because we don't know which files have changed we must commit every
0562: * single file belonging to the given component. (that is the union of old
0563: * and new files)
0564: *
0565: * @param component
0566: * a <code>Component</code> value
0567: * @param create
0568: * if true component was created rather than updated.
0569: * @param cmdList
0570: * a <code>List</code> value
0571: * @param filesToCommit
0572: * a <code>List</code> value
0573: * @exception Exception
0574: * if an error occurs
0575: */
0576: private void createOrUpdateComponent(Component component,
0577: boolean create, List cmdList, List filesToCommit)
0578: throws Exception {
0579: // log.info("createOrUpdateComponent() - component='"+component+"',
0580: // ctx='"+component.getComponentContext()+"' ...");
0581: ComponentPath componentPath = component.getComponentContext()
0582: .getPath();
0583: Writer out = null;
0584: try {
0585: File file = new File(componentsDir, (componentPath
0586: .toString() + ".xml"));
0587: File parentDir = file.getParentFile();
0588: if (create && parentDir.mkdirs()) {
0589: // because we create the local dir for a container lazy the
0590: // parent dir might be missing:
0591: createAddCommand(cmdList, parentDir, false);
0592: }
0593: out = new BufferedWriter(new OutputStreamWriter(
0594: new FileOutputStream(file), "UTF-8"));
0595: ComponentXMLWriter xmlWriter = new ComponentXMLWriter(out,
0596: xmlPolicy);
0597: Set actualFileNames = xmlWriter.write(component);
0598: if (create) {
0599: createAddCommand(cmdList, file, false);
0600: }
0601: filesToCommit.add(file);
0602:
0603: File metaFile = getMetaFile(componentPath);
0604: final HashSet oldFileNames = create ? null
0605: : readFileNames(metaFile);
0606:
0607: final boolean newFiles = (actualFileNames != null && !actualFileNames
0608: .isEmpty());
0609: final boolean oldFiles = (oldFileNames != null && !oldFileNames
0610: .isEmpty());
0611:
0612: if (oldFiles || newFiles) {
0613: // build union of both sets and add them to filesToCommit
0614: Set toCommit = oldFiles ? (Set) oldFileNames.clone()
0615: : null;
0616: if (newFiles) {
0617: if (toCommit == null)
0618: toCommit = actualFileNames;
0619: else
0620: toCommit.addAll(actualFileNames);
0621: }
0622: File[] ff = convertFileNames(toCommit);
0623: if (ff != null) {
0624: filesToCommit.addAll(Arrays.asList(ff));
0625: }
0626:
0627: if ((actualFileNames == null && oldFileNames != null)
0628: || !actualFileNames.equals(oldFileNames)) {
0629: writeFileNames(metaFile, actualFileNames, cmdList);
0630: filesToCommit.add(metaFile);
0631: } // else there's no need to write meta-file
0632:
0633: // remove obsolete files and add new ones:
0634: Set toRemove = null;
0635: if (oldFiles) {
0636: if (!newFiles) {
0637: toRemove = oldFileNames;
0638: } else {
0639: toRemove = (Set) oldFileNames.clone();
0640: toRemove.removeAll(actualFileNames);
0641: }
0642: }
0643: Set toAdd = actualFileNames;
0644: if (toAdd != null && oldFiles) {
0645: toAdd.removeAll(oldFileNames);
0646: }
0647: File[] filesToRemove = convertFileNames(toRemove);
0648: if (filesToRemove != null) {
0649: createRemoveCommand(cmdList, filesToRemove, false);
0650: }
0651:
0652: // directories and files are already created during export.
0653: // Therefor we must check every
0654: // single directory for existance in the CVS repository!
0655: if (toAdd != null && !toAdd.isEmpty()) {
0656: Iterator filesToAdd = toAdd.iterator();
0657: while (filesToAdd.hasNext()) {
0658: String fileToAdd = (String) filesToAdd.next();
0659: String parent = new File(fileToAdd).getParent();
0660: if (parent != null) {
0661: StringBuffer toCreate = new StringBuffer();
0662: // the filenames stored in .meta do not contain the
0663: // repository name, so we start with an empty file!
0664: StringTokenizer st = new StringTokenizer(
0665: parent, File.separator);
0666: while (st.hasMoreTokens()) {
0667: toCreate.append(File.separatorChar)
0668: .append(st.nextToken());
0669: createAddCommand(cmdList, new File(
0670: filesDir, toCreate.toString()),
0671: false);
0672: }
0673: }
0674: File f = new File(filesDir, fileToAdd);
0675: createAddCommand(cmdList, f, true);
0676: }
0677: }
0678: }
0679: } finally {
0680: if (out != null) {
0681: out.close();
0682: }
0683: }
0684: }
0685:
0686: private void deleteType(String typeName, List cmdList,
0687: List filesToCommit) throws Exception {
0688: File file = new File(typesDir, LocalXMLExport
0689: .typeNameToFileName(typeName));
0690: createRemoveCommand(cmdList, file, false);
0691: filesToCommit.add(file);
0692: }
0693:
0694: private void createOrUpdateType(Type type, boolean create,
0695: List cmdList, List filesToCommit) throws Exception {
0696: TypeXMLWriter xmlWriter = new TypeXMLWriter();
0697: String fileName = LocalXMLExport.typeNameToFileName(type
0698: .getName());
0699: Writer out = null;
0700: try {
0701: File file = new File(typesDir, fileName);
0702: if (create && file.getParentFile().mkdirs()) {
0703: String parent = new File(fileName).getParent(); // !use name
0704: // without
0705: // basedir!
0706: if (parent != null) {
0707: StringBuffer toCreate = new StringBuffer();
0708: StringTokenizer st = new StringTokenizer(parent,
0709: File.separator);
0710: while (st.hasMoreTokens()) {
0711: toCreate.append(File.separatorChar).append(
0712: st.nextToken());
0713: File fileToCreate = new File(typesDir, toCreate
0714: .toString());
0715: createAddCommand(cmdList, fileToCreate, false);
0716: }
0717: }
0718: }
0719:
0720: out = new BufferedWriter(new OutputStreamWriter(
0721: new FileOutputStream(file), "UTF-8"));
0722: xmlWriter.write(type, out, false);
0723:
0724: if (create) {
0725: createAddCommand(cmdList, file, false);
0726: }
0727: filesToCommit.add(file);
0728: } finally {
0729: if (out != null) {
0730: out.close();
0731: }
0732: }
0733: }
0734:
0735: private void deletePrincipalGroup(String groupId,
0736: boolean isUserGroup, List cmdList, List filesToCommit)
0737: throws Exception {
0738: ContelligentSecurityManager securityManager = ContelligentSecurityManager
0739: .getInstance();
0740: String fileName = LocalXMLExport
0741: .principalGroupIdToFileName(groupId);
0742: File baseDir = (isUserGroup ? usersDir : rolesDir);
0743: File file = new File(baseDir, LocalXMLExport
0744: .principalGroupIdToFileName(groupId));
0745: createRemoveCommand(cmdList, file, false);
0746: filesToCommit.add(file);
0747: }
0748:
0749: /**
0750: * XXX: principal-groups are always registered at startup (->create event)
0751: * so the file may already exist. Therefor, if a local file exists for the
0752: * given group we assume the it has been added already.
0753: *
0754: * @param groupId
0755: * a <code>String</code> value
0756: * @param isUserGroup
0757: * a <code>boolean</code> value
0758: * @param create
0759: * a <code>boolean</code> value
0760: * @exception Exception
0761: * if an error occurs
0762: */
0763: private void createOrUpdatePrincipalGroup(String groupId,
0764: boolean isUserGroup, boolean create, List cmdList,
0765: List filesToCommit) throws Exception {
0766: ContelligentSecurityManager securityManager = ContelligentSecurityManager
0767: .getInstance();
0768: String fileName = LocalXMLExport
0769: .principalGroupIdToFileName(groupId);
0770: File baseDir = (isUserGroup ? usersDir : rolesDir);
0771: Writer writer = null;
0772: try {
0773: File file = new File(baseDir, fileName);
0774: boolean mustCreate = (create && !file.exists());
0775: writer = new BufferedWriter(new OutputStreamWriter(
0776: new FileOutputStream(file), "UTF-8"));
0777: securityManager.exportPrincipalData(groupId, writer);
0778: writer.close();
0779: if (mustCreate) {
0780: createAddCommand(cmdList, file, false);
0781: }
0782: filesToCommit.add(file);
0783: } finally {
0784: if (writer != null) {
0785: try {
0786: writer.close();
0787: } catch (Exception e) {
0788: log
0789: .warn(
0790: "exportPrincipalGroup() - exception while closing writer: ",
0791: e);
0792: }
0793: }
0794: }
0795: }
0796:
0797: private File getMetaFile(ComponentPath path) {
0798: return new File(componentsDir, (path.toString() + ".meta"));
0799: }
0800:
0801: private HashSet readFileNames(File metaFile) throws Exception {
0802: if (!metaFile.exists()) {
0803: return null;
0804: } else {
0805: HashSet oldFileNames = new HashSet();
0806: BufferedReader reader = null;
0807: try {
0808: reader = new BufferedReader(new InputStreamReader(
0809: new FileInputStream(metaFile), "UTF-8"));
0810: String line = null;
0811: while ((line = reader.readLine()) != null) {
0812: oldFileNames.add(line);
0813: }
0814: } finally {
0815: if (reader != null) {
0816: reader.close();
0817: }
0818: }
0819: return oldFileNames;
0820: }
0821: }
0822:
0823: private void writeFileNames(File metaFile, Set fileNames,
0824: List cmdList) throws Exception {
0825: boolean metaExists = metaFile.exists();
0826: if (fileNames == null || fileNames.isEmpty()) {
0827: if (metaExists)
0828: createRemoveCommand(cmdList, metaFile, false);
0829: } else {
0830: BufferedWriter writer = null;
0831: try {
0832: writer = new BufferedWriter(new OutputStreamWriter(
0833: new FileOutputStream(metaFile), "UTF-8"));
0834: Iterator names = fileNames.iterator();
0835: while (names.hasNext()) {
0836: writer.write((String) names.next());
0837: writer.newLine();
0838: }
0839: writer.flush();
0840: if (!metaExists)
0841: createAddCommand(cmdList, metaFile, false);
0842: } finally {
0843: if (writer != null) {
0844: writer.close();
0845: }
0846: }
0847: }
0848: }
0849:
0850: private File[] convertFileNames(Set localNames) {
0851: if (localNames == null || localNames.isEmpty()) {
0852: return null;
0853: } else {
0854: File[] files = new File[localNames.size()];
0855: int i = 0;
0856: Iterator it = localNames.iterator();
0857: while (it.hasNext()) {
0858: files[i++] = new File(filesDir, (String) it.next());
0859: }
0860: return files;
0861: }
0862: }
0863:
0864: private File[] getFilesFromMeta(File metaFile) throws Exception {
0865: return convertFileNames(readFileNames(metaFile));
0866: }
0867:
0868: private void createAddCommand(List cmdList, File file,
0869: boolean isBinary) throws Exception {
0870: if (log.isDebugEnabled()) {
0871: log.debug("creating add-command for "
0872: + (isBinary ? "binary" : "text") + "-file '" + file
0873: + "' ...");
0874: }
0875: Command lastCmd = (cmdList.isEmpty() ? null : (Command) cmdList
0876: .get(cmdList.size() - 1));
0877: if (lastCmd instanceof AddCommand) {
0878: File[] oldFiles = ((AddCommand) lastCmd).getFiles();
0879: int len = oldFiles.length;
0880: File[] newFiles = new File[len + 1];
0881: System.arraycopy(oldFiles, 0, newFiles, 0, len);
0882: newFiles[len] = file;
0883: ((AddCommand) lastCmd).setFiles(newFiles);
0884: } else {
0885: AddCommand cmd = new AddCommand();
0886: cmd.setBuilder(null);
0887: cmd.setFiles(new File[] { file });
0888: if (isBinary) {
0889: cmd.setKeywordSubst(KeywordSubstitutionOptions.BINARY); // -kb
0890: } else {
0891: cmd
0892: .setKeywordSubst(KeywordSubstitutionOptions.OLD_VALUES); // -ko
0893: }
0894: cmdList.add(cmd);
0895: }
0896: }
0897:
0898: private void createRemoveCommand(List cmdList, File file,
0899: boolean cascadeDelete) throws Exception {
0900: createRemoveCommand(cmdList, new File[] { file }, cascadeDelete);
0901: }
0902:
0903: private void createRemoveCommand(List cmdList, File[] toRemove,
0904: boolean cascadeDelete) throws Exception {
0905: if (log.isDebugEnabled()) {
0906: log.debug("creating remove-command for files '"
0907: + StringUtils.createCSV(Arrays.asList(toRemove),
0908: ',') + "' ...");
0909: }
0910: Command lastCmd = (cmdList.isEmpty() ? null : (Command) cmdList
0911: .get(cmdList.size() - 1));
0912: if ((lastCmd instanceof RemoveCommand)
0913: && (((RemoveCommand) lastCmd).isRecursive() == cascadeDelete)) {
0914: File[] oldFiles = ((RemoveCommand) lastCmd).getFiles();
0915: int len = oldFiles.length;
0916: File[] newFiles = new File[len + toRemove.length];
0917: System.arraycopy(oldFiles, 0, newFiles, 0, len);
0918: System.arraycopy(toRemove, 0, newFiles, len,
0919: toRemove.length);
0920: ((RemoveCommand) lastCmd).setFiles(newFiles);
0921: } else {
0922: RemoveCommand cmd = new RemoveCommand();
0923: cmd.setDeleteBeforeRemove(true);
0924: cmd.setBuilder(null);
0925: cmd.setRecursive(cascadeDelete);
0926: cmd.setFiles(toRemove);
0927: cmdList.add(cmd);
0928: }
0929: }
0930:
0931: private Connection getConnection() throws Exception {
0932: PServerConnection con = new PServerConnection();
0933: con.setUserName(user);
0934: con.setHostName(host);
0935: if (port != PServerConnection.DEFAULT_PORT) {
0936: con.setPort(port);
0937: }
0938: con.setRepository(repository);
0939: con.setEncodedPassword(encodedPassword);
0940: return con;
0941: }
0942:
0943: private String commandToString(Command cmd) {
0944: if (cmd instanceof RemoveCommand) {
0945: return ((RemoveCommand) cmd).getCVSCommand();
0946: } else if (cmd instanceof AddCommand) {
0947: return ((AddCommand) cmd).getCVSCommand();
0948: } else if (cmd instanceof CommitCommand) {
0949: return ((CommitCommand) cmd).getCVSCommand();
0950: } else if (cmd instanceof CheckoutCommand) {
0951: return ((CheckoutCommand) cmd).getCVSCommand();
0952: } else if (cmd instanceof UpdateCommand) {
0953: return ((UpdateCommand) cmd).getCVSCommand();
0954: } else {
0955: return cmd.toString();
0956: }
0957: }
0958:
0959: String contentsOfCVSEntries(File file) throws Exception {
0960: File cvsEntries = new File(file.getParentFile(), "CVS/Entries");
0961: if (cvsEntries.exists()) {
0962: StringBuffer sb = new StringBuffer();
0963: BufferedReader in = null;
0964: try {
0965: in = new BufferedReader(new InputStreamReader(
0966: new FileInputStream(cvsEntries)));
0967: String line = null;
0968: while ((line = in.readLine()) != null) {
0969: sb.append(line).append('\n');
0970: }
0971: } finally {
0972: in.close();
0973: }
0974: return sb.toString();
0975: } else {
0976: return "";
0977: }
0978: }
0979:
0980: // --------------------------------------------------------------------------------
0981: // methods of interface 'de.finix.contelligent.event.CVSListener'
0982: // --------------------------------------------------------------------------------
0983:
0984: /**
0985: * Called when the server wants to send a message to be displayed to the
0986: * user. The message is only for information purposes and clients can choose
0987: * to ignore these messages if they wish.
0988: *
0989: * @param e
0990: * the event
0991: */
0992: public void messageSent(MessageEvent e) {
0993: String msg = e.getMessage();
0994: if (e.isTagged()) {
0995: StringBuffer sb = new StringBuffer();
0996: msg = MessageEvent.parseTaggedMessage(sb, msg);
0997: }
0998: if (log.isDebugEnabled()) {
0999: log.debug(msg);
1000: }
1001: /*
1002: * XXX: Unfortunately some responses of the server are marked as error
1003: * although they aren't, for example if we add a file the server
1004: * responds 'cvs server: scheduling file `...' for addition' and this is
1005: * marked as error. Therefor we log any message as debug. (2003/5/9, rs)
1006: * if (e.isError()) { log.error(msg); } else if (log.isDebugEnabled()) {
1007: * log.debug(msg); }
1008: */
1009: // append every message to global buffer
1010: messages.append(msg).append('\n');
1011: }
1012:
1013: /**
1014: * Called when a file has been added.
1015: *
1016: * @param e
1017: * the event
1018: */
1019: public void fileAdded(FileAddedEvent e) {
1020: if (log.isDebugEnabled())
1021: log.debug("file '" + e.getFilePath() + "' added.");
1022: }
1023:
1024: /**
1025: * Called when a file is removed.
1026: *
1027: * @param e
1028: * the event
1029: */
1030: public void fileRemoved(FileRemovedEvent e) {
1031: // XXX: FileRemovedEvent has no info!
1032: }
1033:
1034: /**
1035: * Called when a file has been updated.
1036: *
1037: * @param e
1038: * the event
1039: */
1040: public void fileUpdated(FileUpdatedEvent e) {
1041: if (log.isDebugEnabled())
1042: log.debug("file '" + e.getFilePath() + "' updated.");
1043: }
1044:
1045: /**
1046: * Called when file status information has been received
1047: */
1048: public void fileInfoGenerated(FileInfoEvent e) {
1049: if (log.isDebugEnabled()) {
1050: FileInfoContainer fileInfo = e.getInfoContainer();
1051: log.debug("file-info: " + fileInfo);
1052: }
1053: }
1054:
1055: /**
1056: * called when server responses with "ok" or "error", (when the command
1057: * finishes)
1058: */
1059: public void commandTerminated(TerminationEvent e) {
1060: lastCommandHadErrors = e.isError();
1061: if (lastCommandHadErrors) {
1062: log.error("commandTerminated() - " + e + " (isError=true)");
1063: } else {
1064: if (log.isDebugEnabled()) {
1065: log.debug("commandTerminated() - " + e);
1066: }
1067: }
1068: }
1069:
1070: /**
1071: * Fire a module expansion event. This is called when the servers has
1072: * responded to an expand-modules request.
1073: */
1074: public void moduleExpanded(ModuleExpansionEvent e) {
1075: if (log.isDebugEnabled())
1076: log.debug("moduleExpanded() - " + e);
1077: }
1078:
1079: }
|