diff --git a/README.md b/README.md index 0551134a0..afa34234e 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ In this configuration, we'll run the direct app in a docker container locally bu ## Test Users -direct_user/topcoder2001 (Use this user to login to Direct and create challenges in the Topcoder DEV environment. You can alsoo use this user to manipulate challenges in Online Review) +direct_user/topcoder2001 (Use this user to login to Direct and create challenges in the Topcoder DEV environment. You can alsoo use this user to manipulate challenges in Online Review). ## **old** instructions diff --git a/components/project_management/src/java/main/com/topcoder/management/project/persistence/AbstractInformixProjectPersistence.java b/components/project_management/src/java/main/com/topcoder/management/project/persistence/AbstractInformixProjectPersistence.java index 2973ea31c..91db63b06 100644 --- a/components/project_management/src/java/main/com/topcoder/management/project/persistence/AbstractInformixProjectPersistence.java +++ b/components/project_management/src/java/main/com/topcoder/management/project/persistence/AbstractInformixProjectPersistence.java @@ -2549,7 +2549,7 @@ public abstract class AbstractInformixProjectPersistence implements ProjectPersi */ private static final String INSERT_MM_ROUND_SQL = "INSERT INTO informixoltp:round(round_id, contest_id, name, status, " + "registration_limit, invitational, round_type_id, short_name, rated_ind) " - + "VALUES (?, ?, ?, 'F', 1024, 0, 13, ?, 0)"; + + "VALUES (?, ?, ?, 'F', 1024, 0, 13, ?, 1)"; /** * Sql statement for updating MM round diff --git a/services/contest_service_facade/src/java/main/com/topcoder/service/facade/contest/ejb/ContestServiceFacadeBean.java b/services/contest_service_facade/src/java/main/com/topcoder/service/facade/contest/ejb/ContestServiceFacadeBean.java index 3cb3f57b2..662616d55 100644 --- a/services/contest_service_facade/src/java/main/com/topcoder/service/facade/contest/ejb/ContestServiceFacadeBean.java +++ b/services/contest_service_facade/src/java/main/com/topcoder/service/facade/contest/ejb/ContestServiceFacadeBean.java @@ -3862,11 +3862,11 @@ else if (isDevContest) contest.getProjectHeader().setProperty(ProjectPropertyType.TRACK_LATE_DELIVERABLES_PROJECT_PROPERTY_KEY, "false"); } - if (isCodeContest(contest) || isF2FContest(contest) || isDesignF2FContest(contest)) { - // no rated for Code || F2F || Design F2F + if (isF2FContest(contest) || isDesignF2FContest(contest)) { + // no rated for F2F || Design F2F contest.getProjectHeader().setProperty(ProjectPropertyType.RATED_PROJECT_PROPERTY_KEY, "No"); - // no Reiliability for Code || F2F || Design F2F + // no Reliability for Code || F2F || Design F2F contest.getProjectHeader().setProperty(ProjectPropertyType.RELIABILITY_BONUS_ELIGIBLE_PROJECT_PROPERTY_KEY, "false"); contest.getProjectHeader().setProperty(ProjectPropertyType.RELIABILITY_BONUS_COST_PROJECT_PROPERTY_KEY, "0"); diff --git a/services/project_services/src/java/main/com/topcoder/project/service/impl/ProjectServicesImpl.java b/services/project_services/src/java/main/com/topcoder/project/service/impl/ProjectServicesImpl.java index db3b0e5fc..a4bbe4b0b 100644 --- a/services/project_services/src/java/main/com/topcoder/project/service/impl/ProjectServicesImpl.java +++ b/services/project_services/src/java/main/com/topcoder/project/service/impl/ProjectServicesImpl.java @@ -1594,6 +1594,7 @@ public FullProjectData createProject(Project projectHeader, com.topcoder.project phase.setId(0); } + setScorecards(projectHeader, projectPhases); // call phaseManager.updatePhases(projectPhases,operator) Util.log(logger, Level.DEBUG, "Starts calling ProjectManager#updatePhases method."); phaseManager.updatePhases(projectPhases, operator); diff --git a/src/java/main/com/topcoder/direct/services/view/action/contest/DownloadAllSoftwareSubmissionsAction.java b/src/java/main/com/topcoder/direct/services/view/action/contest/DownloadAllSoftwareSubmissionsAction.java index 0fecd4697..cfaea01a8 100644 --- a/src/java/main/com/topcoder/direct/services/view/action/contest/DownloadAllSoftwareSubmissionsAction.java +++ b/src/java/main/com/topcoder/direct/services/view/action/contest/DownloadAllSoftwareSubmissionsAction.java @@ -8,6 +8,7 @@ import com.topcoder.direct.services.view.action.contest.launch.ContestAction; import com.topcoder.direct.services.view.dto.contest.ContestRoundType; import com.topcoder.direct.services.view.dto.contest.ContestType; +import com.topcoder.direct.services.view.util.AmazonS3URI; import com.topcoder.direct.services.view.util.DirectUtils; import com.topcoder.management.deliverable.Submission; import com.topcoder.management.deliverable.Upload; @@ -15,18 +16,19 @@ import com.topcoder.service.project.SoftwareCompetition; import com.topcoder.servlet.request.FileUpload; import com.topcoder.servlet.request.UploadedFile; +import com.topcoder.shared.util.logging.Logger; import org.apache.commons.io.FilenameUtils; - -import java.io.BufferedInputStream; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.InputStream; -import java.io.PipedInputStream; -import java.io.PipedOutputStream; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.HttpResponseException; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.DefaultHttpClient; + +import java.io.*; import java.util.ArrayList; import java.util.List; import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; /** @@ -65,6 +67,11 @@ */ public class DownloadAllSoftwareSubmissionsAction extends ContestAction { + /** + * Logging instance + */ + private static final Logger logger = Logger.getLogger(DownloadAllSoftwareSubmissionsAction.class); + /** * The id of the final submission type. * @@ -287,15 +294,42 @@ public void run() { byte[] buffer = new byte[8192]; int read; InputStream is = null; + DefaultHttpClient httpClient = new DefaultHttpClient(); try { for (Submission sub : submissionsToDownload) { String submissionFileZipName; - // url != null is s3 + // url != null is s3/external url if (sub.getUpload().getUrl() != null) { - S3Object s3Object = DirectUtils.getS3Client().getObject(new GetObjectRequest(s3Bucket, - DirectUtils.getS3FileKey(sub.getUpload().getUrl()))); - is = s3Object.getObjectContent(); - submissionFileZipName = DirectUtils.getS3FileKey(sub.getUpload().getUrl()); + try { + AmazonS3URI s3Uri = DirectUtils.getS3Uri(sub.getUpload().getUrl()); + if (s3Uri != null) { + S3Object s3Object = DirectUtils.getS3Client().getObject(new GetObjectRequest(s3Bucket, + DirectUtils.getS3FileKey(sub.getUpload().getUrl()))); + is = s3Object.getObjectContent(); + submissionFileZipName = "Submission-" + sub.getId() + "-" + DirectUtils.getS3FileKey(sub.getUpload().getUrl()); + } else { + // external url other than s3 + HttpGet request = new HttpGet(sub.getUpload().getUrl()); + HttpResponse response = httpClient.execute(request); + // skip status code >=400 + if (response.getStatusLine().getStatusCode() >= HttpStatus.SC_BAD_REQUEST) { + throw new HttpResponseException(response.getStatusLine().getStatusCode(), "Invalid file from external"); + } + + HttpEntity entity = response.getEntity(); + if (entity != null) { + is = entity.getContent(); + } else { + throw new HttpResponseException(HttpStatus.SC_BAD_REQUEST, "Invalid response from external"); + } + submissionFileZipName = "Submission-" + sub.getId() + "-" + DirectUtils.getFileNameFromUrl(sub.getUpload().getUrl()); + } + } catch (Exception e) { + logger.error("Fail to get submission " + sub.getId() + " url: " + sub.getUpload().getUrl() + + " message: " + e.getMessage()); + logger.info("Skipping submission " + sub.getId() + " url: " + sub.getUpload().getUrl()); + continue; + } } else { UploadedFile file; if (DirectUtils.isStudio(contest)) { @@ -350,6 +384,10 @@ public void run() { // ignore } } + } finally { + if (httpClient != null) { + httpClient.getConnectionManager().shutdown(); + } } try { zos.close(); diff --git a/src/java/main/com/topcoder/direct/services/view/action/contest/DownloadSoftwareSubmissionAction.java b/src/java/main/com/topcoder/direct/services/view/action/contest/DownloadSoftwareSubmissionAction.java index e63f3d242..88c785524 100644 --- a/src/java/main/com/topcoder/direct/services/view/action/contest/DownloadSoftwareSubmissionAction.java +++ b/src/java/main/com/topcoder/direct/services/view/action/contest/DownloadSoftwareSubmissionAction.java @@ -5,14 +5,22 @@ import com.amazonaws.services.s3.model.GetObjectRequest; import com.amazonaws.services.s3.model.S3Object; +import com.amazonaws.services.s3.model.S3Object; import com.topcoder.direct.services.view.action.BaseDirectStrutsAction; import com.topcoder.direct.services.view.dto.contest.ContestType; +import com.topcoder.direct.services.view.util.AmazonS3URI; import com.topcoder.direct.services.view.util.DirectUtils; import com.topcoder.management.deliverable.Submission; import com.topcoder.management.resource.Resource; import com.topcoder.service.project.SoftwareCompetition; import com.topcoder.servlet.request.FileUpload; import com.topcoder.servlet.request.UploadedFile; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.HttpResponseException; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.DefaultHttpClient; import java.io.InputStream; @@ -91,9 +99,9 @@ public class DownloadSoftwareSubmissionAction extends BaseDirectStrutsAction { private SoftwareCompetition contest; /** - * S3 url of uploaded file. Null if it use local file + * External url of uploaded file. Null if it use local file */ - private String s3Url; + private String externalUrl; /** * S3 bucket @@ -144,7 +152,7 @@ protected void executeAction() throws Exception { uploadedFile = fileUpload.getUploadedFile(submission.getUpload().getParameter()); } } else { - s3Url = submission.getUpload().getUrl(); + externalUrl = submission.getUpload().getUrl(); } } @@ -157,10 +165,27 @@ protected void executeAction() throws Exception { * if any error occurs when getting the input stream of the uploaded file. */ public InputStream getInputStream() throws Exception { - if (s3Url != null) { - S3Object s3Object = DirectUtils.getS3Client().getObject(new GetObjectRequest(s3Bucket, - DirectUtils.getS3FileKey(s3Url))); - return s3Object.getObjectContent(); + if (externalUrl != null) { + AmazonS3URI s3Uri = DirectUtils.getS3Uri(externalUrl); + if (s3Uri != null) { + S3Object s3Object = DirectUtils.getS3Client().getObject(new GetObjectRequest(s3Bucket, + DirectUtils.getS3FileKey(externalUrl))); + return s3Object.getObjectContent(); + } else { + DefaultHttpClient httpClient = new DefaultHttpClient(); + HttpGet request = new HttpGet(externalUrl); + HttpResponse response = httpClient.execute(request); + // skip status code >=400 + if (response.getStatusLine().getStatusCode() >= HttpStatus.SC_BAD_REQUEST) { + throw new HttpResponseException(response.getStatusLine().getStatusCode(), "Invalid file from external"); + } + + HttpEntity entity = response.getEntity(); + if (entity == null) { + throw new HttpResponseException(HttpStatus.SC_BAD_REQUEST, "Invalid response from external"); + } + return entity.getContent(); + } } if (contest.getProjectHeader().getProjectCategory().getId() == ContestType.COPILOT_POSTING.getId()) { @@ -188,8 +213,12 @@ public InputStream getInputStream() throws Exception { * if any error occurs when getting the file name of the uploaded file. */ public String getContentDisposition() throws Exception { - if (s3Url != null) { - return "attachment; filename=\"submission-" + submission.getId() + "-" + DirectUtils.getS3FileKey(s3Url) + "\""; + if (externalUrl != null) { + AmazonS3URI s3Uri = DirectUtils.getS3Uri(externalUrl); + if (s3Uri != null) { + return "attachment; filename=\"submission-" + submission.getId() + "-" + DirectUtils.getS3FileKey(externalUrl) + "\""; + } + return "attachment; filename=\"submission-" + submission.getId() + "-" + DirectUtils.getFileNameFromUrl(externalUrl) + "\""; } if (contest.getProjectHeader().getProjectCategory().getId() == ContestType.COPILOT_POSTING.getId()) { diff --git a/src/java/main/com/topcoder/direct/services/view/ajax/SoftwareCompetitionBeanProcessor.java b/src/java/main/com/topcoder/direct/services/view/ajax/SoftwareCompetitionBeanProcessor.java index 5b9fba888..5a543a28f 100644 --- a/src/java/main/com/topcoder/direct/services/view/ajax/SoftwareCompetitionBeanProcessor.java +++ b/src/java/main/com/topcoder/direct/services/view/ajax/SoftwareCompetitionBeanProcessor.java @@ -214,11 +214,11 @@ private Object getMapResult(SoftwareCompetition bean) { // retrieve review scorecard id. for(com.topcoder.project.phases.Phase phase : bean.getProjectPhases().getAllPhases()){ - if(phase.getPhaseType().getName().equals(com.topcoder.project.phases.PhaseType.REVIEW_PHASE.getName())){ + if(phase.getPhaseType().getName().equals(com.topcoder.project.phases.PhaseType.REVIEW_PHASE.getName()) && phase.getAttributes().get("Scorecard ID") != null){ result.put("reviewScorecardId", phase.getAttributes().get("Scorecard ID").toString()); } - if(phase.getPhaseType().getName().equals(com.topcoder.project.phases.PhaseType.ITERATIVE_REVIEW_PHASE.getName())){ + if(phase.getPhaseType().getName().equals(com.topcoder.project.phases.PhaseType.ITERATIVE_REVIEW_PHASE.getName()) && phase.getAttributes().get("Scorecard ID") != null){ result.put("iterativeReviewScorecardId", phase.getAttributes().get("Scorecard ID").toString()); } } diff --git a/src/java/main/com/topcoder/direct/services/view/util/AmazonS3URI.java b/src/java/main/com/topcoder/direct/services/view/util/AmazonS3URI.java new file mode 100644 index 000000000..31d02138b --- /dev/null +++ b/src/java/main/com/topcoder/direct/services/view/util/AmazonS3URI.java @@ -0,0 +1,408 @@ +/* + * Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +package com.topcoder.direct.services.view.util; + +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URLEncoder; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A URI wrapper that can parse out information about an S3 URI. + */ +public class AmazonS3URI { + + private static final Pattern ENDPOINT_PATTERN = + Pattern.compile("^(.+\\.)?s3[.-]([a-z0-9-]+)\\."); + + private static final Pattern VERSION_ID_PATTERN = Pattern.compile("[&;]"); + + private final URI uri; + + private final boolean isPathStyle; + private final String bucket; + private final String key; + private final String versionId; + private final String region; + + /** + * Creates a new AmazonS3URI by parsing the given string. + * String will be URL encoded before generating the URI. + * + * @param str the URI to parse. + */ + public AmazonS3URI(final String str) { + this(str, true); + } + + /** + * Creates a new AmazonS3URI by parsing the given string. + * String will optionally be URL encoded before generating the URI. + * URL encoding is recommended if you work with bucket names or object + * keys with special characters. This can be disabled if you pre-encode + * URI strings before passing them to this class. + * + * @param str the URI to parse. + * @param urlEncode true if string should be URL encoded + */ + public AmazonS3URI(final String str, final boolean urlEncode) { + this(URI.create(preprocessUrlStr(str, urlEncode)), urlEncode); + } + + /** + * Creates a new AmazonS3URI by wrapping the given {@code URI}. + * + * @param uri the URI to wrap + */ + public AmazonS3URI(final URI uri) { + this(uri, false); + } + + private AmazonS3URI(final URI uri, final boolean urlEncode) { + if (uri == null) { + throw new IllegalArgumentException("uri cannot be null"); + } + this.uri = uri; + + // s3://* + if ("s3".equalsIgnoreCase(uri.getScheme())) { + this.region = null; + this.versionId = null; + this.isPathStyle = false; + this.bucket = uri.getAuthority(); + + if (bucket == null) { + throw new IllegalArgumentException("Invalid S3 URI: no bucket: " + + uri); + } + + String path = uri.getPath(); + if (path.length() <= 1) { + // s3://bucket or s3://bucket/ + this.key = null; + } else { + // s3://bucket/key + // Remove the leading '/'. + this.key = uri.getPath().substring(1); + } + return; + } + + String host = uri.getHost(); + if (host == null) { + throw new IllegalArgumentException("Invalid S3 URI: no hostname: " + + uri); + } + + Matcher matcher = ENDPOINT_PATTERN.matcher(host); + if (!matcher.find()) { + throw new IllegalArgumentException( + "Invalid S3 URI: hostname does not appear to be a valid S3 " + + "endpoint: " + uri); + } + + String prefix = matcher.group(1); + if (prefix == null || prefix.isEmpty()) { + + // No bucket name in the authority; parse it from the path. + this.isPathStyle = true; + + // Use the raw path to avoid running afoul of '/'s in the + // bucket name if we have not performed full URL encoding + String path = urlEncode ? uri.getPath() : uri.getRawPath(); + + if ("".equals(path) || "/".equals(path)) { + this.bucket = null; + this.key = null; + } else { + + int index = path.indexOf('/', 1); + if (index == -1) { + + // https://s3.amazonaws.com/bucket + this.bucket = decode(path.substring(1)); + this.key = null; + + } else if (index == (path.length() - 1)) { + + // https://s3.amazonaws.com/bucket/ + this.bucket = decode(path.substring(1, index)); + this.key = null; + + } else { + + // https://s3.amazonaws.com/bucket/key + this.bucket = decode(path.substring(1, index)); + this.key = decode(path.substring(index + 1)); + + } + } + + } else { + + // Bucket name was found in the host; path is the key. + this.isPathStyle = false; + + // Remove the trailing '.' from the prefix to get the bucket. + this.bucket = prefix.substring(0, prefix.length() - 1); + + String path = uri.getPath(); + if (path == null || path.isEmpty() || "/".equals(uri.getPath())) { + this.key = null; + } else { + // Remove the leading '/'. + this.key = uri.getPath().substring(1); + } + } + + this.versionId = parseVersionId(uri.getRawQuery()); + + if ("amazonaws".equals(matcher.group(2))) { + // No region specified + this.region = null; + } else { + this.region = matcher.group(2); + } + } + + /** + * Attempts to parse a versionId parameter from the query + * string. + * + * @param query the query string to parse (possibly null) + * @return the versionId (possibly null) + */ + private static String parseVersionId(String query) { + if (query != null) { + String[] params = VERSION_ID_PATTERN.split(query); + for (String param : params) { + if (param.startsWith("versionId=")) { + return decode(param.substring(10)); + } + } + } + return null; + } + + /** + * @return the S3 URI being parsed + */ + public URI getURI() { + return uri; + } + + /** + * @return true if the URI contains the bucket in the path, false if it + * contains the bucket in the authority + */ + public boolean isPathStyle() { + return isPathStyle; + } + + /** + * @return the bucket name parsed from the URI (or null if no bucket + * specified) + */ + public String getBucket() { + return bucket; + } + + /** + * @return the key parsed from the URI (or null if no key specified) + */ + public String getKey() { + return key; + } + + /** + * @return the version id parsed from the URI (or null if no version specified) + */ + public String getVersionId() { + return versionId; + } + + /** + * @return the region parsed from the URI (or null if no region specified) + */ + public String getRegion() { + return region; + } + + @Override + public String toString() { + return uri.toString(); + } + + /** + * URL encodes the given string. This allows us to pass special characters + * that would otherwise be rejected when building a URI instance. Because we + * need to retain the URI's path structure we subsequently need to replace + * percent encoded path delimiters back to their decoded counterparts. + * + * @param str the string to encode + * @return the encoded string + */ + private static String preprocessUrlStr(final String str, final boolean encode) { + if (encode) { + try { + return (URLEncoder.encode(str, "UTF-8") + .replace("%3A", ":") + .replace("%2F", "/") + .replace("+", "%20")); + } catch (UnsupportedEncodingException e) { + // This should never happen unless there is something + // fundamentally broken with the running JVM. + throw new RuntimeException(e); + } + } + return str; + } + + /** + * Percent-decodes the given string, with a fast path for strings that + * are not percent-encoded. + * + * @param str the string to decode + * @return the decoded string + */ + private static String decode(final String str) { + if (str == null) { + return null; + } + + for (int i = 0; i < str.length(); ++i) { + if (str.charAt(i) == '%') { + return decode(str, i); + } + } + + return str; + } + + /** + * Percent-decodes the given string. + * + * @param str the string to decode + * @param firstPercent the index of the first '%' character in the string + * @return the decoded string + */ + private static String decode(final String str, final int firstPercent) { + StringBuilder builder = new StringBuilder(); + builder.append(str.substring(0, firstPercent)); + + appendDecoded(builder, str, firstPercent); + + for (int i = firstPercent + 3; i < str.length(); ++i) { + if (str.charAt(i) == '%') { + appendDecoded(builder, str, i); + i += 2; + } else { + builder.append(str.charAt(i)); + } + } + + return builder.toString(); + } + + /** + * Decodes the percent-encoded character at the given index in the string + * and appends the decoded value to the given {@code StringBuilder}. + * + * @param builder the string builder to append to + * @param str the string being decoded + * @param index the index of the '%' character in the string + */ + private static void appendDecoded(final StringBuilder builder, + final String str, + final int index) { + + if (index > str.length() - 3) { + throw new IllegalStateException("Invalid percent-encoded string:" + + "\"" + str + "\"."); + } + + char first = str.charAt(index + 1); + char second = str.charAt(index + 2); + + char decoded = (char) ((fromHex(first) << 4) | fromHex(second)); + builder.append(decoded); + } + + /** + * Converts a hex character (0-9A-Fa-f) into its corresponding quad value. + * + * @param c the hex character + * @return the quad value + */ + private static int fromHex(final char c) { + if (c < '0') { + throw new IllegalStateException( + "Invalid percent-encoded string: bad character '" + c + "' in " + + "escape sequence."); + } + if (c <= '9') { + return (c - '0'); + } + + if (c < 'A') { + throw new IllegalStateException( + "Invalid percent-encoded string: bad character '" + c + "' in " + + "escape sequence."); + } + if (c <= 'F') { + return (c - 'A') + 10; + } + + if (c < 'a') { + throw new IllegalStateException( + "Invalid percent-encoded string: bad character '" + c + "' in " + + "escape sequence."); + } + if (c <= 'f') { + return (c - 'a') + 10; + } + + throw new IllegalStateException( + "Invalid percent-encoded string: bad character '" + c + "' in " + + "escape sequence."); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + AmazonS3URI that = (AmazonS3URI) o; + + if (isPathStyle != that.isPathStyle) return false; + if (!uri.equals(that.uri)) return false; + if (bucket != null ? !bucket.equals(that.bucket) : that.bucket != null) return false; + if (key != null ? !key.equals(that.key) : that.key != null) return false; + if (versionId != null ? !versionId.equals(that.versionId) : that.versionId != null) return false; + return region != null ? region.equals(that.region) : that.region == null; + } + + @Override + public int hashCode() { + int result = uri.hashCode(); + result = 31 * result + (isPathStyle ? 1 : 0); + result = 31 * result + (bucket != null ? bucket.hashCode() : 0); + result = 31 * result + (key != null ? key.hashCode() : 0); + result = 31 * result + (versionId != null ? versionId.hashCode() : 0); + result = 31 * result + (region != null ? region.hashCode() : 0); + return result; + } +} diff --git a/src/java/main/com/topcoder/direct/services/view/util/DirectUtils.java b/src/java/main/com/topcoder/direct/services/view/util/DirectUtils.java index e69dc2153..c61ed3623 100644 --- a/src/java/main/com/topcoder/direct/services/view/util/DirectUtils.java +++ b/src/java/main/com/topcoder/direct/services/view/util/DirectUtils.java @@ -4075,9 +4075,40 @@ public static AmazonS3Client getS3Client() { * @throws Exception if any exceptions occurs */ public static String getS3FileKey(String url) throws Exception { + AmazonS3URI s3Uri = getS3Uri(url); + if (s3Uri == null) { + return null; + } + return s3Uri.getKey(); + } + + /** + * Get upload uri from url. + * + * @param url upload url + * @return s3 uri + */ + public static AmazonS3URI getS3Uri(String url) { + try { + AmazonS3URI s3Uri = new AmazonS3URI(url); + return s3Uri; + } catch (IllegalArgumentException ex) { + // url doesn't seem to be a valid + return null; + } + } + + /** + * Get filename from URL + * + * @param url + * @return filename + * @throws Exception + */ + public static String getFileNameFromUrl(String url) throws Exception { String path = new URL(url).getPath(); - int sep = path.lastIndexOf( '/' ); - return ( sep < 0 ) ? path : path.substring( sep + 1 ); + int sep = path.lastIndexOf('/'); + return (sep < 0) ? path : path.substring(sep + 1); } /** diff --git a/src/web/WEB-INF/includes/launch/contestSelection.jsp b/src/web/WEB-INF/includes/launch/contestSelection.jsp index 8c1f04851..2ed3b18fb 100644 --- a/src/web/WEB-INF/includes/launch/contestSelection.jsp +++ b/src/web/WEB-INF/includes/launch/contestSelection.jsp @@ -219,6 +219,15 @@ +
+