P4ChangeParser.java #21

  • //
  • guest/
  • perforce_software/
  • p4jenkins/
  • main/
  • src/
  • main/
  • java/
  • org/
  • jenkinsci/
  • plugins/
  • p4/
  • changes/
  • P4ChangeParser.java
  • View
  • Commits
  • Open Download .zip Download (8 KB)
package org.jenkinsci.plugins.p4.changes;

import com.perforce.p4java.core.IChangelistSummary;
import com.perforce.p4java.core.IFix;
import com.perforce.p4java.core.file.FileAction;
import com.perforce.p4java.exception.P4JavaException;
import com.perforce.p4java.exception.RequestException;
import com.perforce.p4java.impl.generic.core.Fix;
import hudson.model.Run;
import hudson.scm.ChangeLogParser;
import hudson.scm.ChangeLogSet;
import hudson.scm.ChangeLogSet.Entry;
import hudson.scm.RepositoryBrowser;
import org.jenkinsci.plugins.p4.browsers.SwarmBrowser;
import org.jenkinsci.plugins.p4.client.ConnectionHelper;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import java.util.logging.Logger;

/**
 * Uses the "index.jelly" view to render the changelist details and use the
 * "digest.jelly" view of to render the summary page.
 */
public class P4ChangeParser extends ChangeLogParser {

	private static Logger logger = Logger.getLogger(P4ChangeParser.class.getName());

	private final String credential;

	public P4ChangeParser(String credential) {
		this.credential = credential;
	}

	@SuppressWarnings("rawtypes")
	@Override
	public ChangeLogSet<? extends Entry> parse(Run run, RepositoryBrowser<?> browser, File file)
			throws IOException, SAXException {
		try (ConnectionHelper p4 = new ConnectionHelper(run, credential, null)) {
			SAXParserFactory factory = SAXParserFactory.newInstance();
			factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
			factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
			factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
			SAXParser parser = factory.newSAXParser();
			ChangeLogHandler handler = new ChangeLogHandler(run, browser, p4);
			parser.parse(file, handler);
			P4ChangeSet changeSet = handler.getChangeLogSet();
			return changeSet;
		} catch (Exception e) {
			logger.severe("Could not parse Perforce changelog: " + file.toString());
		}
		return new P4ChangeSet(run, browser, new ArrayList<>());
	}

	public static class ChangeLogHandler extends DefaultHandler {
		private Stack<P4ChangeEntry> objects = new Stack<P4ChangeEntry>();
		private StringBuffer text = new StringBuffer();

		private List<P4ChangeEntry> changeEntries;
		private P4ChangeSet changeSet;
		private Run<?, ?> run;
		private RepositoryBrowser<?> browser;
		private ConnectionHelper p4;

		public ChangeLogHandler(Run<?, ?> run, RepositoryBrowser<?> browser, ConnectionHelper p4) throws P4JavaException {
			this.run = run;
			this.browser = browser;
			this.p4 = p4;

			if (browser == null) {
				try {
					String url = p4.getSwarm();
					if (url != null) {
						this.browser = new SwarmBrowser(url);
					}
				} catch (RequestException re) {
					if (re.getMessage() != null && !re.getMessage().contains("Unknown command")) {
						throw re;
					}
					// else : Ignore, the command is not supported by older P4 versions
				}
			}
		}

		@Override
		public void characters(char[] ch, int start, int length) throws SAXException {
			text.append(ch, start, length);
		}

		@Override
		public void startDocument() throws SAXException {
			changeEntries = new ArrayList<P4ChangeEntry>();
			changeSet = new P4ChangeSet(run, browser, changeEntries);
		}

		@Override
		public void endDocument() throws SAXException {
		}

		@Override
		public void startElement(String uri, String localName, String qName, Attributes attributes)
				throws SAXException {

			if (qName.equalsIgnoreCase("changelog")) {
				// this is the root, so don't do anything
				text.setLength(0);
				return;
			}
			if (qName.equalsIgnoreCase("entry")) {
				objects.push(new P4ChangeEntry(changeSet));
				text.setLength(0);
				return;
			}
			if (objects.peek() instanceof P4ChangeEntry) {
				P4ChangeEntry entry = (P4ChangeEntry) objects.peek();
				try {
					if (qName.equalsIgnoreCase("file")) {

						// URL decode depot path
						String safePath = attributes.getValue("depot");
						String depotPath = URLDecoder.decode(safePath, "UTF-8");
						String a = attributes.getValue("action");
						//Replacement of / is already done at this point. No need to call the FileAction.fromString(a);
						FileAction action = FileAction.valueOf(a);
						String strRev = attributes.getValue("endRevision");

						P4AffectedFile file = new P4AffectedFile(depotPath, strRev, action);
						entry.addAffectedFiles(file);

						///entry.files.add(temp);
						text.setLength(0);
						return;
					}

					if (qName.equalsIgnoreCase("job")) {
						IFix temp = new Fix();

						String id = attributes.getValue("id");
						temp.setJobId(id);

						String status = attributes.getValue("status");
						temp.setStatus(status);

						entry.addJob(temp);
						text.setLength(0);
						return;
					}
				} catch (UnsupportedEncodingException e) {
					entry = null;
				}
			}

			text.setLength(0);
		}

		@Override
		public void endElement(String uri, String localName, String qName) throws SAXException {

			if (qName.equalsIgnoreCase("changelog")) {
				// this is the root, so don't do anything
				return;
			}

			if (qName.equalsIgnoreCase("entry")) {
				P4ChangeEntry entry = (P4ChangeEntry) objects.pop();
				changeEntries.add(entry);
				return;
			}

			// if we are in the entry element
			if (objects.peek() instanceof P4ChangeEntry) {
				P4ChangeEntry entry = (P4ChangeEntry) objects.peek();
				try {

					if (text.toString().trim().length() != 0
							&& (qName.equalsIgnoreCase("changenumber")
							|| qName.equalsIgnoreCase("label")
							|| qName.equalsIgnoreCase("commit"))) {

						// Add changelist to entry
						if (qName.equalsIgnoreCase("changenumber")) {
							int id = Integer.parseInt(text.toString());
							IChangelistSummary summary = p4.getChangeSummary(id);
							entry.setChange(p4, summary);
						}

						// Add label to entry
						if (qName.equalsIgnoreCase("label")) {
							String id = text.toString();
							entry.setLabel(p4, id);
						}

						// Add Commit to entry
						if (qName.equalsIgnoreCase("commit")) {
							String id = text.toString();
							entry.setGraphCommit(p4, id);
						}
					} else {

						String elementText = text.toString().trim();

						if (qName.equalsIgnoreCase("changeInfo")) {
							if (elementText.contains("@")) {
								entry.setId(new P4GraphRef(p4, elementText));
								text.setLength(0);
								return;
							} else {
								int id = Integer.parseInt(elementText);
								entry.setId(new P4ChangeRef(id));
								text.setLength(0);
								return;
							}
						}

						if (qName.equalsIgnoreCase("shelved")) {
							entry.setShelved(elementText.equals("true"));
							text.setLength(0);
							return;
						}

						if (qName.equalsIgnoreCase("fileLimit")) {
							entry.setFileLimit(elementText.equals("true"));
							text.setLength(0);
							return;
						}

						if (qName.equalsIgnoreCase("msg")) {
							entry.setMsg(elementText);
							text.setLength(0);
							return;
						}

						if (qName.equalsIgnoreCase("clientId")) {
							entry.setClientId(elementText);
							text.setLength(0);
							return;
						}

						if (qName.equalsIgnoreCase("changeUser")) {
							entry.setAuthor(elementText);
							text.setLength(0);
							return;
						}

						if (qName.equalsIgnoreCase("changeTime")) {
							entry.setDate(elementText);
							text.setLength(0);
							return;
						}
					}

					text.setLength(0);
					return;
				} catch (Exception e) {
					entry = null;
				}
			}
		}

		public P4ChangeSet getChangeLogSet() {
			return changeSet;
		}
	}
}
# Change User Description Committed
#21 29673 Sandeep Kumar Merge pull request #188 from jenkinsci/CodeRefactoring

Code Cleanup
#20 28784 Paul Allen Merge pull request #140 from jorgenpt/fix-file-limit-not-persisting

Persist `fileLimit` when serializing a changeset
#19 24938 Paul Allen Prevent XML parse errors from failing the build.

Jelly page is called while the changelist is being built.  Log the error and return an empty list of changes.  JENKINS-53030
#18 24937 Paul Allen Merge pull request #84 from p4charu/jenkinsci-master

Added help text for Include and Fixed incorrect images. JENKINS-54584
#17 24487 Paul Allen Perforce Connection Refactor.
#16 23478 Paul Allen Merge pull request #65 from PhRX/jenkins-44845

JENKINS-48845 : Backward compatibility for pre-swarm P4 releases
#15 22970 Paul Allen Merge pull request #57 from jfperusse-bhvr/fix-changelog-errors

Fix changelog issues caused by NullPointerException in getSwarm
JENKINS-47336
#14 22856 Paul Allen Remove guessBrowser() and lookup browser when used.

guessBrowser() gets called a lot and each lookup opens a Perforce connection. JENKINS-46810
#13 22128 Paul Allen Helix Graph support for Jenkins.

JENKINS-40354
#12 21794 Paul Allen Merge pull request #39 from Dohbedoh/JENKINS-34825

Jenkins 34825
#11 19581 Paul Allen Minor fixes to satisfy FindBugs Analysis.
#10 16409 Paul Allen Use Fix records for Job information.

'p4 fixes' is lighter and still provides enough useful data.  Includes jelly fix and extra job data in changelog.xml.
#9 16390 Paul Allen Update change/file reporting.

Use `p4 changes @=1234` to get change summary and `p4 files -m50 @=1234` to get limited file list.
#8 15750 Paul Allen Use a P4Revision object and not int/String as Object.

In sure that there is no ambiguity with the revision specifier. Should fix change summary when using the Workflow plugin.

JENKINS-30425
#7 14150 Paul Allen URL Encode/Decode the depot path for changes.

Filenames with ampersands was causing Jelly to break when showing the change detail.

JENKINS-29017
#6 11626 Paul Allen Workflow support.

 - Updated the plugin to the latest LTS Jenkins release 1.580.3.
 - Updated P4Java to 2014.1

Tested with simple ‘static’ workspace, there may be limitations with ‘manual’ workspace.  Plan to add DSL support by extending the SCMStep class.

#review-11537

JENKINS-24206
#5 11084 Paul Allen (mjoubert) Fully populate changelog.xml

Replaces sparse change log which only recorded the change number.  The change is now recorded in full allowing faster page load times. 

The number of files listed in the change is limited to 50; to view larger changes please add a repo browser such as Swarm, Fisheye, P4Web, etc…
#4 10201 Paul Allen Fix page loading error with no connection to P4
#3 9803 Paul Allen Merging using p4-jenkins
#2 9738 Paul Allen Merging using p4-jenkins
#1 9690 Paul Allen [Branching using p4-jenkins]
Release 1.0.1
//guest/paul_allen/dev/p4-jenkins/p4-client/src/main/java/org/jenkinsci/plugins/p4/changes/P4ChangeParser.java
#1 9672 Paul Allen Refactor name from 'p4_client' to 'p4'.
//guest/paul_allen/dev/p4-jenkins/p4-client/src/main/java/org/jenkinsci/plugins/p4_client/changes/P4ChangeParser.java
#3 9055 Paul Allen Label support.

Build at a label using the pram 'label'.  This includes adding the label to the ChangeEntry, building the change reports and Browser links to Swarm. (TPI-102)
#2 8940 Paul Allen Major refactor for the ConnectionHelper class to simplify serialisation.
  Fixed remote
Jenkins JNLP slave connection issue.

ClientHelper now extends ConnectionHelper and takes on all methods that require a client
workspace.  P4StandardCredentials is sent to the remote node instead of Credentials ID
due to an issue accessing the Credentials store over a remote connection.  For simplicity
Client ID (workspace name) is serialised instead of the Workspace object.
#1 8915 Paul Allen Support for ChangeLog and RepoBrowser.

 - Added RepoBrowser for Swarm (porting the others should be easy)
 - ChangeLog XML file now only stores the changelist number all other information is fetched from Perforce