/* * Licensed under the Apache 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.apache.org/licenses/LICENSE-2.0 * * 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. * * Contributions from 2013-2017 where performed either by US government * employees, or under US Veterans Health Administration contracts. * * US Veterans Health Administration contributions by government employees * are work of the U.S. Government and are not subject to copyright * protection in the United States. Portions contributed by government * employees are USGovWork (17USC ยง105). Not subject to copyright. * * Contribution by contractors to the US Veterans Health Administration * during this period are contractually contributed under the * Apache License, Version 2.0. * * See: https://www.usa.gov/government-works * * Contributions prior to 2013: * * Copyright (C) International Health Terminology Standards Development Organisation. * Licensed under the Apache License, Version 2.0. * */ package sh.isaac.api.sync; //~--- JDK imports ------------------------------------------------------------ import java.io.File; import java.io.IOException; import java.util.Map; import java.util.Set; import javax.naming.AuthenticationException; //~--- non-JDK imports -------------------------------------------------------- import org.jvnet.hk2.annotations.Contract; //~--- interfaces ------------------------------------------------------------- /** * {@link SyncFiles} * * An interface to specify the various methods available by remote profile sync services. * * Implementations of this interface include GIT and SVN backed remote sync services. * * @author <a href="mailto:daniel.armbrust.list@gmail.com">Dan Armbrust</a> */ @Contract public interface SyncFiles { /** The Constant DEFAULT_README_CONTENT. */ public static final String DEFAULT_README_CONTENT = "ISAAC Profiles Storage \r" + "=== \r" + "This is a repository for storing ISAAC profiles and changesets.\r" + "It is highly recommended that you do not make changes to this repository manually - ISAAC interfaces with this."; //~--- methods ------------------------------------------------------------- /** * Mark the specified files as files that should be synchronized. This is a local operation only - does not push to the server. * * @param files - The relative path of each file that should be added (relative to the localFolder) * @throws IllegalArgumentException - if the passed parameters are invalid * @throws IOException - Thrown if an error occurs accessing local or remote resources */ public void addFiles(String... files) throws IllegalArgumentException, IOException; /** * Equivalent of calling {@link #addFiles(File, Set)} for each file in the localFolder which is currently unmanaged. * * @throws IllegalArgumentException - if the passed parameters are invalid * @throws IOException - Thrown if an error occurs accessing local or remote resources */ public void addUntrackedFiles() throws IllegalArgumentException, IOException; /** * Connect to remote service - pull down any existing files in the remote service. * <br><br> * In general, this operation should not result in any add / commit operations (though implementations may add/commit a 'README' file * if necessary to complete the linkage in the case of empty repositories - in fact, are encouraged to ensure that a reasonable README * file exists on both the client and the server at the end of this operation) * <br><br> * This is NOT intended to be called from a running application - rather - it should only be called during the initial configuration * (typically done during the build sequence) as it doesn't provide any feedback as to what local files changed, during its operations. * <pre> * Implementers should handle the following cases: * * * Remote repository exists, but is empty * o Local folder exists - is not a repository - may or may not contain content. * ==> The expected behavior for this case is that the the local folder will be turned into a repository managed folder, * and the remote server should be linked to it. Implementers may commit and push a single file, such as a 'README' file * if necessary - as implementations like GIT and SVN don't allow syncing when there is nothing to sync. Any other pre-existing * local content should NOT be added, committed or synced as part of this operation. * o Local folder exists, and is already a repository - may or may not contain content. * ==> The expected behavior for this case is that the local repository should be linked to the remote repository. In the case of GIT, * it is acceptable to push the local repository directly to the remote server. This will push any content that was previously committed to the * repository - but any existing content in the local folder that has not been added or committed should not be pushed to the remote server. * For other source control systems, this is likely an impossible state - and they may simply discard the local repository information - and link * to the new location. Any local content should be preserved, but not committed or added to the remote repository. * * * Remote repository exists, and is populated * o Local folder exists - is not a repository - may or may not contain content * ==> The expected behavior for this case is that the server content should be checked out locally - and added to the local folder. If local files * already exist that would be overwritten by the checkout (because they also exist on the server) the local files should be PRESERVED - not overwritten. * The end result of the operation would show these files in a MODIFIED state. * o Local folder exists, and is already a repository - may or may not contain content. * ==> The expected behavior for this case is that the server and local repositories should be linked, and the content from the server should be checked * out and added to the local folder. The assumption is made that the client and server were a pair at one point - this should be the equivalent of * updating the client to point to a new URL for the server if the server was relocated, for example. * If the client repository is found to be incompatible with the server repository - then the client state should be discarded - and a new checkout * should be done from the server. Any local files which have naming collisions with server files should be PRESERVED during the checkout - not * overwritten - leaving them in a MODIFIED state, if they happen to differ from the files that were on the server. * </pre> * * @param remoteAddress - the URL to the remote server * @param username - The username to use for remote operations * @param password - The password to use for remote operations * @throws IllegalArgumentException - if the passed parameters are invalid * @throws IOException - Thrown if an error occurs accessing local or remote resources * @throws AuthenticationException the authentication exception */ public void linkAndFetchFromRemote(String remoteAddress, String username, char[] password) throws IllegalArgumentException, IOException, AuthenticationException; /** * Fix the URL to the remote service. This call should only be used when both the local and remote repositories exist, and are a proper pair - * but the URL for the remote service needs to be corrected, for whatever reason (for example, the domain name changed) * * Has no impact on any local files. * * @param remoteAddress - the URL to the remote server * @param username - remote credentials * @param password - remote credentials * @throws IllegalArgumentException - if the passed parameters are invalid * @throws IOException - Thrown if an error occurs accessing local or remote resources * @throws AuthenticationException - if auth fails during remote relink */ public void relinkRemote(String remoteAddress, String username, char[] password) throws IllegalArgumentException, IOException, AuthenticationException; /** * Mark the specified files as files that should be removed from the server. This is a local operation only - does not push to the server. * If the file exists locally, it will be removed by this operation. * * @param files - The relative path of each file that should be removed (deleted) (relative to the localFolder) * @throws IllegalArgumentException - if the passed parameters are invalid * @throws IOException - Thrown if an error occurs accessing local or remote resources */ public void removeFiles(String... files) throws IllegalArgumentException, IOException; /** * If {@link #updateCommitAndPush(File, String, String, String, MergeFailOption, String...) or {@link #updateFromRemote(File, String, String, MergeFailOption)} * resulted in a MergeFailure exception, this method should be called to specify how to resolve each merge failure. * * After calling this, you may call {@link #updateCommitAndPush(File, String, String, String, MergeFailOption, String...)} again. * * Note - some implementations (specifically GIT) may throw another {@link MergeFailure} during this operation - this is a secondary merge failure * which will also have to be resolved by the user (by calling this method again) before you can commit and push. * * @param resolutions - A map where each key is a relative file name of a file that had a mergeFailure, and the corresponding value is * the action that should be taken to resolve the issue. Note that {@link MergeFailOption#FAIL} it not a valid option. * @return At a minimum, the set of files modified by the resolution of the merge. Implementation may, at their choosing , return the complete set of files * that changed during the pull from the server that led to the merge failure, in addition to the files that were changes to resolve the conflicts. * @throws IllegalArgumentException - Thrown if an error occurs accessing local or remote resources * @throws IOException - if the passed parameters are invalid * @throws MergeFailure - If the update cannot be applied cleanly. The exception will contain the list of files that were changed (cleanly, or not) during the * update attempt. */ public Set<String> resolveMergeFailures(Map<String, MergeFailOption> resolutions) throws IllegalArgumentException, IOException, MergeFailure; /** * An optional method that allows implementations to do an implementation specific substitution on the URL that will be used to connect. * * For example, in the GIT implementation: * ssh://someuser@csfe.aceworkspace.net:29418/... needs to become: * ssh://<getUsername()>@csfe.aceworkspace.net:29418/... * * The default implementation simply returns url directly back. * * @param url the url * @param username the username * @return The substituted URL for this implementation. Return url directly, if no substitution is required. */ public default String substituteURL(String url, String username) { return url; } /** * Update (all), commit and push the specified files to the remote server. This can include files that have been modified, added or removed. * Assuming that the status call reflects that state. The implementation will also perform an update from remote as part of this operation. * * @param commitMessage - the message to attach to this commit * @param username - the username to use to push the commit remotely * @param password - The password to use to push the commit remotely * @param mergeFailOption - (optional - defaults to {@link MergeFailOption#FAIL}) - the action to take if the required update results in a merge conflict. * @param files - The list of files to commit. May be empty, to support cases where a commit was completed, but the push failed due to a merge issue that * required resolution. May be null to request that all tracked files with changes be committed and pushed. * @return The set of files that changed during the pull from the server. * @throws IllegalArgumentException - Thrown if an error occurs accessing local or remote resources * @throws IOException - if the passed parameters are invalid * @throws MergeFailure - If the update cannot be applied cleanly. The exception will contain the list of files that were changed (cleanly, or not) during the * update attempt. * @throws AuthenticationException the authentication exception */ public Set<String> updateCommitAndPush(String commitMessage, String username, char[] password, MergeFailOption mergeFailOption, String... files) throws IllegalArgumentException, IOException, MergeFailure, AuthenticationException; /** * Get the latest files from the server. * * @param username - the username to use to pull the updates * @param password - The password to use to pull the updates * @param mergeFailOption - (optional - defaults to {@link MergeFailOption#FAIL}) - the action to take if the required update results in a merge conflit. * @return The set of files that changed during the pull from the server. * @throws IllegalArgumentException - Thrown if an error occurs accessing local or remote resources * @throws IOException - if the passed parameters are invalid * @throws MergeFailure - If the update cannot be applied cleanly. The exception will contain the list of files that were changed (cleanly, or not) during the * update attempt. * @throws AuthenticationException the authentication exception */ public Set<String> updateFromRemote(String username, char[] password, MergeFailOption mergeFailOption) throws IllegalArgumentException, IOException, MergeFailure, AuthenticationException; //~--- get methods --------------------------------------------------------- /** * Get the list of files currently in a merge-conflicted state. This should normally return an empty set. Would typically only be called * if a merge sequence was abnormally aborted. This would allow you to get the files that still need to be merged, then call resolveMergeFailures... * * @return the files in merge conflict * @throws IOException Signals that an I/O exception has occurred. */ public Set<String> getFilesInMergeConflict() throws IOException; /** * Check the local SCM status, and get a count of files that have changes that will be pushed. * * @return the locally modified file count * @throws IOException Signals that an I/O exception has occurred. */ public int getLocallyModifiedFileCount() throws IOException; //~--- set methods --------------------------------------------------------- /** * Sets the readme file content. * * @param readmeFileContent - The content to place in the README file that the implementation will create (if it doesn't exist). * If this method is never called, the implementation will default to the text from {@link SyncFiles#DEFAULT_README_CONTENT}. * If left blank - the README file may still be created by the implementation, as some SCM systems don't allow a commit with an empty * repository. */ public abstract void setReadmeFileContent(String readmeFileContent); //~--- get methods --------------------------------------------------------- /** * Return the currently configured root location. * @return full path the the folder that should be synchronizable (or null, if not configured) */ public abstract File getRootLocation(); //~--- set methods --------------------------------------------------------- /** * Set the base folder that should be used in this instance of Profile Sync. Necessary for HK2 style * init where it can't be passed in via the constructor. * * @param localFolder - full path the the folder that should be synchronizable * @throws IllegalArgumentException - if the passed in file is not a folder, or does not exist. */ public abstract void setRootLocation(File localFolder) throws IllegalArgumentException; //~--- get methods --------------------------------------------------------- /** * Returns true if the specified location appears to be a SCM store, false otherwise. * * @return true, if root location configured for SCM */ public boolean isRootLocationConfiguredForSCM(); }