package com.perforce.spark.artifact; import java.io.File; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.io.FileUtils; import org.apache.commons.pool.impl.GenericObjectPool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import spark.Request; import spark.Response; import com.perforce.p4java.client.IClient; import com.perforce.p4java.core.IChangelist; import com.perforce.p4java.core.file.FileSpecBuilder; import com.perforce.p4java.core.file.FileSpecOpStatus; import com.perforce.p4java.core.file.IFileSpec; import com.perforce.p4java.exception.P4JavaException; import com.perforce.p4java.impl.generic.client.ClientView; import com.perforce.p4java.impl.generic.client.ClientView.ClientViewMapping; import com.perforce.p4java.impl.generic.core.Changelist; import com.perforce.p4java.impl.mapbased.client.Client; import com.perforce.p4java.option.changelist.SubmitOptions; import com.perforce.p4java.option.client.ReconcileFilesOptions; import com.perforce.p4java.option.client.ReopenFilesOptions; import com.perforce.p4java.option.client.SyncOptions; import com.perforce.p4java.option.server.OpenedFilesOptions; import com.perforce.p4java.server.IOptionsServer; import com.perforce.spark.common.ClientFactory; import com.perforce.spark.connection.ConnectionConfig; import com.perforce.spark.connection.ConnectionSession; import com.perforce.spark.deploy.ChangeModel; import com.perforce.spark.depot.DepotModel; public abstract class DefaultArtifact implements ArtifactInterface { private static Logger logger = LoggerFactory .getLogger(DefaultArtifact.class); private static String PATH_ID = "PAM Artifact:"; private static GenericObjectPool<IClient> pool = new GenericObjectPool<>( new ClientFactory(), ConnectionConfig.getProxyPool()); protected final IOptionsServer p4; protected String repoID; public DefaultArtifact(Request request, Response response) throws P4JavaException, FileUploadException { this.p4 = ConnectionSession.get(request, response); } public DefaultArtifact(IOptionsServer p4) { this.p4 = p4; } /** * Implementation processes the content of the multipart form, building a * list of the files to upload and any parameters used to generate the path. * * @param request * @throws FileUploadException */ public abstract void processForm(Request request) throws FileUploadException; /** * Provide the location of upload directory, used to set Perforce Workspace * root. * * @return Workspace root */ public abstract Path getPath(); /** * Provide the root location of the index. A Perforce worksapce root with * all the index files. * * @return */ public abstract DepotModel getDepot() throws P4JavaException; /** * Provide the files from web client into the workspace root. * * @param root * @return * @throws Exception */ public abstract List<String> populateWorkspace(File root) throws Exception; /** * Build the index, updating the files in the workspace. * * @throws Exception */ public abstract void buildIndex() throws Exception; @Override public void proxyUpload() throws Exception { IClient client = pool.borrowObject(); try { int pending = upload(client.getName()); userSubmit(pending, "Submitted by PAM Proxy."); } catch (Exception e) { throw e; } finally { pool.returnObject(client); } } @Override public int userUpload(Request request) throws Exception { processForm(request); IClient client = newClient(); File root = new File(client.getRoot()); if (root.exists()) { FileUtils.deleteDirectory(root); } if (!root.exists() && !root.mkdirs()) { logger.warn("Failed to create upload directory."); } int pending = upload(client.getName()); return pending; } private int upload(String clientName) throws Exception { // reload client (pool uses a different p4 connection) IClient iclient = p4.getClient(clientName); p4.setCurrentClient(iclient); // set client view mappings mapClient(iclient); // upload files to workspace File root = new File(iclient.getRoot()); List<String> upload = populateWorkspace(root); logger.info("uploaded(" + upload.size() + "): " + root); // create new pending change and add description IChangelist change = new Changelist(); change.setDescription(PATH_ID + getPath()); change = iclient.createChangelist(change); // add files to pending change String[] array = upload.stream().toArray(String[]::new); List<IFileSpec> files = FileSpecBuilder.makeFileSpecList(array); // virtual sync -k files SyncOptions syncOpts = new SyncOptions(); syncOpts.setClientBypass(true); iclient.sync(files, syncOpts); // reconcile files ReconcileFilesOptions statusOpts = new ReconcileFilesOptions(); statusOpts.setOutsideAdd(true); statusOpts.setOutsideEdit(true); statusOpts.setChangelistId(change.getId()); List<IFileSpec> statusSpec = iclient.reconcileFiles(files, statusOpts); validateFileSpecs(statusSpec); return change.getId(); } private IClient newClient() throws P4JavaException { String user = p4.getUserName(); String clientName = ConnectionConfig.getClientPrefix() + user; IClient iclient = p4.getClient(clientName); // clean up and delete if (iclient != null) { ClientFactory.clean(p4, iclient); p4.deleteClient(iclient.getName(), false); } // create new workspace Client implClient = new Client(p4); implClient.setName(clientName); p4.createClient(implClient); iclient = p4.getClient(clientName); iclient.setOwnerName(user); File root = new File("upload", clientName); iclient.setRoot(root.getAbsolutePath()); iclient.update(); return iclient; } private void mapClient(IClient iclient) throws Exception { // create LHS 'depot' mapping Path depotPath = getPath(); String lhs = "//" + depotPath + "/..."; // create RHS 'client' mapping String rhs = "//" + iclient.getName() + "/..."; // add view mapping ClientView clientView = new ClientView(); String line = lhs + " " + rhs; ClientViewMapping entry = new ClientViewMapping(0, line); clientView.addEntry(entry); iclient.setClientView(clientView); iclient.update(); logger.info("workspace: " + iclient.getName()); logger.info("... mapping: " + lhs); } @Override public ChangeModel userSubmit(int id, String desc) throws Exception { // get current workspace (from pending change) IChangelist change = p4.getChangelist(id); String clientName = change.getClientId(); IClient iclient = p4.getClient(clientName); p4.setCurrentClient(iclient); // !HACK! extract path and repoID from pending description String path = change.getDescription(); path = path.replaceAll("(\\r|\\n)", ""); path = path.substring(PATH_ID.length()); String[] parts = path.split("/"); repoID = parts[0]; change.setDescription(desc); // submit pending change SubmitOptions submitOpts = new SubmitOptions(); List<IFileSpec> submitted = change.submit(submitOpts); validateFileSpecs(submitted, "Submitted as change"); logger.info("submitted."); long c = findSubmittedChange(submitted); ChangeModel changeModel = new ChangeModel(path, c); return changeModel; } private long findSubmittedChange(List<IFileSpec> submitted) { long change = 0; for (IFileSpec spec : submitted) { if (spec.getOpStatus() != FileSpecOpStatus.VALID) { String msg = spec.getStatusMessage(); String cng = "Submitted as change "; if (msg.startsWith(cng)) { try { String id = msg.substring(cng.length()); change = Long.parseLong(id); } catch (NumberFormatException e) { change = -1; } } } } return change; } protected void versionIndex() throws Exception { // fetch depot from implementation DepotModel depot = getDepot(); File root = new File("index", depot.getName()); String clientName = ConnectionConfig.getClientPrefix(); clientName += depot.getName() + ".index"; logger.info("index with client: " + clientName); // create/setup workspace IClient iclient = p4.getClient(clientName); Client implClient = new Client(p4); implClient.setName(clientName); p4.createClient(implClient); iclient = p4.getClient(clientName); iclient.setRoot(root.getAbsolutePath()); iclient.setOwnerName(p4.getUserName()); // create LHS 'depot' mapping String lhs = depot.getPath() + "/..."; // create RHS 'client' mapping String rhs = "//" + clientName + "/..."; // add view mapping ClientView clientView = new ClientView(); String line = lhs + " " + rhs; ClientViewMapping entry = new ClientViewMapping(0, line); clientView.addEntry(entry); iclient.setClientView(clientView); iclient.update(); // select workspace p4.setCurrentClient(iclient); // build file revision spec String ws = "//" + iclient.getName() + "/..."; List<IFileSpec> files = FileSpecBuilder.makeFileSpecList(ws); // sync files SyncOptions syncOpts = new SyncOptions(); List<IFileSpec> syncSpec = iclient.sync(files, syncOpts); validateFileSpecs(syncSpec, "file(s) up-to-date"); // call implementation index here to update files buildIndex(); // add files to default pending change ReconcileFilesOptions statusOpts = new ReconcileFilesOptions(); statusOpts.setOutsideAdd(true); statusOpts.setOutsideEdit(true); List<IFileSpec> statusSpec = iclient.reconcileFiles(files, statusOpts); validateFileSpecs(statusSpec, "no file(s) to reconcile"); if (isOpened(iclient)) { // create new pending change and add description IChangelist change = new Changelist(); change.setDescription("Indexing:" + getPath()); change = iclient.createChangelist(change); // move files from default change ReopenFilesOptions reopenOpts = new ReopenFilesOptions(); reopenOpts.setChangelistId(change.getId()); iclient.reopenFiles(files, reopenOpts); // submit pending change SubmitOptions submitOpts = new SubmitOptions(); List<IFileSpec> submitted = change.submit(submitOpts); validateFileSpecs(submitted, "Submitted as change"); logger.info("indexed updated."); } else { logger.info("indexed unchanged."); } } private boolean isOpened(IClient iclient) throws Exception { String ws = "//" + iclient.getName() + "/..."; List<IFileSpec> files = FileSpecBuilder.makeFileSpecList(ws); OpenedFilesOptions openOps = new OpenedFilesOptions(); List<IFileSpec> open = iclient.openedFiles(files, openOps); for (IFileSpec file : open) { if (file != null && file.getAction() != null) { return true; } } return false; } @Override public void disconnect() throws Exception { p4.disconnect(); } /** * Look for a message in the returned FileSpec from operation. * * @param fileSpecs * @param ignore * @return * @throws ConverterException */ private static void validateFileSpecs(List<IFileSpec> fileSpecs, String... ignore) throws Exception { boolean abort = false; ArrayList<String> ignoreList = new ArrayList<String>(); ignoreList.addAll(Arrays.asList(ignore)); for (IFileSpec fileSpec : fileSpecs) { FileSpecOpStatus status = fileSpec.getOpStatus(); if (status != FileSpecOpStatus.VALID) { String msg = fileSpec.getStatusMessage(); // superfluous p4java message boolean unknownMsg = true; for (String istring : ignoreList) { if (msg.contains(istring)) { // its a known message unknownMsg = false; break; } } // check and report unknown message if (unknownMsg) { logger.warn("P4JAVA: " + msg); if (status == FileSpecOpStatus.ERROR || status == FileSpecOpStatus.CLIENT_ERROR) { abort = true; } } } } if (abort) { String msg = "P4JAVA: Error(s)"; throw new P4JavaException(msg); } } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#2 | 15782 | Paul Allen |
Update to UX - deploy has 3 process steps (including a submit report) |
||
#1 | 15313 | Paul Allen |
Added Client Workspace pool to manage parallel upload requests by the proxy (configuration set to 50). User uploads are restricted to only one per user. |
||
//guest/paul_allen/p4am/src/main/java/com/perforce/spark/artifact/Artifact.java | |||||
#8 | 15232 | Paul Allen | Fix on Artifact indexing | ||
#7 | 15221 | Paul Allen | Generate Gem index and submit after a deploy or proxy fetch event. | ||
#6 | 15218 | Paul Allen |
Index/catalog for Ruby. Plus depot names must be lower-case. |
||
#5 | 15215 | Paul Allen | Added RubyGem pages and tidy up. | ||
#4 | 15094 | Paul Allen | Added support for Proxy and Group. | ||
#3 | 14887 | Paul Allen | Fix: check client exists before delete. | ||
#2 | 14884 | Paul Allen | Moved create depot type into path and not a param. | ||
#1 | 14883 | Paul Allen | Submit via preview and redirect to asset | ||
//guest/paul_allen/p4am/src/main/java/com/perforce/spark/AbstractAsset.java | |||||
#7 | 14863 | Paul Allen | Change File/Dir browse to support extended options | ||
#6 | 14742 | Paul Allen | update - still issues with upload. | ||
#5 | 14741 | Paul Allen | Fix upload for multiple files. | ||
#4 | 14228 | Paul Allen |
Moved View over to the new model. Retactor connection to package. |
||
#3 | 14181 | Paul Allen | Lots of updates and refactoring. | ||
#2 | 14017 | Paul Allen | Proxy caching to Perforce and SLF4J logging. | ||
#1 | 14013 | Paul Allen | Basic proxy |