All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.kuali.maven.mojo.s3.UpdateOriginBucketMojo Maven / Gradle / Ivy

The newest version!
/**
 * Copyright 2004-2012 The Kuali Foundation
 *
 * Licensed under the Educational Community License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.opensource.org/licenses/ecl2.php
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License 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 org.kuali.maven.mojo.s3;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
import org.apache.maven.project.MavenProject;
import org.kuali.common.threads.ElementHandler;
import org.kuali.common.threads.ExecutionStatistics;
import org.kuali.common.threads.ThreadInvoker;
import org.kuali.maven.common.UrlBuilder;

import com.amazonaws.AmazonServiceException;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.CannedAccessControlList;
import com.amazonaws.services.s3.model.CopyObjectRequest;
import com.amazonaws.services.s3.model.ListObjectsRequest;
import com.amazonaws.services.s3.model.ObjectListing;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.S3ObjectSummary;

/**
 * 

* This mojo updates a bucket serving as an origin for a Cloud Front distribution. It generates an html directory listing for each * "directory" in the bucket and stores the html under a key in the bucket such that a regular http request for a directory returns html. * What would happen otherwise is XML for "object does not exist" would be returned by Amazon. *

*

* For example: The url "http://www.mybucket.com/foo/bar" will return an html page containing a listing of all the files and directories * under "foo/bar" in the bucket. *

*

* If a directory contains an object with a key that is the same as the default object, the plugin copies the object to a key representing * the directory structure. *

* *

* For example, the url "http://www.mybucket.com/foo/bar/index.html" represents an object in an S3 bucket under the key * "foo/bar/index.html". This plugin will copy the object from the key "foo/bar/index.html" to the key "foo/bar/". This causes the url * "http://www.mybucket.com/foo/bar/" to return the same content as the url "http://www.mybucket.com/foo/bar/index.html" *

*

* It also generates an html directory listing at the root of the bucket hierarchy and places that html into the bucket as the default * object, unless a default object already exists. *

* * @goal updateoriginbucket */ public class UpdateOriginBucketMojo extends S3Mojo { UrlBuilder builder = new UrlBuilder(); SimpleFormatter formatter = new SimpleFormatter(); ThreadInvoker invoker = new ThreadInvoker(); private static final String S3_INDEX_METADATA_KEY = "maven-cloudfront-plugin-index"; private static final String S3_INDEX_CONTENT_TYPE = "text/html"; CloudFrontHtmlGenerator generator; S3DataConverter converter; /** * GAV strings representing organizational poms eg "org.kuali.pom:kuali" or "org.kuali.pom:kuali-common". Version is ignored, only * groupId and artifactId are relevant. * * @parameter */ private List orgPomGavs; /** * The max number of threads to use when making calls to S3 * * @parameter expression="${cloudfront.threads}" default-value="20" */ private int threads; /** * @parameter expression="${cloudfront.latestToken}" */ private String latestToken; /** * @parameter expression="${cloudfront.latest}" */ private boolean latest; /** * This parameter should represent the portion of the groupId that is implied by the hostname where content is published. * * For example, the Kuali Rice project has the groupId "org.kuali.rice" and content for the Kuali Rice project is published under * "site.kuali.org/rice". The "org.kuali" portion of the groupId is implied from the hostname "site.kuali.org" and is thus removed when * calculating the url in order to keep things a little more compact. * * If this parameter is not supplied, the complete groupId is used. * * @parameter expression="${cloudfront.organizationGroupId}" */ private String organizationGroupId; /** * This controls the caching behavior for CloudFront. By default, CloudFront edge locations cache content from an S3 bucket for 24 * hours. That interval is shortened to 1 hour for the html indexes generated by this plugin. * * @parameter expression="${cloudfront.cacheControl}" default-value="max-age=3600, must-revalidate" */ private String cacheControl; /** * If true, "foo/bar/index.html" will get copied to "foo/bar/" * * @parameter expression="${cloudfront.copyDefaultObjectWithDelimiter}" default-value="true" */ private boolean copyDefaultObjectWithDelimiter; /** * If true, "foo/bar/index.html" will get copied to "foo/bar". This is defaulted to false because the relative pathing in the html * generated by the maven-site-plugin does not render correctly from a url without the trailing slash. * * @parameter expression="${cloudfront.copyDefaultObjectWithoutDelimiter}" default-value="false" */ private boolean copyDefaultObjectWithoutDelimiter; /** * The stylesheet to use for the directory listing * * @parameter expression="${cloudfront.css}" default-value="http://s3browse.ks.kuali.org/css/style.css" */ private String css; /** * Image representing a file * * @parameter expression="${cloudfront.fileImage}" default-value="http://s3browse.ks.kuali.org/images/page_white.png" */ private String fileImage; /** * Image representing a directory * * @parameter expression="${cloudfront.directoryImage}" default-value="http://s3browse.ks.kuali.org/images/folder.png" */ private String directoryImage; /** * When displaying the last modified timestamp, use this timezone * * @parameter expression="${cloudfront.timezone}" default-value="UTC" */ private String timezone; /** * When displaying the last modified timestamp use this format * * @parameter expression="${cloudfront.dateFormat}" default-value="EEE, dd MMM yyyy HH:mm:ss z" */ private String dateFormat; /** * The key containing the default object for the Cloud Front distribution. If this object already exists, the plugin will not modify it. * If it does not exist, this plugin will generate an html directory listing and place it into the bucket under this key. * * @parameter expression="${cloudfront.defaultObjectKey}" default-value="index.html" */ private String defaultObjectKey; /** * The html for browsing a directory will be created under this key * * @parameter expression="${cloudfront.browseKey}" default-value="browse.html" */ private String browseKey; /** * The default permissions for S3 objects created by this plugin * * @parameter expression="${cloudfront.acl}" default-value="PublicRead" * @required */ private CannedAccessControlList acl; /** * Return a listing of all the prefixes in this directory plus all the prefixes leading back to (and including) the root directory * * @param listing * @param prefix * @param delimiter * @return */ protected List getPrefixes(ObjectListing listing, String prefix, String delimiter) { List commonPrefixes = listing.getCommonPrefixes(); List pathPrefixes = getPathsToRoot(delimiter, prefix); List prefixes = new ArrayList(); prefixes.addAll(commonPrefixes); prefixes.addAll(pathPrefixes); Collections.sort(prefixes); return prefixes; } /** * Get an object listing for the current directory * * @param context * @param request * @return */ protected ObjectListing getObjectListing(S3BucketContext context, ListObjectsRequest request) { String prefix = getPrefix(); String bucket = context.getBucket(); String delimiter = context.getDelimiter(); Integer maxKeys = context.getMaxKeys(); // This is the file system equivalent of typing "ls" in a directory // No objects are retrieved, just metadata about the objects long start = System.currentTimeMillis(); ObjectListing listing = context.getClient().listObjects(request); long millis = System.currentTimeMillis() - start; getLog().info(getListMsg(listing.getCommonPrefixes().size(), millis)); int size = listing.getObjectSummaries().size(); getLog().debug("Found " + size + " summaries for " + prefix); getLog().debug("Found " + listing.getCommonPrefixes().size() + " common prefixes for " + prefix); // If we have more than 1000 objects in the current directory we have an issue if (listing.isTruncated()) { throw new AmazonServiceException("The listing for " + bucket + delimiter + prefix + " exceeded " + maxKeys); } return listing; } /** * Convert prefixes intl ListObjectContext objects * * @param bucketContext * @param prefixes * @return */ protected List getListObjectsContexts(S3BucketContext bucketContext, List prefixes) { List contexts = new ArrayList(); for (String prefix : prefixes) { ListObjectsRequest request = getListObjectsRequest(bucketContext, prefix); ListObjectsContext context = new ListObjectsContext(); context.setRequest(request); context.setBucketContext(bucketContext); contexts.add(context); } return contexts; } /** * Given a prefix and a bucket context return a ListObjectsRequest object * * @param context * @param prefix * @return */ protected ListObjectsRequest getListObjectsRequest(S3BucketContext context, String prefix) { String bucket = context.getBucket(); String delimiter = context.getDelimiter(); Integer maxKeys = context.getMaxKeys(); if (prefix.equals(delimiter)) { prefix = null; } return new ListObjectsRequest(bucket, prefix, null, delimiter, maxKeys); } /** * Show some information about the current directory * * @param subDirectoryCount * @param millis * @return */ protected String getListMsg(int subDirectoryCount, long millis) { StringBuilder sb = new StringBuilder(); sb.append("S3 Directories: " + subDirectoryCount); sb.append(" Listing Request: " + formatter.getTime(millis)); return sb.toString(); } @Override public void executeMojo() throws MojoExecutionException, MojoFailureException { try { // Track what time we started long start = System.currentTimeMillis(); getLog().info("Updating S3 bucket - " + getBucket()); // Update properties on the mojo updateMojoState(); // Save some context info about the bucket we are updating S3BucketContext context = getS3BucketContext(); // Utilities for generating html generator = new CloudFrontHtmlGenerator(context); converter = new S3DataConverter(context); converter.setBrowseKey(getBrowseKey()); // Show what we are up to String prefix = getPrefix(); getLog().info("Re-indexing content for - " + prefix); // Get the object listing for this projects directory ListObjectsRequest request = getListObjectsRequest(context, prefix); // This object listing is a reflection of the projects directory out on S3 ObjectListing projectDirectory = getObjectListing(context, request); // Get a list of prefixes representing other directories in this directory and the directories // leading back to (and including) the root directory List prefixes = getPrefixes(projectDirectory, prefix, context.getDelimiter()); // Make a multi-threaded call out to S3 to obtain object summaries for all of the prefixes List listings = getObjectListings(context, prefixes, threads); // Add the ObjectListing for the projects directory listings.add(projectDirectory); // Convert ObjectListings into S3PrefixContext objects (this generates the html) List contexts = getS3PrefixContexts(context, listings); // Convert S3PrefixContext objects into UpdateDirectoryContext objects List udcs = getUpdateDirContexts(contexts); // Create a thread safe handler for dealing with elements in the list ElementHandler handler = new UpdateDirectoryContextHandler(this); // Make a multi-threaded call out to S3. This creates and/or updates html indexes ExecutionStatistics stats = invoker.invokeThreads(threads, handler, udcs); // Print some diagnostics long millis = stats.getExecutionTime(); long count = stats.getIterationCount(); getLog().info("Updated " + count + " bucket keys. Time: " + formatter.getTime(millis)); getLog().info("S3 Bucket update complete - " + getBucket()); getLog().info("Total time: " + formatter.getTime(System.currentTimeMillis() - start)); } catch (Exception e) { throw new MojoExecutionException("Unexpected error: ", e); } } /** * Make a multi-threaded call to S3 to acquire ObjectListing objects for each of the prefixes * * @param context * @param prefixes * @param maxThreads * @return */ protected List getObjectListings(S3BucketContext context, List prefixes, int maxThreads) { List contexts = getListObjectsContexts(context, prefixes); ListObjectsContextHandler elementHandler = new ListObjectsContextHandler(); ExecutionStatistics stats = invoker.invokeThreads(maxThreads, elementHandler, contexts); long millis = stats.getExecutionTime(); long count = stats.getIterationCount(); getLog().info("Acquired listings for " + count + " prefixes. Time: " + formatter.getTime(millis)); return elementHandler.getObjectListings(); } /** * Convert S3PrefixContext objects into UpdateDirectoryContext objects. Each S3PrefixContext object generates two S3 calls. One for the * prefix with the delimiter and one for the prefix without the delimiter. * * @param contexts * @return */ protected List getUpdateDirContexts(List contexts) { List list = new ArrayList(); for (S3PrefixContext context : contexts) { // Root object requires special handling if (context.isRoot()) { UpdateDirectoryContext udc = new UpdateDirectoryContext(); udc.setContext(context); list.add(udc); continue; } // Create context info for prefixes with and without the delimiter String delimiter = context.getBucketContext().getDelimiter(); String trimmedPrefix = converter.getTrimmedPrefix(context.getPrefix(), delimiter); UpdateDirectoryContext udc1 = new UpdateDirectoryContext(); udc1.setContext(context); udc1.setCopyIfExists(isCopyDefaultObjectWithDelimiter()); udc1.setCopyToKey(context.getPrefix()); UpdateDirectoryContext udc2 = new UpdateDirectoryContext(); udc2.setContext(context); udc2.setCopyIfExists(isCopyDefaultObjectWithoutDelimiter()); udc2.setCopyToKey(trimmedPrefix); list.add(udc1); list.add(udc2); } return list; } /** * Split the string up and return a list of Strings representing the path from the starting prefix back to (and including) the root of * the bucket. * * @param delimiter * @param startingPrefix * @return */ protected List getPathsToRoot(String delimiter, String startingPrefix) { List list = new ArrayList(); list.add(delimiter); String[] prefixes = StringUtils.splitByWholeSeparator(startingPrefix, delimiter); String newPrefix = ""; for (int i = 0; i < prefixes.length - 2; i++) { newPrefix += prefixes[i] + delimiter; list.add(newPrefix); } return list; } /** * Get a default prefix into the bucket from the project + groupId * * @param project * @param groupId * @return */ protected String getDefaultPrefix(MavenProject project, String orgGroupId) { List orgPoms = builder.getMavenProjects(orgPomGavs); DefaultSiteContext context = new DefaultSiteContext(); context.setLatest(this.latest); context.setLatestToken(this.latestToken); context.setOrganizationGroupId(orgGroupId); context.setOrgPoms(orgPoms); return builder.getSitePath(project, context); } /** * If they supplied a prefix use it. Otherwise generate one from the project metadata */ protected void updatePrefix() { String s = getPrefix(); if (StringUtils.isEmpty(s)) { s = getDefaultPrefix(getProject(), getOrganizationGroupId()); } if (!s.endsWith(getDelimiter())) { s = s + getDelimiter(); } setPrefix(s); } /** * Update the state of this mojo from the project metadata */ protected void updateMojoState() throws MojoExecutionException { updateCredentials(); validateCredentials(); updatePrefix(); } /** * Get context information about the bucket we are operating on */ protected S3BucketContext getS3BucketContext() throws MojoExecutionException { AWSCredentials credentials = getCredentials(); AmazonS3Client client = new AmazonS3Client(credentials); S3BucketContext context = new S3BucketContext(); try { BeanUtils.copyProperties(context, this); } catch (Exception e) { throw new MojoExecutionException("Error copying properties", e); } context.setClient(client); context.setLastModifiedDateFormatter(getLastModifiedDateFormatter()); context.setAbout(getAbout()); return context; } /** * Create a PutObjectRequest for some html generated by this mojo. The PutObjectRequest sets the content type to S3_INDEX_CONTENT_TYPE, * sets the ACL to PublicRead, and adds some custom metadata so we can positively identify it as an object created by this plugin */ protected PutObjectRequest getPutIndexObjectRequest(String html, String key) { InputStream in = new ByteArrayInputStream(html.getBytes()); ObjectMetadata om = new ObjectMetadata(); om.setCacheControl(getCacheControl()); String contentType = S3_INDEX_CONTENT_TYPE; om.setContentType(contentType); om.setContentLength(html.length()); om.addUserMetadata(S3_INDEX_METADATA_KEY, "true"); PutObjectRequest request = new PutObjectRequest(getBucket(), key, in, om); request.setCannedAcl(getAcl()); return request; } /** * Return a SimpleDateFormat object initialized with the date format and timezone supplied to the mojo */ protected SimpleDateFormat getLastModifiedDateFormatter() { SimpleDateFormat sdf = new SimpleDateFormat(getDateFormat()); sdf.setTimeZone(TimeZone.getTimeZone(getTimezone())); return sdf; } /** * Return true if the Collection is null or contains no entries, false otherwise */ protected boolean isEmpty(Collection c) { return c == null || c.size() == 0; } /** * Show some text about this plugin */ protected String getAbout() { String date = getLastModifiedDateFormatter().format(new Date()); PluginDescriptor descriptor = (PluginDescriptor) this.getPluginContext().get("pluginDescriptor"); if (descriptor == null) { // Maven 2.2.1 is returning a null descriptor return "Listing generated by the maven-cloudfront-plugin on " + date; } else { String name = descriptor.getArtifactId(); String version = descriptor.getVersion(); return "Listing generated by the " + name + " v" + version + " on " + date; } } public void updateDirectory(UpdateDirectoryContext context) throws IOException { updateDirectory(context.getContext(), context.isCopyIfExists(), context.getCopyToKey()); } /** * Create an object in the bucket under a key that lets a normal http request function correctly with CloudFront / S3.
* Either use the client's object or upload some html created by this plugin
*/ protected void updateDirectory(S3PrefixContext context, boolean isCopyIfExists, String copyToKey) throws IOException { S3BucketContext bucketContext = context.getBucketContext(); AmazonS3Client client = context.getBucketContext().getClient(); String bucket = bucketContext.getBucket(); boolean containsDefaultObject = isExistingObject(context.getObjectListing(), context.getDefaultObjectKey()); if (containsDefaultObject && isCopyIfExists) { // Copy the contents of the clients default object String sourceKey = context.getDefaultObjectKey(); String destKey = copyToKey; CopyObjectRequest request = getCopyObjectRequest(bucket, sourceKey, destKey); getLog().debug("Copy: " + sourceKey + " to " + destKey); client.copyObject(request); } else { // Upload our custom content PutObjectRequest request = getPutIndexObjectRequest(context.getHtml(), copyToKey); getLog().debug("Put: " + copyToKey); client.putObject(request); } } /** * Update this S3 "directory". */ protected void updateDirectory(final S3PrefixContext context) throws IOException { String trimmedPrefix = converter.getTrimmedPrefix(context.getPrefix(), context.getBucketContext().getDelimiter()); // Handle "http://www.mybucket.com/foo/bar/" updateDirectory(context, isCopyDefaultObjectWithDelimiter(), context.getPrefix()); // Handle "http://www.mybucket.com/foo/bar" updateDirectory(context, isCopyDefaultObjectWithoutDelimiter(), trimmedPrefix); // Handle "http://www.mybucket.com/foo/bar/browse.html" // context.getBucketContext().getClient().putObject(getPutIndexObjectRequest(context.getHtml(), // context.getBrowseHtmlKey())); } /** * If this is the root of the bucket and the default object either does not exist or was created by this plugin, overwrite the default * object with newly generated html. Otherwise, do nothing. */ public void updateRoot(UpdateDirectoryContext udc) throws IOException { updateRoot(udc.getContext()); } /** * If this is the root of the bucket and the default object either does not exist or was created by this plugin, overwrite the default * object with newly generated html. Otherwise, do nothing. */ protected void updateRoot(S3PrefixContext context) throws IOException { AmazonS3Client client = context.getBucketContext().getClient(); // Handle "http://www.mybucket.com/browse.html" PutObjectRequest request1 = getPutIndexObjectRequest(context.getHtml(), context.getBrowseHtmlKey()); StringBuilder sb = new StringBuilder(); sb.append(context.getBrowseHtmlKey()); client.putObject(request1); boolean isCreateOrUpdateDefaultObject = isCreateOrUpdateDefaultObject(context); if (!isCreateOrUpdateDefaultObject) { getLog().info("Put: " + sb.toString()); return; } // Update the default object PutObjectRequest request2 = getPutIndexObjectRequest(context.getHtml(), context.getDefaultObjectKey()); getLog().info("Put: " + sb.toString() + ", " + context.getDefaultObjectKey()); client.putObject(request2); } /** * Convert ObjectListing objects into S3PrefixContext objects */ protected List getS3PrefixContexts(S3BucketContext context, List listings) { List contexts = new ArrayList(); for (ObjectListing listing : listings) { S3PrefixContext prefixContext = getS3PrefixContext(context, listing); contexts.add(prefixContext); } return contexts; } /** * Convert an ObjectListing into an S3PrefixContext */ protected S3PrefixContext getS3PrefixContext(S3BucketContext context, ObjectListing objectListing) { String prefix = objectListing.getPrefix(); String delimiter = context.getDelimiter(); List data = converter.getData(objectListing, prefix, delimiter); String html = generator.getHtml(data, prefix, delimiter); String defaultObjectKey = StringUtils.isEmpty(prefix) ? getDefaultObjectKey() : prefix + getDefaultObjectKey(); String browseHtmlKey = StringUtils.isEmpty(prefix) ? getBrowseKey() : prefix + getBrowseKey(); // Is this the root of the bucket? boolean isRoot = StringUtils.isEmpty(prefix); S3PrefixContext prefixContext = new S3PrefixContext(); prefixContext.setObjectListing(objectListing); prefixContext.setHtml(html); prefixContext.setRoot(isRoot); prefixContext.setDefaultObjectKey(defaultObjectKey); prefixContext.setPrefix(prefix); prefixContext.setBucketContext(context); prefixContext.setBrowseHtmlKey(browseHtmlKey); return prefixContext; } /** * Return true if the ObjectListing contains an object under "key" */ protected boolean isExistingObject(final ObjectListing objectListing, final String key) { List summaries = objectListing.getObjectSummaries(); for (S3ObjectSummary summary : summaries) { if (key.equals(summary.getKey())) { return true; } } return false; } /** * Return true if there is no object in the ObjectListing under defaultObjectKey.
* Return true if the object in the ObjectListing was created by this plugin.
* Return false otherwise.
*/ protected boolean isCreateOrUpdateDefaultObject(final S3PrefixContext context) { if (!isExistingObject(context.getObjectListing(), context.getDefaultObjectKey())) { // There is no default object, we are free to create one return true; } S3BucketContext s3Context = context.getBucketContext(); // There is a default object, but if it was created by this plugin, we // still need to update it S3Object s3Object = s3Context.getClient().getObject(s3Context.getBucket(), context.getDefaultObjectKey()); boolean isOurDefaultObject = isOurObject(s3Object); IOUtils.closeQuietly(s3Object.getObjectContent()); if (isOurDefaultObject) { return true; } else { return false; } } /** * Return true if this S3Object was created by this plugin. This is is done by checking the metadata attached to this object for the * presence of a custom value. */ protected boolean isOurObject(final S3Object s3Object) { ObjectMetadata metadata = s3Object.getObjectMetadata(); Map userMetadata = metadata.getUserMetadata(); String value = userMetadata.get(S3_INDEX_METADATA_KEY); boolean isOurObject = "true".equals(value); return isOurObject; } /** * Create a CopyObjectRequest with an ACL set to PublicRead */ protected CopyObjectRequest getCopyObjectRequest(final String bucket, final String sourceKey, final String destKey) { CopyObjectRequest request = new CopyObjectRequest(bucket, sourceKey, bucket, destKey); request.setCannedAccessControlList(getAcl()); return request; } public String getTimezone() { return timezone; } public void setTimezone(final String timezone) { this.timezone = timezone; } public String getDateFormat() { return dateFormat; } public void setDateFormat(final String dateFormat) { this.dateFormat = dateFormat; } public String getDefaultObjectKey() { return defaultObjectKey; } public void setDefaultObjectKey(final String defaultCloudFrontObject) { this.defaultObjectKey = defaultCloudFrontObject; } public String getFileImage() { return fileImage; } public void setFileImage(final String fileImage) { this.fileImage = fileImage; } public String getDirectoryImage() { return directoryImage; } public void setDirectoryImage(final String directoryImage) { this.directoryImage = directoryImage; } public String getCss() { return css; } public void setCss(final String css) { this.css = css; } public boolean isCopyDefaultObjectWithDelimiter() { return copyDefaultObjectWithDelimiter; } public void setCopyDefaultObjectWithDelimiter(final boolean copyDefaultObjectWithDelimiter) { this.copyDefaultObjectWithDelimiter = copyDefaultObjectWithDelimiter; } public boolean isCopyDefaultObjectWithoutDelimiter() { return copyDefaultObjectWithoutDelimiter; } public void setCopyDefaultObjectWithoutDelimiter(final boolean copyDefaultObjectWithoutDelimiter) { this.copyDefaultObjectWithoutDelimiter = copyDefaultObjectWithoutDelimiter; } public String getCacheControl() { return cacheControl; } public void setCacheControl(final String cacheControl) { this.cacheControl = cacheControl; } public String getBrowseKey() { return browseKey; } public void setBrowseKey(final String browseHtml) { this.browseKey = browseHtml; } /** * @return the organizationGroupId */ public String getOrganizationGroupId() { return organizationGroupId; } /** * @param organizationGroupId * the organizationGroupId to set */ public void setOrganizationGroupId(final String organizationGroupId) { this.organizationGroupId = organizationGroupId; } public int getThreads() { return threads; } public void setThreads(int threadCount) { this.threads = threadCount; } public CannedAccessControlList getAcl() { return acl; } public void setAcl(CannedAccessControlList acl) { this.acl = acl; } public List getOrgPomGavs() { return orgPomGavs; } public void setOrgPomGavs(List orgPomGavs) { this.orgPomGavs = orgPomGavs; } public String getLatestToken() { return latestToken; } public void setLatestToken(String latestToken) { this.latestToken = latestToken; } public boolean isLatest() { return latest; } public void setLatest(boolean latest) { this.latest = latest; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy