/*
 * RHQ Management Platform
 * Copyright (C) 2005-2008 Red Hat, Inc.
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
package org.rhq.enterprise.server.content;

import java.io.OutputStream;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.ejb.Local;

import org.rhq.core.domain.auth.Subject;
import org.rhq.core.domain.content.ContentSource;
import org.rhq.core.domain.content.ContentSourceSyncResults;
import org.rhq.core.domain.content.ContentSourceType;
import org.rhq.core.domain.content.DistributionFile;
import org.rhq.core.domain.content.DownloadMode;
import org.rhq.core.domain.content.PackageBits;
import org.rhq.core.domain.content.PackageDetailsKey;
import org.rhq.core.domain.content.PackageVersion;
import org.rhq.core.domain.content.PackageVersionContentSource;
import org.rhq.core.domain.content.Repo;
import org.rhq.core.domain.content.RepoSyncResults;
import org.rhq.core.domain.content.composite.PackageVersionMetadataComposite;
import org.rhq.core.domain.util.PageControl;
import org.rhq.core.domain.util.PageList;
import org.rhq.enterprise.server.plugin.pc.content.AdvisorySyncReport;
import org.rhq.enterprise.server.plugin.pc.content.ContentProviderPackageDetails;
import org.rhq.enterprise.server.plugin.pc.content.ContentProviderPackageDetailsKey;
import org.rhq.enterprise.server.plugin.pc.content.DistributionSyncReport;
import org.rhq.enterprise.server.plugin.pc.content.PackageSyncReport;
import org.rhq.enterprise.server.plugin.pc.content.RepoDetails;

/**
 * Interface that provides access to the {@link ContentSource} objects deployed in the server, allowing the callers to
 * access data about and from remote content repositories.
 */
@Local
public interface ContentSourceManagerLocal {
    /**
     * This will look for any {@link PackageVersion}s that are "orphaned" (that is, is not related to any existing
     * content source or repo and is not installed anywhere) and will remove any orphans that it finds. This means it
     * will delete orphaned {@link PackageVersion} definitions and (if loaded) their {@link PackageBits}.
     *
     * @param subject user requesting the purge
     */
    void purgeOrphanedPackageVersions(Subject subject);

    /**
     * Deletes the identified content source. Any package versions that originated from this content source but are
     * still related to one or more repos will remain.
     *
     * @param subject An authenticated user making the request.
     * @param contentSourceId The id of the content source to be deleted.
     */
    void deleteContentSource(Subject subject, int contentSourceId);

    /**
     * Returns all {@link ContentSourceType} objects that are configured in the system.
     *
     * @return all content source types
     */
    Set<ContentSourceType> getAllContentSourceTypes();

    /**
     * Returns all {@link ContentSource} objects that are configured in the system but not presently
     * associated with the repo identified by repoId
     *
     * @param  subject   user asking to perform this
     * @param  repoId the identifier for the repo
     * @param  pc        pagination controls
     *
     * @return all content sources that are not presently associated with the repo identified by repoId
     */
    PageList<ContentSource> getAvailableContentSourcesForRepo(Subject subject, Integer repoId, PageControl pc);

    /**
     * Returns all {@link ContentSource} objects that are configured in the system.
     *
     * @param  subject user asking to perform this
     * @param  pc      pagination controls
     *
     * @return all content sources
     */
    PageList<ContentSource> getAllContentSources(Subject subject, PageControl pc);

    /**
     * Get a {@link ContentSourceType} by name. <code>null</code> will be returned if there is no content source type by
     * that name.
     *
     * @param  name the name of the {@link ContentSourceType} to be returned
     *
     * @return {@link ContentSourceType} found. <code>null</code> if none found
     */
    ContentSourceType getContentSourceType(String name);

    /**
     * Returns the {@link ContentSource} from its ID.
     *
     * @param  subject         user asking to perform this
     * @param  contentSourceId identifies the content source to return
     *
     * @return the content source object, <code>null</code> if the ID is invalid
     */
    ContentSource getContentSource(Subject subject, int contentSourceId);

    /**
     * Get a {@link ContentSource} by name and {@link ContentSourceType} name. <code>null</code> will be returned if
     * there is no content source with the given criteria.
     *
     * @param  subject  user asking to perform this
     * @param  name     the name of the {@link ContentSource} to be returned
     * @param  typeName the name of the {@link ContentSourceType}
     *
     * @return {@link ContentSource} found. <code>null</code> if none found
     */
    ContentSource getContentSourceByNameAndType(Subject subject, String name, String typeName);

    /**
     * Gets the list of imported repos that are associated with a given content source.
     *
     * @param  subject         user asking to perform this
     * @param  contentSourceId the id of a content source.
     * @param  pc              pagination controls
     *
     * @return list of associated repos
     */
    PageList<Repo> getAssociatedRepos(Subject subject, int contentSourceId, PageControl pc);

    /**
     * Gets the list of candidate repos that are associated with a given content source.
     *
     * @param  subject         user asking to perform this
     * @param  contentSourceId the id of a content source.
     * @param  pc              pagination controls
     *
     * @return list of candidate repos
     */
    PageList<Repo> getCandidateRepos(Subject subject, int contentSourceId, PageControl pc);

    /**
     * Allows the caller to page through a list of historical sync results for a content source.
     *
     * @param  subject user asking to perform this
     * @param  contentSourceId The id of a content source.
     * @param  pc pagination controls
     *
     * @return the list of results
     */
    PageList<ContentSourceSyncResults> getContentSourceSyncResults(Subject subject, int contentSourceId, PageControl pc);

    /**
     * Allow a user to purge content source sync results.
     *
     * @param subject  user asking to perform this
     * @param ids     the IDs of the {@link ContentSourceSyncResults} to delete
     */
    void deleteContentSourceSyncResults(Subject subject, int[] ids);

    /**
     * Create the specified content source.
     *
     * @param subject The user making the request.
     *
     * @param contentSource A content source to be created.
     *
     * @return The created content source.
     */
    ContentSource createContentSource(Subject subject, ContentSource contentSource) throws ContentSourceException;

    /**
     * Update an existing {@link ContentSource} object and restarts its underlying adapter. This also forces the adapter
     * to immediately sync with the remote repository. Note that this will only update the content source's basic fields
     * like name, description, etc. as well as its configuration. Specifically, it will not update the other
     * relationships like its repos. Use {@link #addContentSourcesToRepo(Subject, int, int[])} for things like
     * that.
     *
     * @param  subject       wanting to update the ContentSource
     * @param  contentSource to be updated
     * @param  syncNow if you wish to resync the ContentSource after updating
     *
     * @return the ContentSource that was updated
     */
    ContentSource updateContentSource(Subject subject, ContentSource contentSource, boolean syncNow)
        throws ContentSourceException;

    /**
     * Given a content source ID, this will test that the adapter responsible for pulling data from the content source's
     * remote repository can actually connect to that repository, and if not, throw an Exception.
     *
     * @param  contentSourceId the id of the content source on which to test the connection
     *
     * @throws Exception if the test failed
     */
    void testContentSourceConnection(int contentSourceId) throws Exception;

    /**
     * Requests that the identified content source be synchronized and if not lazy-loading to also download its
     * packages' bits. This ensures that the server maintains an accurate list of what is available on the content
     * source by seeing what was added, removed or updated since the last time the content source was synchronized. This
     * method is performed asynchronously - the calling thread will not block and will return immediately.
     *
     * @param  subject         the user asking to perform this
     * @param  contentSourceId identifies the content source to synchronize
     *
     * @throws Exception if failed to kick off the synchronize job
     */
    void synchronizeAndLoadContentSource(Subject subject, int contentSourceId);

    /**
     * Returns all the package versions that are served by the content source identified by the given ID.
     *
     * @param  subject         the user asking to perform this
     * @param  contentSourceId The id of a content source.
     * @param  pc pagination controls
     *
     * @return all package versions that the content source will be providing content for. The object returned also
     *         contains the location where those package versions are located in the content source
     */
    PageList<PackageVersionContentSource> getPackageVersionsFromContentSource(Subject subject, int contentSourceId,
        PageControl pc);

    /**
     * Returns all packages from the given repo. This call takes into account the content source to provide any
     * extra data on the package versions that exist for the particular mapping of content source and package,
     * such as the location attribute.
     *
     * @param subject         user retrieving the data
     * @param contentSourceId content source from which the packages are retrieved
     * @param repoId          repo from which the packages are retrieved
     *
     * @return all package versions that the content source will be providing content for. The object returned also
     *         contains the location where those package versions are located in the content source
     */
    List<PackageVersionContentSource> getPackageVersionsFromContentSourceForRepo(Subject subject, int contentSourceId,
        int repoId);

    /**
     * Returns count of PackageVersions associated with the given content source.
     *
     * @param  subject         caller requesting count
     * @param  contentSourceId to lookup
     *
     * @return count if any
     */
    long getPackageVersionCountFromContentSource(Subject subject, int contentSourceId);

    /**
     * Returns the length of the package version identified by its {@link PackageDetailsKey}. This method ensures that
     * the given resource is subscribed to a repo that contains the package version.
     *
     * @param  resourceId
     * @param  packageDetailsKey
     *
     * @return the length of the package version
     */
    long getPackageBitsLength(int resourceId, PackageDetailsKey packageDetailsKey);

    /////////////////////////////////////////////////////////////////////
    // The methods below should not be exposed to remote clients

    /**
     * Returns all the package versions that are served by all the content sources identified by the given IDs.
     *
     * @param  subject          the user asking to perform this
     * @param  contentSourceIds
     * @param  pc
     *
     * @return all package versions that the content sources will be providing content for. The object returned also
     *         contains the location where those package versions are located in the content source
     */
    PageList<PackageVersionContentSource> getPackageVersionsFromContentSources(Subject subject, int[] contentSourceIds,
        PageControl pc);

    /**
     * Returns all the package versions that are served by the content source identified by the given ID but whose
     * {@link PackageVersion#getPackageBits() package bits} have not been loaded yet.
     *
     * @param  subject         the user asking to perform this
     * @param  contentSourceId
     * @param  pc
     *
     * @return all unloaded package versions that the content source will be providing content for. The object returned
     *         also contains the location where those package versions are located in the content source
     */
    PageList<PackageVersionContentSource> getUnloadedPackageVersionsFromContentSourceInRepo(Subject subject,
        int contentSourceId, int repoId, PageControl pc);

    /**
     * This will download all the distribution bits associated with a specific content source.
     *
     * @param subject
     * @param contentSource
     */
    void downloadDistributionBits(Subject subject, ContentSource contentSource);
    
    /**
     * This will download the actual package bits for that package version from that content source's remote repository.
     * 
     * @param resourceId
     * @param packageDetailsKey
     * @param packageVersionId
     * @return
     */
    boolean downloadPackageBits(int resourceId, PackageDetailsKey packageDetailsKey);
        
    
    /**
     * Given a {@link PackageVersionContentSource} which contains the ID of a content source, an ID of a package
     * version, and the location of that package version on the remote content source repo, this will download the
     * actual package bits for that package version from that content source's remote repository.
     *
     * <p>An exception will be thrown if the package bits could not be loaded.</p>
     *
     * <p>This method is potentially a long running operation. Its transaction timeout should be extended
     * appropriately.</p>
     *
     * <p>If the content source where the package version is found is flagged to {@link DownloadMode#NEVER NEVER}
     * download package bits, this will immediately return <code>null</code>.</p>
     *
     * @param  subject the user asking to perform this
     * @param  pvcs
     *
     * @return information about the package bits that were downloaded - note that this will NOT have the actual bits
     *         inside it - we will not load the package bits in memory for obvious reasons. This will be <code>
     *         null</code> if the content source is configured to never actually download package bits.
     */
    PackageBits downloadPackageBits(Subject subject, PackageVersionContentSource pvcs);

    /**
     * Requests that the identified content source be synchronized. This ensures that the server maintains an accurate
     * list of what is available on the content source by seeing what was added, removed or updated since the last time
     * the content source was synchronized.
     *
     * <p>Do <b>not</b> call this method unless you know what you are doing. It is potentially a long running process
     * and will block the calling thread. In addition, this method must <b>never</b> be called from inside a
     * transaction, because it can be long running. You probably want to call
     * {@link #synchronizeAndLoadContentSource(int)}.</p>
     *
     * @param  contentSourceId identifies the content source to synchronize
     *
     * @return <code>true</code> if the synchronization is complete; <code>false</code> if there is already a
     *         synchronization already in progress and this call did nothing and aborted.
     *
     * @throws Exception if failed to synchronize
     */
    boolean internalSynchronizeContentSource(int contentSourceId) throws Exception;

    /**
     * Creates a new sync results object. Note that this will return <code>null</code> if the given results object has a
     * status of INPROGRESS but there is already a sync results object that is still INPROGRESS and has been in that
     * state for less than 24 hours. Use this to prohibit the system from synchronizing on the same content source
     * concurrently.
     *
     * @param  results the results that should be persisted
     *
     * @return the persisted object, or <code>null</code> if another sync is currently inprogress.
     */
    ContentSourceSyncResults persistContentSourceSyncResults(ContentSourceSyncResults results);

    /**
     * Updates an existing sync results object. Do not use this method to create a new sync results object - use
     * {@link #persistContentSourceSyncResults(ContentSourceSyncResults)} for that.
     *
     * @param  results the existing results that should be or merged
     *
     * @return the merged object
     */
    ContentSourceSyncResults mergeContentSourceSyncResults(ContentSourceSyncResults results);

    /**
     * Returns the full sync results object.
     *
     * @param  resultsId the ID of the object to return
     *
     * @return the full sync results
     */
    ContentSourceSyncResults getContentSourceSyncResults(int resultsId);

    /**
     * Updates the server with the results of a repo import from a content source.
     *
     * @param repos list of repo data received from the content source; should not be <code>null</code>
     */
    void mergeRepoImportResults(List<RepoDetails> repos);

    /**
     * After a sync has happened, this is responsible for persisting the results.
     *
     * @param  contentSource content source that was just sync'ed
     * @param  report        information on what the current inventory should look like
     * @param  previous      information from the previous inventory, before the sync happened
     * @param  syncResults   sync results object that should be updated to track this method's progress
     *
     * @return the updated syncResults that includes more summary information in the results string that indicates what
     *         was done
     */
    RepoSyncResults mergePackageSyncReport(ContentSource contentSource, Repo repo, PackageSyncReport report,
        Map<ContentProviderPackageDetailsKey, PackageVersionContentSource> previous, RepoSyncResults syncResults);

    /**
     * After a sync has happened, this is responsible for persisting the results.
     *
     * @param  contentSource content source that was just sync'ed
     * @param  report        information on what the current inventory should look like
     * @param  syncResults   sync results object that should be updated to track this method's progress
     *
     * @return the updated syncResults that includes more summary information in the results string that indicates what
     *         was done
     */
    RepoSyncResults mergeDistributionSyncReport(ContentSource contentSource, DistributionSyncReport report,
        RepoSyncResults syncResults);

    /**
     * After a sync has happened, this is responsible for persisting the results.
     *
     * @param  contentSource content source that was just sync'ed
     * @param  report        information on what the current inventory should look like
     * @param  syncResults   sync results object that should be updated to track this method's progress
     *
     * @return the updated syncResults that includes more summary information in the results string that indicates what
     *         was done
     */
    RepoSyncResults mergeAdvisorySyncReport(ContentSource contentSource, AdvisorySyncReport report,
        RepoSyncResults syncResults);

    void _mergePackageSyncReportUpdateRepo(int contentSourceId);

    RepoSyncResults _mergePackageSyncReportREMOVE(ContentSource contentSource, Repo repo, PackageSyncReport report,
        Map<ContentProviderPackageDetailsKey, PackageVersionContentSource> previous, RepoSyncResults syncResults,
        StringBuilder progress);

    RepoSyncResults _mergePackageSyncReportADD(ContentSource contentSource, Repo repo,
        Collection<ContentProviderPackageDetails> newPackages,
        Map<ContentProviderPackageDetailsKey, PackageVersionContentSource> previous, RepoSyncResults syncResults,
        StringBuilder progress, int addCount);

    RepoSyncResults _mergePackageSyncReportUPDATE(ContentSource contentSource, PackageSyncReport report,
        Map<ContentProviderPackageDetailsKey, PackageVersionContentSource> previous, RepoSyncResults syncResults,
        StringBuilder progress);

    RepoSyncResults _mergeDistributionSyncReportREMOVE(ContentSource contentSource, DistributionSyncReport report,
        RepoSyncResults syncResults, StringBuilder progress);

    RepoSyncResults _mergeDistributionSyncReportADD(ContentSource contentSource, DistributionSyncReport report,
        RepoSyncResults syncResults, StringBuilder progress);

    RepoSyncResults _mergeAdvisorySyncReportADD(ContentSource contentSource, AdvisorySyncReport report,
        RepoSyncResults syncResults, StringBuilder progress);

    RepoSyncResults _mergeAdvisorySyncReportREMOVE(ContentSource contentSource, AdvisorySyncReport report,
        RepoSyncResults syncResults, StringBuilder progress);

    /**
     * Requests all {@link PackageVersion#getMetadata() metadata} for all package versions that the given resource
     * component is subscribed to (see {@link Repo#getResources()}. The returned object has the metadata bytes that
     * are meaningful to the calling plugin component.
     *
     * <p>Note that the returned object has the package version IDs that can be used to retrieve the actual content bits
     * of the package versions via a call to {@link #retrievePackageVersionBits(int, int)}.</p>
     *
     * <p>Callers should consider caching the returned metadata. Use {@link #getResourceSubscriptionMD5(int)} to get the
     * MD5 hashcode of the metadata for the resource to aid in determining when a cache of metadata is stale.</p>
     *
     * @param  resourceId identifies the resource requesting the data; all package versions in all the resource's
     *                    subscribed repos will be represented in the returned map
     * @param  pc         this method can potentially return a large set; this page control object allows the caller to
     *                    page through that large set, as opposed to requesting the entire set in one large chunk
     *
     * @return the list of all package versions' metadata
     *
     * @see    #getResourceSubscriptionMD5(int)
     */
    PageList<PackageVersionMetadataComposite> getPackageVersionMetadata(int resourceId, PageControl pc);

    /**
     * Gets the MD5 hash which identifies a resource "content subscription". This MD5 hash will change when any repo
     * the resource is subscribed to has changed its contents (that is, if a package version was added/updated/removed
     * from it).
     *
     * @param  resourceId identifies the resource requesting the MD5; any change to any package version in any of the
     *                    resource's subscribed repos will determine the MD5
     *
     * @return the MD5
     *
     * @see    #getPackageVersionMetadata(int, PageControl)
     */
    String getResourceSubscriptionMD5(int resourceId);

    /**
     * Requests that the actual content data (the "bits") of the identified package version be streamed down to the
     * caller over the given output stream that the caller provides. This method will <b>not</b> be responsible for
     * closing the stream when its done, the caller needs to close it. This may be a time-consuming method call because
     * if the bits have not yet been loaded (i.e. the content source where the package version lives
     * {@link ContentSource#isLazyLoad() is lazy loading} then this may be the time when it is downloaded from the
     * remote repository.
     *
     * <p>This is the same as calling
     * {@link #outputPackageVersionBitsRangeGivenResource(int, PackageDetailsKey, OutputStream, long, long)} with the start/end bytes
     * of 0 and -1 respectively.</p>
     *
     * @param  resourceId        identifies the resource making the request; if this resource is not allowed to see the
     *                           package version (due to the fact that it is not subscribed to a repo that is serving
     *                           that package version), an exception is thrown
     * @param  packageDetailsKey identifies the {@link PackageVersion} whose {@link PackageBits} are to be streamed
     * @param  outputStream      a stream that the caller prepared where this method will write the actual content
     *
     * @return the number of bytes written to the output stream
     */
    long outputPackageVersionBitsGivenResource(int resourceId, PackageDetailsKey packageDetailsKey,
        OutputStream outputStream);

    /**
     * Requests that the actual content data (the "bits") of the identified package version be streamed down to the
     * caller over the given output stream that the caller provides. This method will <b>not</b> be responsible for
     * closing the stream when its done, the caller needs to close it. This may be a time-consuming method call because
     * if the bits have not yet been loaded (i.e. the content source where the package version lives
     * {@link ContentSource#isLazyLoad() is lazy loading} then this may be the time when it is downloaded from the
     * remote repository.
     *
     * @param  resourceId        identifies the resource making the request; if this resource is not allowed to see the
     *                           package version (due to the fact that it is not subscribed to a repo that is serving
     *                           that package version), an exception is thrown
     * @param  packageDetailsKey identifies the {@link PackageVersion} whose {@link PackageBits} are to be streamed
     * @param  outputStream      a stream that the caller prepared where this method will write the actual content
     * @param  startByte         the first byte (inclusive) of the byte range to retrieve and output (bytes start at
     *                           index 0)
     * @param  endByte           the last byte (inclusive) of the byte range to retrieve and output (-1 means up to EOF)
     *                           (bytes start at index 0)
     *
     * @return the number of bytes written to the output stream
     */
    long outputPackageVersionBitsRangeGivenResource(int resourceId, PackageDetailsKey packageDetailsKey,
        OutputStream outputStream, long startByte, long endByte);

    /**
     * Requests the bits of a package being used to create a child resource be stream down to the caller over
     * the given output stream. This method will <b>not</b> take care of closing the stream when it is finished;
     * it is the caller's responsibility. This may be a time-consuming method call because
     * if the bits have not yet been loaded (i.e. the content source where the package version lives
     * {@link ContentSource#isLazyLoad() is lazy loading} then this may be the time when it is downloaded from the
     * remote repository.
     *
     * @param parentResourceId  identifies the parent resource under which the new resource is being created
     * @param resourceTypeName  type of child resource being created
     * @param packageDetailsKey package being used to create the child resource
     * @param outputStream      an output stream where the server should write the package contents. It is up to the
     *                          caller to prepare this output stream in order to write the package content to an
     *                          appropriate location.
     *
     * @return the number of bytes written to the output stream
     */
    long outputPackageBitsForChildResource(int parentResourceId, String resourceTypeName,
        PackageDetailsKey packageDetailsKey, OutputStream outputStream);

    /**
     * Requests the bits of a package be streamed down to the caller over the given output stream. 
     * This method will <b>not</b> take care of closing the stream when it is finished;
     * it is the caller's responsibility. This may be a time-consuming method call because
     * if the bits have not yet been loaded (i.e. the content source where the package version lives
     * {@link ContentSource#isLazyLoad() is lazy loading} then this may be the time when it is downloaded from the
     * remote repository.
     *
     * @param packageVersion    packageVersion to fetch 
     * @param outputStream      an output stream where the server should write the package contents. It is up to the
     *                          caller to prepare this output stream in order to write the package content to an
     *                          appropriate location.
     *
     * @return the number of bytes written to the output stream
     */
    long outputPackageVersionBits(PackageVersion packageVersion, OutputStream outputStream);

    /**
     * Requests a range of bits from a package.  This range of bits will be streamed down to the caller over the given 
     * output stream.  This method will <b>not</b> take care of closing the stream when it is finished;
     * it is the caller's responsibility. This may be a time-consuming method call because
     * if the bits have not yet been loaded (i.e. the content source where the package version lives
     * {@link ContentSource#isLazyLoad() is lazy loading} then this may be the time when it is downloaded from the
     * remote repository.
     *
     * @param packageVersion    packageVersion to fetch 
     * @param outputStream      an output stream where the server should write the package contents. It is up to the
     *                          caller to prepare this output stream in order to write the package content to an
     *                          appropriate location.
     * @param startByte         start index
     * @param endByte           end index
     *
     * @return the number of bytes written to the output stream
     */
    long outputPackageVersionBits(PackageVersion packageVersion, OutputStream outputStream, long startByte, long endByte);

    /**
     * Requests the bits of a distribution file be streamed down to the caller over the given output stream.
     * This method will <b>not</b> take care of closing the stream when it is finished;
     * it is the caller's responsibility.
     *
     * @param distFile          distribution file to fetch
     * @param outputStream      an output stream where the server should write the package contents. It is up to the
     *                          caller to prepare this output stream in order to write the package content to an
     *                          appropriate location.
     *
     * @return the number of bytes written to the output stream
     */
    long outputDistributionFileBits(DistributionFile distFile, OutputStream outputStream);

    /**
     * Adds the specified content source to the database but does not attempt to create or start
     * the server-side plugin provider implementation associated with it.
     * <p/>
     * This should only be used for test purposes.
     *
     * @param subject       may not be <code>null</code>
     * @param contentSource may not be <code>null</code>
     * @return instance after being persisted; will contain a populated ID value
     * @throws ContentSourceException if the content source cannot be created, such as if the data in
     *                                the given object are not valid
     */
    ContentSource simpleCreateContentSource(Subject subject, ContentSource contentSource) throws ContentSourceException;
} 
