Java tutorial
/** * Copyright (C) 2014 OpenTravel Alliance (info@opentravel.org) * * 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. */ package org.opentravel.schemacompiler.repository; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.InetAddress; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.regex.Pattern; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.opentravel.ns.ota2.repositoryinfo_v01_00.LibraryInfoType; import org.opentravel.ns.ota2.repositoryinfo_v01_00.LibraryStatus; import org.opentravel.ns.ota2.repositoryinfo_v01_00.RefreshPolicy; import org.opentravel.ns.ota2.repositoryinfo_v01_00.RemoteRepositoriesType; import org.opentravel.ns.ota2.repositoryinfo_v01_00.RemoteRepositoryType; import org.opentravel.ns.ota2.repositoryinfo_v01_00.RepositoryInfoType; import org.opentravel.ns.ota2.repositoryinfo_v01_00.RepositoryPermission; import org.opentravel.ns.ota2.repositoryinfo_v01_00.RepositoryState; import org.opentravel.schemacompiler.ioc.SchemaCompilerApplicationContext; import org.opentravel.schemacompiler.loader.LibraryModuleInfo; import org.opentravel.schemacompiler.loader.LibraryModuleLoader; import org.opentravel.schemacompiler.loader.impl.LibraryStreamInputSource; import org.opentravel.schemacompiler.loader.impl.MultiVersionLibraryModuleLoader; import org.opentravel.schemacompiler.model.TLLibrary; import org.opentravel.schemacompiler.model.TLLibraryStatus; import org.opentravel.schemacompiler.repository.impl.DefaultRepositoryFileManager; import org.opentravel.schemacompiler.repository.impl.ProjectFileUtils; import org.opentravel.schemacompiler.repository.impl.RemoteRepositoryClient; import org.opentravel.schemacompiler.repository.impl.RepositoryItemImpl; import org.opentravel.schemacompiler.repository.impl.RepositoryItemVersionedWrapper; import org.opentravel.schemacompiler.repository.impl.RepositoryUtils; import org.opentravel.schemacompiler.saver.LibraryModelSaver; import org.opentravel.schemacompiler.security.PasswordHelper; import org.opentravel.schemacompiler.transform.ObjectTransformer; import org.opentravel.schemacompiler.transform.TransformerFactory; import org.opentravel.schemacompiler.transform.symbols.DefaultTransformerContext; import org.opentravel.schemacompiler.util.ExceptionUtils; import org.opentravel.schemacompiler.util.URLUtils; import org.opentravel.schemacompiler.validate.ValidationFindings; import org.opentravel.schemacompiler.version.VersionScheme; import org.opentravel.schemacompiler.version.VersionSchemeException; import org.opentravel.schemacompiler.version.VersionSchemeFactory; import org.opentravel.schemacompiler.xml.XMLGregorianCalendarConverter; /** * Manager that coordinates the actions, lookups, and relationships between OTA2.0 repositories and * the items that are managed within those repositories. * * @author S. Livezey */ public final class RepositoryManager implements Repository { private static final String CURRENT_USER_BASE_NAMESPACE = "http://opentravel.org/local/"; private static final String ENCRYPTED_PASSWORD_PREFIX = "enc:"; private static final int MAX_DISPLAY_NAME_LENGTH = 256; private static final Pattern REPOSITORY_ID_PATTERN = Pattern .compile("([A-Za-z0-9\\-._~!$&'()*+,;=]|%[0-9A-Fa-f]{2})*"); private static RepositoryManager defaultInstance; private static Log log = LogFactory.getLog(RepositoryManager.class); private RepositoryFileManager fileManager; private String localRepositoryId; private String localRepositoryDisplayName; private Date lastUpdatedDate; private List<RemoteRepositoryClient> remoteRepositories = new ArrayList<RemoteRepositoryClient>(); private List<String> rootNamespaces; /** * Constructor that specifies the root location of the repository to manage. * * @param repositoryLocation * the file system location of the repository to manage * @throws RepositoryException * thrown if the local repository information cannot be initialized */ public RepositoryManager(File repositoryLocation) throws RepositoryException { this(new DefaultRepositoryFileManager(repositoryLocation)); } /** * Constructor that specifies the root location of the repository to manage and the file manager * to use when interacting with the local file system. * * @param repositoryLocation * the file system location of the repository to manage * @param fileManager * the file manager to use when interacting with the local file system (null for * default) * @throws RepositoryException * thrown if the local repository information cannot be initialized */ public RepositoryManager(RepositoryFileManager fileManager) throws RepositoryException { this.fileManager = fileManager; initializeLocalRepositoryInfo(); } /** * Returns the default <code>RepositoryManager</code> instance to manage the repository at the * default file system location. * * @return RepositoryManager * @throws RepositoryException * thrown if the local repository information cannot be initialized */ public static RepositoryManager getDefault() throws RepositoryException { synchronized (RepositoryManager.class) { if (defaultInstance == null) { defaultInstance = new RepositoryManager(RepositoryFileManager.getDefaultRepositoryLocation()); } return defaultInstance; } } /** * Returns the location of the OTA2.0 repository that is managed by this * <code>RepositoryManager</code> instance. * * @return File */ public File getRepositoryLocation() { return fileManager.getRepositoryLocation(); } /** * Returns the file location for the projects folder for the local repository. * * @return File */ public File getProjectsFolder() { return fileManager.getProjectsFolder(); } /** * @see org.opentravel.schemacompiler.repository.Repository#getManager() */ @Override public RepositoryManager getManager() { return this; } /** * Returns the file manager used by this repository manager instance. * * @return RepositoryFileManager */ public RepositoryFileManager getFileManager() { return fileManager; } /** * @see org.opentravel.schemacompiler.repository.Repository#getId() */ @Override public String getId() { return getLocalRepositoryId(); } /** * Returns the ID of the local repository instance. * * @return String */ public String getLocalRepositoryId() { refreshLocalRepositoryInfo(false); return localRepositoryId; } /** * @see org.opentravel.schemacompiler.repository.Repository#getDisplayName() */ @Override public String getDisplayName() { return getLocalRepositoryDisplayName(); } /** * Returns the display name of the local repository instance. * * @return String */ public String getLocalRepositoryDisplayName() { refreshLocalRepositoryInfo(false); return localRepositoryDisplayName; } /** * Updates the identity information for the local repository. * * @param repositoryId * the ID of the local repository * @param displayName * the display name for the local repository * @throws RepositoryException * thrown if the local repository's configuration settings cannot be updated */ public void updateLocalRepositoryIdentity(String repositoryId, String displayName) throws RepositoryException { boolean success = false; try { if (repositoryId == null) repositoryId = ""; if (displayName == null) displayName = ""; if (displayName.length() > MAX_DISPLAY_NAME_LENGTH) { throw new RepositoryException( "Invalid display name for repository (max length is " + MAX_DISPLAY_NAME_LENGTH + ")"); } if (!REPOSITORY_ID_PATTERN.matcher(repositoryId).matches()) { throw new RepositoryException("Invalid repository ID: " + repositoryId); } this.localRepositoryId = repositoryId; this.localRepositoryDisplayName = displayName; fileManager.startChangeSet(); saveLocalRepositoryMetadata(); fileManager.commitChangeSet(); success = true; } finally { if (!success) fileManager.rollbackChangeSet(); } } /** * Constructs a new local repository instance using the information provided. * * @param repositoryId * the ID of the new repository to create * @param displayName * the display name for the new repository * @throws RepositoryException * throw if the repository cannot be created */ public void createLocalRepository(String repositoryId, String displayName) throws RepositoryException { File repositoryMetadataFile = new File(fileManager.getRepositoryLocation(), RepositoryFileManager.REPOSITORY_METADATA_FILENAME); if (repositoryMetadataFile.exists()) { throw new RepositoryException("An OTA2.0 repository already exists at location: " + fileManager.getRepositoryLocation().getAbsolutePath()); } // Create and save the repository meta-data record RepositoryInfoType repositoryMetadata = new RepositoryInfoType(); boolean success = false; try { repositoryMetadata.setID(repositoryId); repositoryMetadata.setDisplayName(displayName); repositoryMetadata.setRemoteRepositories(new RemoteRepositoriesType()); fileManager.startChangeSet(); fileManager.saveRepositoryMetadata(repositoryMetadata); fileManager.commitChangeSet(); success = true; // Refresh the contents of the repository to overwrite any of the old settings refreshLocalRepositoryInfo(true); } finally { if (!success) fileManager.rollbackChangeSet(); } } /** * Returns a list of all known OTA2.0 remote repositories. * * @return List<RemoteRepository> */ public List<RemoteRepository> listRemoteRepositories() { List<RemoteRepository> repositoryList = new ArrayList<RemoteRepository>(); repositoryList.addAll(remoteRepositories); repositoryList.remove(this); return repositoryList; } /** * Returns the repository with the specified ID. * * @param id * the ID of the repository to return * @return Repository */ public Repository getRepository(String id) { if (id == null) return null; Repository repository = null; if (id.equals(localRepositoryId)) { repository = this; } else { for (RemoteRepositoryClient remoteRepository : remoteRepositories) { if (id.equals(remoteRepository.getId())) { repository = remoteRepository; break; } } } return repository; } /** * Adds the given <code>Repository</code> to the universe of known remotely-accessible * repositories. This information is added to the global OTA2.0 configuration that applies to * all projects and models that can be accessed by the local user. * * <p> * NOTE: The remote repository must be available for this method to function properly. If the * repository's web service is not accessible, this method will throw an exception. * * @param endpointUrl * the URL of the repository's web service endpoint * @throws RepositoryException * thrown if the specified repository cannot be added */ public RemoteRepository addRemoteRepository(String endpointUrl) throws RepositoryException { RepositoryInfoType localRepositoryMetadata = fileManager.loadRepositoryMetadata(); RepositoryInfoType remoteRepositoryMetadata = RemoteRepositoryClient.getRepositoryMetadata(endpointUrl); String newRepositoryID = remoteRepositoryMetadata.getID(); // Make sure the requested endpoint is not a duplicate of an existing repository if (localRepositoryMetadata.getRemoteRepositories() != null) { if (localRepositoryMetadata.getID().equals(newRepositoryID)) { throw new RepositoryException( "The remote repository has the same ID as that of the local repository instance."); } for (RemoteRepositoryType existingRepository : localRepositoryMetadata.getRemoteRepositories() .getRemoteRepository()) { if (existingRepository.getID().equals(newRepositoryID)) { throw new RepositoryException( "A remote repository is already defined with ID: " + newRepositoryID); } if (existingRepository.getEndpointUrl().equals(endpointUrl)) { throw new RepositoryException( "A remote repository with the specified endpoint URL already exists: " + newRepositoryID); } } } else { localRepositoryMetadata.setRemoteRepositories(new RemoteRepositoriesType()); } // Add the new remote repository and update the local repository's meta-data RemoteRepositoryType newRepository = new RemoteRepositoryType(); boolean success = false; newRepository.setID(newRepositoryID); newRepository.setDisplayName(remoteRepositoryMetadata.getDisplayName()); newRepository.setEndpointUrl(endpointUrl); newRepository.setRefreshPolicy(RefreshPolicy.DAILY); localRepositoryMetadata.getRemoteRepositories().getRemoteRepository().add(newRepository); try { fileManager.startChangeSet(); fileManager.saveRepositoryMetadata(localRepositoryMetadata); fileManager.commitChangeSet(); success = true; } finally { if (!success) { try { fileManager.rollbackChangeSet(); } catch (RepositoryException e) { log.warn("Error rolling back the active change set.", e); } } } // Refresh the list of repositories information and return the newly-added item refreshLocalRepositoryInfo(true); return (RemoteRepository) getRepository(newRepositoryID); } /** * Removes the given <code>Repository</code> from the universe of known remotely-accessible * repositories. The associated record is deleted from the global OTA2.0 configuration that * applies to all projects and models that can be accessed by the local user. * * @param repository * the remote repository to add * @throws RepositoryException * thrown if the local repository's configuration settings cannot be updated */ public void removeRemoteRepository(RemoteRepository repository) throws RepositoryException { boolean success = false; if (remoteRepositories.contains(repository)) { remoteRepositories.remove(repository); } // Save this change to the local repository's metadata try { fileManager.startChangeSet(); saveLocalRepositoryMetadata(); fileManager.commitChangeSet(); success = true; } finally { if (!success) { try { fileManager.rollbackChangeSet(); } catch (RepositoryException e) { log.warn("Error rolling back the active change set.", e); } refreshLocalRepositoryInfo(true); } } } /** * Refreshes the server-provided configuration of all remote repositories that are know to the * local host. * * @throws RepositoryException * thrown if the local configuration settings cannot be updated */ public void refreshRemoteRepositories() throws RepositoryException { boolean success = false; for (RemoteRepositoryClient repository : remoteRepositories) { try { repository.refreshRepositoryMetadata(); } catch (RepositoryException e) { log.warn("Unable to refresh configuration of remote repository: " + repository.getId()); } } // Save any updates obtained from the remote repositories to the local repository's metadata try { fileManager.startChangeSet(); saveLocalRepositoryMetadata(); fileManager.commitChangeSet(); success = true; } finally { if (!success) { try { fileManager.rollbackChangeSet(); } catch (RepositoryException e) { log.warn("Error rolling back the active change set.", e); } refreshLocalRepositoryInfo(true); } } } /** * Assigns a policy value for the given repository that indicates how often items that are * cached in the local repository should be checked for updates. * * @param repository * the repository for which the credentials apply * @param refreshPolicy * the refresh policy value to apply * @throws RepositoryException * thrown if the local repository's configuration settings cannot be updated */ public void setRefreshPolicy(RemoteRepository repository, RefreshPolicy refreshPolicy) throws RepositoryException { if (!remoteRepositories.contains(repository)) { throw new IllegalArgumentException( "The specified repository is not accessible from the local workspace."); } RemoteRepositoryClient remoteRepository = (RemoteRepositoryClient) repository; boolean success = false; remoteRepository.setRefreshPolicy(refreshPolicy); // Save this change to the local repository's metadata try { fileManager.startChangeSet(); saveLocalRepositoryMetadata(); fileManager.commitChangeSet(); success = true; } finally { if (!success) { try { fileManager.rollbackChangeSet(); } catch (RepositoryException e) { log.warn("Error rolling back the active change set.", e); } refreshLocalRepositoryInfo(true); } } } /** * Assigns the user ID and password credentials for the given repository. * * @param repository * the remote repository for which the credentials apply * @param userId * the user ID credential for the repository * @param password * the plain-text password credential for the repository * @throws RepositoryException * thrown if the local repository's configuration settings cannot be updated */ public void setCredentials(Repository repository, String userId, String password) throws RepositoryException { if (repository == this) { throw new IllegalArgumentException( "Security credentials are not required for local repository access."); } if (!remoteRepositories.contains(repository)) { throw new IllegalArgumentException( "The specified repository is not accessible from the local workspace."); } RemoteRepositoryClient remoteRepository = (RemoteRepositoryClient) repository; boolean success = false; remoteRepository.setUserId(userId); remoteRepository.setEncryptedPassword( ((userId == null) || (password == null)) ? null : PasswordHelper.encrypt(password)); // Save this change to the local repository's metadata try { fileManager.startChangeSet(); saveLocalRepositoryMetadata(); fileManager.commitChangeSet(); success = true; } finally { if (!success) { try { fileManager.rollbackChangeSet(); } catch (RepositoryException e) { log.warn("Error rolling back the active change set.", e); } refreshLocalRepositoryInfo(true); } } } /** * Since the <code>RepositoryManager</code> represents the root repository, it will always * return a single namespace URI that is based on the current user's ID. Only remote * repositories can manage namespaces other than this default URI. * * @see org.opentravel.schemacompiler.repository.Repository#listRootNamespaces() */ @Override public List<String> listRootNamespaces() throws RepositoryException { List<String> rootNamespaces = new ArrayList<String>(); if ((this.rootNamespaces == null) || this.rootNamespaces.isEmpty()) { rootNamespaces.add(CURRENT_USER_BASE_NAMESPACE + System.getProperty("user.name").toLowerCase()); } else { rootNamespaces.addAll(this.rootNamespaces); } return rootNamespaces; } /** * @see org.opentravel.schemacompiler.repository.Repository#listNamespaceChildren(java.lang.String) */ @Override public List<String> listNamespaceChildren(String baseNamespace) throws RepositoryException { String baseNS = RepositoryNamespaceUtils.normalizeUri(baseNamespace); List<String> childPaths = fileManager.findChildBaseNamespacePaths(baseNS); Collections.sort(childPaths); return childPaths; } /** * @see org.opentravel.schemacompiler.repository.Repository#listBaseNamespaces() */ @Override public List<String> listBaseNamespaces() throws RepositoryException { List<String> nsList = fileManager.findAllBaseNamespaces(listRootNamespaces()); List<String> baseNamespaces = new ArrayList<String>(); for (String ns : nsList) { baseNamespaces.add(ns); } Collections.sort(baseNamespaces); return baseNamespaces; } /** * @see org.opentravel.schemacompiler.repository.Repository#listAllNamespaces() */ @Override public List<String> listAllNamespaces() throws RepositoryException { List<String> nsList = fileManager.findAllNamespaces(listRootNamespaces()); List<String> namespaces = new ArrayList<String>(); for (String ns : nsList) { namespaces.add(ns); } Collections.sort(namespaces); return namespaces; } /** * @see org.opentravel.schemacompiler.repository.Repository#listItems(java.lang.String, boolean, * boolean) */ @Override public List<RepositoryItem> listItems(String baseNamespace, boolean latestVersionsOnly, boolean includeDraftVersions) throws RepositoryException { String baseNS = RepositoryNamespaceUtils.normalizeUri(baseNamespace); Map<String, List<RepositoryItemVersionedWrapper>> libraryVersionMap = new HashMap<String, List<RepositoryItemVersionedWrapper>>(); List<LibraryInfoType> metadataList = fileManager.loadLibraryMetadataRecords(baseNS); List<RepositoryItem> itemList = new ArrayList<RepositoryItem>(); for (LibraryInfoType itemMetadata : metadataList) { // Create a map that groups each library's versions together if (localRepositoryId.equals(itemMetadata.getOwningRepository()) && (includeDraftVersions || (itemMetadata.getStatus() == LibraryStatus.FINAL))) { RepositoryItem item = RepositoryUtils.createRepositoryItem(this, itemMetadata); List<RepositoryItemVersionedWrapper> libraryVersions = libraryVersionMap.get(item.getLibraryName()); if (libraryVersions == null) { libraryVersions = new ArrayList<RepositoryItemVersionedWrapper>(); libraryVersionMap.put(item.getLibraryName(), libraryVersions); } libraryVersions.add(new RepositoryItemVersionedWrapper(item)); } } // Sort the results by library name first, then by descending version number List<String> libraryNames = new ArrayList<String>(); libraryNames.addAll(libraryVersionMap.keySet()); Collections.sort(libraryNames); for (String libraryName : libraryNames) { List<RepositoryItemVersionedWrapper> libraryVersions = libraryVersionMap.get(libraryName); String versionSchemeId = libraryVersions.get(0).getVersionScheme(); try { VersionScheme versionScheme = VersionSchemeFactory.getInstance().getVersionScheme(versionSchemeId); Collections.sort(libraryVersions, versionScheme.getComparator(false)); } catch (VersionSchemeException e) { log.warn("Unable to sort library versions - unrecognized version scheme: " + versionSchemeId); } if (latestVersionsOnly) { RepositoryUtils.checkItemState((RepositoryItemImpl) libraryVersions.get(0).getItem(), this); itemList.add(libraryVersions.get(0).getItem()); } else { for (RepositoryItemVersionedWrapper itemWrapper : libraryVersions) { RepositoryUtils.checkItemState((RepositoryItemImpl) itemWrapper.getItem(), this); itemList.add(itemWrapper.getItem()); } } } return itemList; } /** * @see org.opentravel.schemacompiler.repository.Repository#search(java.lang.String, boolean, * boolean) */ @Override public List<RepositoryItem> search(String freeTextQuery, boolean latestVersionsOnly, boolean includeDraftVersions) throws RepositoryException { List<RepositoryItem> searchResults = new ArrayList<RepositoryItem>(); for (RemoteRepository repository : remoteRepositories) { try { List<RepositoryItem> itemList = repository.search(freeTextQuery, latestVersionsOnly, includeDraftVersions); for (RepositoryItem item : itemList) { RepositoryUtils.checkItemState((RepositoryItemImpl) item, this); searchResults.add(item); } } catch (RepositoryException e) { log.warn("Error contacting remote repository: " + repository.getId() + ", reason: " + ExceptionUtils.getExceptionMessage(e)); } } return searchResults; } /** * @see org.opentravel.schemacompiler.repository.Repository#getVersionHistory(org.opentravel.schemacompiler.repository.RepositoryItem) */ @Override public List<RepositoryItem> getVersionHistory(RepositoryItem item) throws RepositoryException { List<RepositoryItem> versionHistory = new ArrayList<RepositoryItem>(); if (item.getRepository() == this) { String baseNS = RepositoryNamespaceUtils.normalizeUri(item.getBaseNamespace()); List<LibraryInfoType> metadataList = fileManager.loadLibraryMetadataRecords(baseNS); List<RepositoryItemVersionedWrapper> versionList = new ArrayList<RepositoryItemVersionedWrapper>(); for (LibraryInfoType libraryMetadata : metadataList) { if (libraryMetadata.getLibraryName().equals(item.getLibraryName())) { RepositoryItem itemVersion = RepositoryUtils.createRepositoryItem(this, libraryMetadata); versionList.add(new RepositoryItemVersionedWrapper(itemVersion)); } } try { VersionScheme vScheme = VersionSchemeFactory.getInstance() .getVersionScheme(item.getVersionScheme()); Collections.sort(versionList, vScheme.getComparator(false)); for (RepositoryItemVersionedWrapper itemWrapper : versionList) { RepositoryUtils.checkItemState((RepositoryItemImpl) itemWrapper.getItem(), this); versionHistory.add(itemWrapper.getItem()); } } catch (VersionSchemeException e) { throw new RepositoryException("Unknown version scheme :" + item.getVersionScheme(), e); } } else if (item.getRepository() != null) { versionHistory = item.getRepository().getVersionHistory(item); } return versionHistory; } /** * @see org.opentravel.schemacompiler.repository.Repository#getRepositoryItem(java.lang.String, * java.lang.String, java.lang.String) */ @Override public RepositoryItem getRepositoryItem(String baseNamespace, String filename, String versionIdentifier) throws RepositoryException { String baseNS = RepositoryNamespaceUtils.normalizeUri(baseNamespace); File itemContent = fileManager.getLibraryContentLocation(baseNS, filename, versionIdentifier); for (RemoteRepositoryClient repository : remoteRepositories) { if (itemContent.exists()) break; try { repository.downloadContent(baseNS, filename, versionIdentifier, false); } catch (RepositoryException e) { // Ignore and move onto the next repository } } if (!itemContent.exists()) { throw new RepositoryException("The managed content for the requested resource could not be located."); } LibraryInfoType libraryMetadata = fileManager.loadLibraryMetadata(baseNS, filename, versionIdentifier); RepositoryItemImpl item = RepositoryUtils.createRepositoryItem(this, libraryMetadata); RepositoryUtils.checkItemState(item, this); return item; } /** * @see org.opentravel.schemacompiler.repository.Repository#getRepositoryItem(java.lang.String) */ @Override public RepositoryItem getRepositoryItem(String itemUri) throws RepositoryException, URISyntaxException { URI uri = RepositoryUtils.toRepositoryItemUri(itemUri); Repository repository = getRepository(uri.getAuthority()); RepositoryItem item; if (repository == null) { throw new RepositoryException("Unknown repository ID: " + uri.getAuthority()); } if (repository == this) { item = null; } else { item = repository.getRepositoryItem(itemUri, null); } return item; } /** * @see org.opentravel.schemacompiler.repository.Repository#getRepositoryItem(java.lang.String, * java.lang.String) */ @Override public RepositoryItem getRepositoryItem(String itemUri, String itemNamespace) throws RepositoryException, URISyntaxException { URI uri = RepositoryUtils.toRepositoryItemUri(itemUri); Repository repository = getRepository(uri.getAuthority()); RepositoryItem item; if (repository == null) { throw new RepositoryException("Unknown repository ID: " + uri.getAuthority()); } if (repository == this) { VersionSchemeFactory vsFactory = VersionSchemeFactory.getInstance(); String[] uriParts = RepositoryUtils.parseRepositoryItemUri(uri); String versionScheme = (uriParts[3] == null) ? vsFactory.getDefaultVersionScheme() : uriParts[3]; if (itemNamespace == null) { itemNamespace = uriParts[1]; } if (itemNamespace == null) { throw new RepositoryException( "Unable to identify the repository item because its namespace was not specified."); } try { VersionScheme vScheme = vsFactory.getVersionScheme(versionScheme); String baseNS = vScheme.getBaseNamespace(itemNamespace); String versionIdentifier = vScheme.getVersionIdentifier(itemNamespace); LibraryInfoType libraryMetadata = fileManager.loadLibraryMetadata(baseNS, uriParts[2], versionIdentifier); if ((libraryMetadata == null) || !libraryMetadata.getOwningRepository().equals(uriParts[0])) { throw new RepositoryException("Resource not found in repository: " + uriParts[0]); } RepositoryItemImpl itemImpl = RepositoryUtils.createRepositoryItem(this, libraryMetadata); RepositoryUtils.checkItemState(itemImpl, this); item = itemImpl; } catch (VersionSchemeException e) { throw new RepositoryException("Unknown version scheme specified by URI: " + versionScheme); } } else { item = repository.getRepositoryItem(itemUri, itemNamespace); } return item; } /** * Always returns write permissions for the local repository. * * @see org.opentravel.schemacompiler.repository.RemoteRepository#getUserAuthorization(java.lang.String) */ @Override public RepositoryPermission getUserAuthorization(String baseNamespace) throws RepositoryException { return RepositoryPermission.WRITE; } /** * @see org.opentravel.schemacompiler.repository.Repository#createRootNamespace(java.lang.String) */ @Override public void createRootNamespace(String rootNamespace) throws RepositoryException { String rootNS = RepositoryNamespaceUtils.normalizeUri(rootNamespace); boolean success = false; try { fileManager.startChangeSet(); File rootNSFolder = fileManager.getNamespaceFolder(rootNS, null); File nsidFile = new File(rootNSFolder, RepositoryFileManager.NAMESPACE_ID_FILENAME); // Validation Check - Make sure the namespace follows a proper URL format try { URL nsUrl = new URL(rootNamespace); if ((nsUrl.getProtocol() == null) || (nsUrl.getAuthority() == null)) { throw new MalformedURLException(); // URLs without protocols or authorities are // not valid for the repository } if (rootNamespace.indexOf('?') >= 0) { throw new RepositoryException("Query strings are not allowed on root namespace URIs."); } } catch (MalformedURLException e) { throw new RepositoryException("The root namespace does not conform to the required URI format."); } // Validation Check - Look for a file conflict with an existing namespace if (nsidFile.exists()) { throw new RepositoryException( "The root namespace cannot be created because it conflicts with an existing one."); } // Validation Check - Check to see if the new root is a parent or child of an existing // root namespace String repositoryBaseFolder = fileManager.getRepositoryLocation().getAbsolutePath(); List<String> existingRootNSFolderPaths = new ArrayList<String>(); String rootNSTestPath = rootNSFolder.getAbsolutePath(); if (!rootNSTestPath.endsWith("/")) { rootNSTestPath += "/"; } for (String existingRootNS : listRootNamespaces()) { File existingRootNSFolder = fileManager.getNamespaceFolder(existingRootNS, null); if (rootNSTestPath.startsWith(existingRootNSFolder.getAbsolutePath())) { throw new RepositoryException( "The root namespace cannot be created because it conflicts with an existing one."); } while ((existingRootNSFolder != null) && !repositoryBaseFolder.equals(existingRootNSFolder.getAbsolutePath())) { existingRootNSFolderPaths.add(existingRootNSFolder.getAbsolutePath()); existingRootNSFolder = existingRootNSFolder.getParentFile(); } } if (existingRootNSFolderPaths.contains(rootNSFolder.getAbsolutePath())) { throw new RepositoryException( "The root namespace cannot be created because it conflicts with an existing one."); } // Create the new root namespace if all of the validation checks passed fileManager.createNamespaceIdFiles(rootNS); rootNamespaces.add(rootNS); saveLocalRepositoryMetadata(); success = true; log.info("Successfully created root namespace: " + rootNS + " by " + fileManager.getCurrentUserId()); } finally { // Commit or roll back the changes based on the result of the operation if (success) { fileManager.commitChangeSet(); } else { try { fileManager.rollbackChangeSet(); } catch (Throwable t) { log.error("Error rolling back the current change set.", t); } } } } /** * @see org.opentravel.schemacompiler.repository.Repository#deleteRootNamespace(java.lang.String) */ @Override public void deleteRootNamespace(String rootNamespace) throws RepositoryException { String rootNS = RepositoryNamespaceUtils.normalizeUri(rootNamespace); boolean success = false; try { fileManager.startChangeSet(); if (!rootNamespaces.contains(rootNS)) { throw new RepositoryException("The root namespace to be deleted is not valid."); } if (!listNamespaceChildren(rootNS).isEmpty()) { throw new RepositoryException("The root namespace cannot be deleted because it is not empty."); } fileManager.deleteNamespaceIdFile(rootNS, true); rootNamespaces.remove(rootNS); saveLocalRepositoryMetadata(); success = true; log.info("Successfully deleted root namespace: " + rootNS + " by " + fileManager.getCurrentUserId()); } finally { // Commit or roll back the changes based on the result of the operation if (success) { fileManager.commitChangeSet(); } else { try { fileManager.rollbackChangeSet(); } catch (Throwable t) { log.error("Error rolling back the current change set.", t); } } } } /** * @see org.opentravel.schemacompiler.repository.Repository#createNamespace(java.lang.String) */ @Override public void createNamespace(String baseNamespace) throws RepositoryException { String baseNS = RepositoryNamespaceUtils.normalizeUri(baseNamespace); boolean success = false; try { fileManager.startChangeSet(); File nsidFile = new File(fileManager.getNamespaceFolder(baseNS, null), RepositoryFileManager.NAMESPACE_ID_FILENAME); if (nsidFile.exists()) { throw new RepositoryException( "The namespace cannot be created because it conflicts with an existing one."); } fileManager.createNamespaceIdFiles(baseNS); success = true; log.info("Successfully created namespace: " + baseNS); } finally { // Commit or roll back the changes based on the result of the operation if (success) { fileManager.commitChangeSet(); } else { try { fileManager.rollbackChangeSet(); } catch (Throwable t) { log.error("Error rolling back the current change set.", t); } } } } /** * @see org.opentravel.schemacompiler.repository.Repository#deleteNamespace(java.lang.String) */ @Override public void deleteNamespace(String baseNamespace) throws RepositoryException { String baseNS = RepositoryNamespaceUtils.normalizeUri(baseNamespace); boolean success = false; try { fileManager.startChangeSet(); fileManager.deleteNamespaceIdFile(baseNS, false); success = true; log.info("Successfully deleted namespace: " + baseNS); } finally { // Commit or roll back the changes based on the result of the operation if (success) { fileManager.commitChangeSet(); } else { try { fileManager.rollbackChangeSet(); } catch (Throwable t) { log.error("Error rolling back the current change set.", t); } } } } /** * @see org.opentravel.schemacompiler.repository.Repository#publish(java.io.InputStream, * java.lang.String, java.lang.String, java.lang.String, java.lang.String, * java.lang.String, org.opentravel.schemacompiler.model.TLLibraryStatus) */ @Override public RepositoryItem publish(InputStream unmanagedContent, String filename, String libraryName, String namespace, String versionIdentifier, String versionScheme, TLLibraryStatus initialStatus) throws RepositoryException { String targetNS = RepositoryNamespaceUtils.normalizeUri(namespace); boolean success = false; try { log.info("Publishing '" + filename + "' to namespace '" + targetNS + "'"); fileManager.startChangeSet(); // Check to see if the library has already been published to the repository String baseNamespace = targetNS; if (versionScheme != null) { VersionScheme vScheme = VersionSchemeFactory.getInstance().getVersionScheme(versionScheme); if (vScheme.isValidNamespace(targetNS)) { baseNamespace = vScheme.getBaseNamespace(targetNS); } else { throw new RepositoryException("Cannot publish '" + filename + "' because its namespace is not valid for the assigned version scheme."); } } try { fileManager.loadLibraryMetadata(baseNamespace, filename, versionIdentifier); throw new IllegalStateException(); } catch (IllegalStateException e) { throw new RepositoryException("Unable to publish - the library '" + filename + "' has already been published to a repository."); } catch (RepositoryException e) { // Happy path - the item must not yet exist in the repository } // Create the namespace folder (if it does not already exist) fileManager.createNamespaceIdFiles(baseNamespace); // Save the library meta-data file LibraryInfoType libraryMetadata = new LibraryInfoType(); libraryMetadata.setNamespace(targetNS); libraryMetadata.setBaseNamespace(baseNamespace); libraryMetadata.setFilename(filename); libraryMetadata.setLibraryName(libraryName); libraryMetadata.setVersion(versionIdentifier); libraryMetadata.setVersionScheme(versionScheme); libraryMetadata.setStatus(initialStatus.toRepositoryStatus()); libraryMetadata.setState(RepositoryState.MANAGED_UNLOCKED); libraryMetadata.setOwningRepository(localRepositoryId); File metadataFile = fileManager.saveLibraryMetadata(libraryMetadata); log.info("Library metadata saved: " + metadataFile.getAbsolutePath()); File contentFile = new File(metadataFile.getParent(), filename); // Save the library content fileManager.saveFile(contentFile, unmanagedContent); log.info("Library content saved: " + contentFile.getAbsolutePath()); // Build and return the repository item to represent the content we just published RepositoryItem publishedItem = RepositoryUtils.createRepositoryItem(this, libraryMetadata); success = true; log.info("Content '" + filename + "' published successfully to namespace '" + baseNamespace + "'"); return publishedItem; } catch (VersionSchemeException e) { throw new RepositoryException(e.getMessage(), e); } finally { // Close all streams try { if (unmanagedContent != null) unmanagedContent.close(); } catch (Throwable t) { } // Commit or roll back the changes based on the result of the operation if (success) { fileManager.commitChangeSet(); } else { try { fileManager.rollbackChangeSet(); } catch (Throwable t) { log.error("Error rolling back the current change set.", t); } } } } /** * @see org.opentravel.schemacompiler.repository.Repository#commit(org.opentravel.schemacompiler.repository.RepositoryItem) */ @Override public void commit(RepositoryItem item) throws RepositoryException { if (item.getRepository() == this) { InputStream wipContent = null; try { File wipFile = fileManager.getLibraryWIPContentLocation(item.getBaseNamespace(), item.getFilename()); if (!wipFile.exists()) { throw new RepositoryException("The work-in-process file does not exist: " + item.getFilename()); } wipContent = new FileInputStream(wipFile); commit(item, wipContent); } catch (IOException e) { throw new RepositoryException("The work-in-process file cannot be accessed: " + item.getFilename()); } } else { ((RemoteRepository) item.getRepository()).commit(item); } } /** * Commits the content of the specified <code>RepositoryItem</code> by updating its repository * contents to match the data obtained from the input stream for the work-in-process content * provided. * * @param item * the repository item to commit * @param wipContent * the work-in-process content that will replace the current content of the * repository item * @throws RepositoryException * thrown if the file content cannot be committed or is not yet locked by the * current user */ public void commit(RepositoryItem item, InputStream wipContent) throws RepositoryException { String baseNS = RepositoryNamespaceUtils.normalizeUri(item.getBaseNamespace()); LibraryInfoType libraryMetadata = fileManager.loadLibraryMetadata(baseNS, item.getFilename(), item.getVersion()); if (((libraryMetadata.getState() != RepositoryState.MANAGED_LOCKED) && (item.getState() != RepositoryItemState.MANAGED_WIP)) || (item.getLockedByUser() == null) || !item.getLockedByUser().equalsIgnoreCase(libraryMetadata.getLockedBy())) { throw new RepositoryException( "Unable to commit - only work-in-process items can be committed to the repository."); } boolean success = false; try { log.info("Committing updated content '" + item.getFilename() + "' to namespace '" + item.getBaseNamespace() + "'"); fileManager.startChangeSet(); // Overwrite the existing content with the WIP data File contentFile = fileManager.getLibraryContentLocation(item.getBaseNamespace(), item.getFilename(), item.getVersion()); fileManager.saveFile(contentFile, wipContent); // Update the library's meta-data with the current date libraryMetadata.setLastUpdated(XMLGregorianCalendarConverter.toXMLGregorianCalendar(new Date())); fileManager.saveLibraryMetadata(libraryMetadata); log.info("Successfully committed content '" + item.getFilename() + "' to namespace '" + baseNS + "'"); success = true; } finally { try { if (wipContent != null) wipContent.close(); } catch (Throwable t) { } // Commit or roll back the changes based on the result of the operation if (success) { fileManager.commitChangeSet(); } else { try { fileManager.rollbackChangeSet(); } catch (Throwable t) { log.error("Error rolling back the current change set.", t); } } } } /** * Reverts the specified file to match the content currently stored in the item's owning * repository. * * @param item * the repository item to be reverted * @throws RepositoryException * thrown if the file content cannot be reverted */ public void revert(RepositoryItem item) throws RepositoryException { boolean success = false; InputStream in = null; try { fileManager.startChangeSet(); String baseNS = RepositoryNamespaceUtils.normalizeUri(item.getBaseNamespace()); LibraryInfoType libraryMetadata = fileManager.loadLibraryMetadata(baseNS, item.getFilename(), item.getVersion()); if (((libraryMetadata.getState() != RepositoryState.MANAGED_LOCKED) && (item.getState() != RepositoryItemState.MANAGED_WIP)) || (item.getLockedByUser() == null) || !item.getLockedByUser().equalsIgnoreCase(libraryMetadata.getLockedBy())) { throw new RepositoryException("Unable to revert - only work-in-process items can be reverted."); } // Replace the existing WIP content with the latest content from the local repository File repositoryContentFile = fileManager.getLibraryContentLocation(item.getBaseNamespace(), item.getFilename(), item.getVersion()); File wipContentFile = fileManager.getLibraryWIPContentLocation(item.getBaseNamespace(), item.getFilename()); fileManager.saveFile(wipContentFile, new FileInputStream(repositoryContentFile)); success = true; } catch (IOException e) { throw new RepositoryException("Unable to revert the repository item: " + item.getFilename(), e); } finally { // Close all open streams try { if (in != null) in.close(); } catch (Throwable t) { } // Commit or roll back the changes based on the result of the operation if (success) { fileManager.commitChangeSet(); } else { try { fileManager.rollbackChangeSet(); } catch (Throwable t) { log.error("Error rolling back the current change set.", t); } } } } /** * @see org.opentravel.schemacompiler.repository.Repository#lock(org.opentravel.schemacompiler.repository.RepositoryItem) */ @Override public void lock(RepositoryItem item) throws RepositoryException { String baseNS = RepositoryNamespaceUtils.normalizeUri(item.getBaseNamespace()); LibraryInfoType libraryMetadata = fileManager.loadLibraryMetadata(baseNS, item.getFilename(), item.getVersion()); if (libraryMetadata.getState() != RepositoryState.MANAGED_UNLOCKED) { throw new RepositoryException("Unable to obtain lock - item is not currently unlocked"); } if (libraryMetadata.getStatus() == LibraryStatus.FINAL) { throw new RepositoryException("Unable to obtain lock - only draft repository items can be edited"); } if (item.getRepository() == this) { boolean success = false; try { log.info("Locking content for '" + item.getFilename() + "' to namespace '" + baseNS + "'"); fileManager.startChangeSet(); libraryMetadata.setState(RepositoryState.MANAGED_LOCKED); libraryMetadata.setLockedBy(item.getLockedByUser()); libraryMetadata.setLastUpdated(XMLGregorianCalendarConverter.toXMLGregorianCalendar(new Date())); fileManager.saveLibraryMetadata(libraryMetadata); ((RepositoryItemImpl) item).setState(RepositoryItemState.MANAGED_WIP); log.info("Successfully locked content for '" + item.getFilename() + "' to namespace '" + baseNS + "'"); success = true; } finally { // Commit or roll back the changes based on the result of the operation if (success) { fileManager.commitChangeSet(); } else { try { fileManager.rollbackChangeSet(); } catch (Throwable t) { log.error("Error rolling back the current change set.", t); } } } } else { ((RemoteRepository) item.getRepository()).lock(item); } } /** * @see org.opentravel.schemacompiler.repository.Repository#unlock(org.opentravel.schemacompiler.repository.RepositoryItem, * boolean) */ @Override public void unlock(RepositoryItem item, boolean commitWIP) throws RepositoryException { File wipFile = fileManager.getLibraryWIPContentLocation(item.getBaseNamespace(), item.getFilename()); if (item.getRepository() == this) { InputStream wipContent = null; try { if (commitWIP) { if (!wipFile.exists()) { throw new RepositoryException( "The work-in-process file does not exist: " + item.getFilename()); } wipContent = new FileInputStream(wipFile); } unlock(item, wipContent); } catch (IOException e) { throw new RepositoryException("The work-in-process file cannot be accessed: " + item.getFilename()); } finally { try { if (wipContent != null) wipContent.close(); } catch (Throwable t) { } } } else { ((RemoteRepository) item.getRepository()).unlock(item, commitWIP); } // Rename WIP file to ".bak" since the 'real' content is now managed by the repository. File backupFile = new File(wipFile.getParentFile(), ProjectFileUtils.getBackupFilename(wipFile)); if (backupFile.exists()) { // delete the old backup, if one exists backupFile.delete(); } wipFile.renameTo(backupFile); } /** * Locks the specified repository item using the credentials for its owning repository. If the * 'wipContent' stream is provided, the work-in-process conent of the will be committed to the * remote repository before the existing lock is released. If the stream parameter is null, any * changes in the item's WIP content will be discarded. * * @param item * the repository item to lock * @param wipContent * stream for content that should be committed before the item's lock is released * (may be null) * @throws RepositoryException * thrown if the file content cannot be unlocked or is not yet locked by the current * user */ public void unlock(RepositoryItem item, InputStream wipContent) throws RepositoryException { String baseNS = RepositoryNamespaceUtils.normalizeUri(item.getBaseNamespace()); LibraryInfoType libraryMetadata = fileManager.loadLibraryMetadata(baseNS, item.getFilename(), item.getVersion()); if (((libraryMetadata.getState() != RepositoryState.MANAGED_LOCKED) && (item.getState() != RepositoryItemState.MANAGED_WIP)) || (item.getLockedByUser() == null) || !item.getLockedByUser().equalsIgnoreCase(libraryMetadata.getLockedBy())) { throw new RepositoryException("Unable to release lock - item is not currently locked"); } boolean success = false; try { log.info("Unlocking content for '" + item.getFilename() + "' to namespace '" + baseNS + "'"); // Commit the existing WIP content if requested by the caller if (wipContent != null) { commit(item, wipContent); } fileManager.startChangeSet(); // Update the meta-data to release the lock libraryMetadata.setState(RepositoryState.MANAGED_UNLOCKED); libraryMetadata.setLockedBy(null); libraryMetadata.setLastUpdated(XMLGregorianCalendarConverter.toXMLGregorianCalendar(new Date())); fileManager.saveLibraryMetadata(libraryMetadata); ((RepositoryItemImpl) item).setState(RepositoryItemState.MANAGED_UNLOCKED); log.info( "Successfully unlocked content for '" + item.getFilename() + "' to namespace '" + baseNS + "'"); success = true; } finally { // Commit or roll back the changes based on the result of the operation if (success) { fileManager.commitChangeSet(); } else { try { fileManager.rollbackChangeSet(); } catch (Throwable t) { log.error("Error rolling back the current change set.", t); } } } } /** * @see org.opentravel.schemacompiler.repository.Repository#promote(org.opentravel.schemacompiler.repository.RepositoryItem) */ @Override public void promote(RepositoryItem item) throws RepositoryException { String baseNS = RepositoryNamespaceUtils.normalizeUri(item.getBaseNamespace()); LibraryInfoType libraryMetadata = fileManager.loadLibraryMetadata(baseNS, item.getFilename(), item.getVersion()); File contentFile = fileManager.getLibraryContentLocation(baseNS, item.getFilename(), item.getVersion()); if (libraryMetadata.getState() != RepositoryState.MANAGED_UNLOCKED) { throw new RepositoryException("Unable to promote - the item is currently locked for editing."); } if (libraryMetadata.getStatus() != LibraryStatus.DRAFT) { throw new RepositoryException( "Unable to promote - only user-defined libraries in DRAFT status can be promoted."); } if (item.getRepository() == this) { boolean success = false; try { fileManager.startChangeSet(); // Change the status of the library metadata and content (if necessary) to FINAL TLLibrary libraryContent = loadOtmLibraryContent(contentFile); if (libraryContent != null) { libraryContent.setStatus(TLLibraryStatus.FINAL); } libraryMetadata.setStatus(LibraryStatus.FINAL); libraryMetadata.setLastUpdated(XMLGregorianCalendarConverter.toXMLGregorianCalendar(new Date())); // Save the changes and update the repository item sent for this method call if (libraryContent != null) { LibraryModelSaver modelSaver = new LibraryModelSaver(); fileManager.addToChangeSet(contentFile); modelSaver.getSaveHandler().setCreateBackupFile(false); modelSaver.saveLibrary(libraryContent); } fileManager.saveLibraryMetadata(libraryMetadata); if (!(item instanceof ProjectItem)) { // Only required for non-ProjectItems; ProjectItem derives the status value from // its library content ((RepositoryItemImpl) item).setStatus(TLLibraryStatus.FINAL); } log.info("Successfully promoted managed item '" + item.getFilename() + "' to FINAL status by " + fileManager.getCurrentUserId()); success = true; } catch (Exception e) { throw new RepositoryException("Unable to promote the repository item: " + item.getFilename(), e); } finally { // Commit or roll back the changes based on the result of the operation if (success) { fileManager.commitChangeSet(); } else { try { fileManager.rollbackChangeSet(); } catch (Throwable t) { log.error("Error rolling back the current change set.", t); } } } } else { ((RemoteRepository) item.getRepository()).promote(item); } } /** * @see org.opentravel.schemacompiler.repository.Repository#demote(org.opentravel.schemacompiler.repository.RepositoryItem) */ @Override public void demote(RepositoryItem item) throws RepositoryException { String baseNS = RepositoryNamespaceUtils.normalizeUri(item.getBaseNamespace()); LibraryInfoType libraryMetadata = fileManager.loadLibraryMetadata(baseNS, item.getFilename(), item.getVersion()); File contentFile = fileManager.getLibraryContentLocation(baseNS, item.getFilename(), item.getVersion()); if (libraryMetadata.getState() != RepositoryState.MANAGED_UNLOCKED) { throw new RepositoryException("Unable to demote - the item is currently locked for editing."); } if (libraryMetadata.getStatus() != LibraryStatus.FINAL) { throw new RepositoryException( "Unable to demote - only user-defined libraries in FINAL status can be demoted."); } if (item.getRepository() == this) { boolean success = false; try { fileManager.startChangeSet(); // Change the status of the library metadata and content (if necessary) to FINAL TLLibrary libraryContent = loadOtmLibraryContent(contentFile); if (libraryContent != null) { libraryContent.setStatus(TLLibraryStatus.DRAFT); } libraryMetadata.setStatus(LibraryStatus.DRAFT); libraryMetadata.setLastUpdated(XMLGregorianCalendarConverter.toXMLGregorianCalendar(new Date())); if (libraryContent != null) { LibraryModelSaver modelSaver = new LibraryModelSaver(); fileManager.addToChangeSet(contentFile); modelSaver.getSaveHandler().setCreateBackupFile(false); modelSaver.saveLibrary(libraryContent); } fileManager.saveLibraryMetadata(libraryMetadata); if (!(item instanceof ProjectItem)) { // Only required for non-ProjectItems; ProjectItem derives the status value from // its library content ((RepositoryItemImpl) item).setStatus(TLLibraryStatus.DRAFT); } log.info("Successfully demoted managed item '" + item.getFilename() + "' to DRAFT status by " + fileManager.getCurrentUserId()); success = true; } catch (Exception e) { throw new RepositoryException("Unable to promote the repository item: " + item.getFilename(), e); } finally { // Commit or roll back the changes based on the result of the operation if (success) { fileManager.commitChangeSet(); } else { try { fileManager.rollbackChangeSet(); } catch (Throwable t) { log.error("Error rolling back the current change set.", t); } } } } else { ((RemoteRepository) item.getRepository()).demote(item); } } /** * @see org.opentravel.schemacompiler.repository.Repository#recalculateCrc(org.opentravel.schemacompiler.repository.RepositoryItem) */ @Override public void recalculateCrc(RepositoryItem item) throws RepositoryException { String baseNS = RepositoryNamespaceUtils.normalizeUri(item.getBaseNamespace()); LibraryInfoType libraryMetadata = fileManager.loadLibraryMetadata(baseNS, item.getFilename(), item.getVersion()); File contentFile = fileManager.getLibraryContentLocation(baseNS, item.getFilename(), item.getVersion()); if (libraryMetadata.getStatus() != LibraryStatus.FINAL) { throw new RepositoryException( "Unable to demote - CRC values can only be recalculated for user-defined libraries in FINAL status."); } if (item.getRepository() == this) { boolean success = false; try { fileManager.startChangeSet(); // Re-save the library content; this will force a recalculation of the CRC value TLLibrary libraryContent = loadOtmLibraryContent(contentFile); if (libraryContent != null) { LibraryModelSaver modelSaver = new LibraryModelSaver(); fileManager.addToChangeSet(contentFile); libraryContent.setStatus(TLLibraryStatus.FINAL); // just in case the library and // its meta-data are out of // sync modelSaver.getSaveHandler().setCreateBackupFile(false); modelSaver.saveLibrary(libraryContent); } libraryMetadata.setLastUpdated(XMLGregorianCalendarConverter.toXMLGregorianCalendar(new Date())); fileManager.saveLibraryMetadata(libraryMetadata); log.info("Successfully recalculated CRC for item '" + item.getFilename() + "' by " + fileManager.getCurrentUserId()); success = true; } catch (Exception e) { throw new RepositoryException( "Unable to recalculate the CRC for repository item: " + item.getFilename(), e); } finally { // Commit or roll back the changes based on the result of the operation if (success) { fileManager.commitChangeSet(); } else { try { fileManager.rollbackChangeSet(); } catch (Throwable t) { log.error("Error rolling back the current change set.", t); } } } } else { ((RemoteRepository) item.getRepository()).recalculateCrc(item); } } /** * @see org.opentravel.schemacompiler.repository.Repository#delete(org.opentravel.schemacompiler.repository.RepositoryItem) */ @Override public void delete(RepositoryItem item) throws RepositoryException { if (item.getRepository() == this) { String baseNS = RepositoryNamespaceUtils.normalizeUri(item.getBaseNamespace()); boolean success = false; try { fileManager.startChangeSet(); File metadataFile = fileManager.getLibraryMetadataLocation(baseNS, item.getFilename(), item.getVersion()); File contentFile = fileManager.getLibraryContentLocation(baseNS, item.getFilename(), item.getVersion()); fileManager.addToChangeSet(metadataFile); fileManager.addToChangeSet(contentFile); if (!metadataFile.delete()) { throw new RepositoryException( "Unable to delete library meta-data file: " + metadataFile.getAbsolutePath()); } if (!contentFile.delete()) { throw new RepositoryException( "Unable to delete library content file: " + contentFile.getAbsolutePath()); } log.info("Successfully deleted managed item '" + item.getFilename() + "', by " + fileManager.getCurrentUserId()); success = true; } finally { // Commit or roll back the changes based on the result of the operation if (success) { fileManager.commitChangeSet(); } else { try { fileManager.rollbackChangeSet(); } catch (Throwable t) { log.error("Error rolling back the current change set.", t); } } } } else { ((RemoteRepository) item.getRepository()).delete(item); } } /** * Returns the URL location of the repository item's content. * * @param item * the repository item whose location is to be returned * @return URL * @throws RepositoryException * thrown if the content is not available */ public URL getContentLocation(RepositoryItem item) throws RepositoryException { String baseNS = RepositoryNamespaceUtils.normalizeUri(item.getBaseNamespace()); URL contentLocation; if (item.getState() == RepositoryItemState.MANAGED_WIP) { contentLocation = URLUtils.toURL(fileManager.getLibraryWIPContentLocation(baseNS, item.getFilename())); } else { Repository repository = item.getRepository(); File itemFile = fileManager.getLibraryContentLocation(baseNS, item.getFilename(), item.getVersion()); if ((repository instanceof RemoteRepository) && !itemFile.exists()) { ((RemoteRepository) repository).downloadContent(item, false); } contentLocation = URLUtils.toURL(itemFile); } return contentLocation; } /** * If the given <code>RepositoryItem</code> is owned by a remote repository, the local * repository's copy is updated with the latest available content. If the item is owned by the * local repository, this method has no effect. This update is performed regardless of the * update policy for the repository that owns and manages the item. * * <p>This method will return true if the item's local copy was replaced by new content from * the remote repository. * * @param item * the repository item to refresh * @throws RepositoryException * thrown if the file content cannot be locked by the current user */ public boolean refreshLocalCopy(RepositoryItem item) throws RepositoryException { Repository repository = item.getRepository(); boolean isRefreshed = false; if (repository instanceof RemoteRepository) { isRefreshed = ((RemoteRepository) repository).downloadContent(item, true); } return isRefreshed; } /** * Saves the configuration settings for the local repository. * * NOTE: Although the repository metadata will be saved by this method, the active change set * will not be committed. It is the responsibility of the calling method to commit or roll back * the change set depending on the success of the end-to-end transaction. * * @throws RepositoryException * thrown if the local repository's configuration settings cannot be updated */ public void saveLocalRepositoryMetadata() throws RepositoryException { List<String> rootNamespaces = this.rootNamespaces; boolean success = false; // Get the list of root namespaces from the existing file if the list has not yet been // initialized. They // are not accessible from local repository fields because a default namespace is always // published for the // local repository. if (rootNamespaces == null) { try { RepositoryInfoType fileMetadata = fileManager.loadRepositoryMetadata(); rootNamespaces = fileMetadata.getRootNamespace(); } catch (RepositoryException e) { // Not an error if the file does not yet exist - just use an empty list rootNamespaces = new ArrayList<String>(); } } try { RepositoryInfoType repositoryMetadata = new RepositoryInfoType(); repositoryMetadata.setID(localRepositoryId); repositoryMetadata.setDisplayName(localRepositoryDisplayName); repositoryMetadata.getRootNamespace().addAll(rootNamespaces); repositoryMetadata.setRemoteRepositories(new RemoteRepositoriesType()); for (RemoteRepositoryClient remoteRepository : remoteRepositories) { RemoteRepositoryType jaxbRepository = new RemoteRepositoryType(); jaxbRepository.setID(remoteRepository.getId()); jaxbRepository.setDisplayName(remoteRepository.getDisplayName()); jaxbRepository.setEndpointUrl(remoteRepository.getEndpointUrl()); jaxbRepository.setRefreshPolicy(remoteRepository.getRefreshPolicy()); jaxbRepository.getRootNamespace().addAll(remoteRepository.listRootNamespaces()); jaxbRepository.setUserID(remoteRepository.getUserId()); jaxbRepository.setPassword(ENCRYPTED_PASSWORD_PREFIX + remoteRepository.getEncryptedPassword()); repositoryMetadata.getRemoteRepositories().getRemoteRepository().add(jaxbRepository); } fileManager.saveRepositoryMetadata(repositoryMetadata); success = true; } finally { // If an error occurred, refresh all local settings to make sure we are still in-sync // with the // local file system try { if (!success) { refreshLocalRepositoryInfo(true); } } catch (Throwable t) { } } } /** * Initializes the settings for the local OTA2.0 repository. If a local repository does not yet * exist, one is created automatically. * * @throws RepositoryException * thrown if the local repository cannot be accessed */ private void initializeLocalRepositoryInfo() throws RepositoryException { // If a repository does not already exist, create one automatically if (fileManager.getRepositoryMetadataLastUpdated() == null) { String newRepositoryId; try { newRepositoryId = InetAddress.getLocalHost().getHostName(); } catch (UnknownHostException e) { // Not an error - just use 'localhost' newRepositoryId = "localhost"; } createLocalRepository(newRepositoryId, "Local OTA2.0 Repository"); } // Populate this singleton instance with the repository's meta-data properties RepositoryInfoType repositoryInfo = fileManager.loadRepositoryMetadata(); this.localRepositoryId = repositoryInfo.getID(); this.localRepositoryDisplayName = repositoryInfo.getDisplayName(); this.rootNamespaces = repositoryInfo.getRootNamespace(); this.lastUpdatedDate = new Date(); for (RemoteRepositoryType jaxbRemoteRepository : repositoryInfo.getRemoteRepositories() .getRemoteRepository()) { RemoteRepositoryClient remoteRepositoryClient = new RemoteRepositoryClient(this); try { copyRemoteRepositorySettings(jaxbRemoteRepository, remoteRepositoryClient); remoteRepositoryClient.refreshRepositoryMetadata(); } catch (RepositoryException e) { // Ignore and use locally-cached data } this.remoteRepositories.add(remoteRepositoryClient); } } /** * Checks the last-updated date of the local repository's meta-data and reloads it if required. * * @param forceRefresh * indicates whether the metadata should be refreshed regardless of the file's * last-upated timestamp */ private void refreshLocalRepositoryInfo(boolean forceRefresh) { try { Date actualLastUpdated = fileManager.getRepositoryMetadataLastUpdated(); if (actualLastUpdated == null) { log.error("OTA2.0 repository not found at location: " + fileManager.getRepositoryLocation().getAbsolutePath()); } else if (forceRefresh || (lastUpdatedDate == null) || actualLastUpdated.after(lastUpdatedDate)) { RepositoryInfoType repositoryInfo = fileManager.loadRepositoryMetadata(); Map<String, RemoteRepositoryClient> oldRepositories = new HashMap<String, RemoteRepositoryClient>(); Map<String, RemoteRepositoryType> newRepositories = new HashMap<String, RemoteRepositoryType>(); // Build a catalog of all the new and existing repositories, indexed by ID for (RemoteRepositoryClient oldRepository : remoteRepositories) { oldRepositories.put(oldRepository.getId(), oldRepository); } for (RemoteRepositoryType newRepository : repositoryInfo.getRemoteRepositories() .getRemoteRepository()) { newRepositories.put(newRepository.getID(), newRepository); } // Refresh the remote repository content by updating, adding, and deleting as needed Iterator<RemoteRepositoryClient> iterator = remoteRepositories.iterator(); while (iterator.hasNext()) { RemoteRepositoryClient oldRepository = iterator.next(); if (!newRepositories.containsKey(oldRepository.getId())) { iterator.remove(); } } for (RemoteRepositoryType newRepository : newRepositories.values()) { RemoteRepositoryClient oldRepository = oldRepositories.get(newRepository.getID()); if (oldRepository == null) { oldRepository = new RemoteRepositoryClient(this); remoteRepositories.add(oldRepository); } try { copyRemoteRepositorySettings(newRepository, oldRepository); oldRepository.refreshRepositoryMetadata(); } catch (RepositoryException e) { // Ignore and use locally-cached data } } // Update the remaining meta-data fields this.localRepositoryId = repositoryInfo.getID(); this.localRepositoryDisplayName = repositoryInfo.getDisplayName(); this.rootNamespaces = repositoryInfo.getRootNamespace(); this.lastUpdatedDate = new Date(); } } catch (RepositoryException e) { log.warn("Unable to refresh contents of local OTA2.0 repository. Reason: " + ExceptionUtils.getExceptionMessage(e)); } } /** * Copies all settings from the JAXB remote repository instance to the object used to represent * that repository within the schema compiler's framework. * * @param jaxbRemoteRepository * the JAXB object containing the remote repository configuration settings * @param remoteRepositoryImpl * the schema compiler's remote repository object instance */ private void copyRemoteRepositorySettings(RemoteRepositoryType jaxbRemoteRepository, RemoteRepositoryClient remoteRepository) { remoteRepository.setId(jaxbRemoteRepository.getID()); remoteRepository.setDisplayName(jaxbRemoteRepository.getDisplayName()); remoteRepository.setEndpointUrl(jaxbRemoteRepository.getEndpointUrl()); remoteRepository.setRootNamespaces(jaxbRemoteRepository.getRootNamespace()); remoteRepository.setRefreshPolicy(jaxbRemoteRepository.getRefreshPolicy()); remoteRepository.setUserId(jaxbRemoteRepository.getUserID()); // If the password is already encrypted, use the value as-is; otherwise encrypt it now String jaxbPassword = jaxbRemoteRepository.getPassword(); if (jaxbPassword != null) { String encryptedPassword; if (jaxbPassword.startsWith(ENCRYPTED_PASSWORD_PREFIX)) { encryptedPassword = jaxbPassword.substring(ENCRYPTED_PASSWORD_PREFIX.length()); } else { encryptedPassword = PasswordHelper.encrypt(jaxbPassword); } remoteRepository.setEncryptedPassword(encryptedPassword); } else { remoteRepository.setEncryptedPassword(null); } } /** * Attempts to load the content of the specified file as an OTM library. If any non-validation * exceptions occur during the load, the file will be assumed to be a non-OTM file. In such, * cases this method will return null instead of throwing an exception. * * @param libraryFile * the library file load * @return TLLibrary */ private TLLibrary loadOtmLibraryContent(File libraryFile) { TLLibrary library = null; try { if ((libraryFile != null) && libraryFile.exists() && !libraryFile.getName().toLowerCase().endsWith(".xsd")) { LibraryModuleLoader<InputStream> loader = new MultiVersionLibraryModuleLoader(); LibraryModuleInfo<Object> moduleInfo = loader.loadLibrary(new LibraryStreamInputSource(libraryFile), new ValidationFindings()); Object jaxbLibrary = moduleInfo.getJaxbArtifact(); if (jaxbLibrary != null) { TransformerFactory<DefaultTransformerContext> transformerFactory = TransformerFactory .getInstance(SchemaCompilerApplicationContext.LOADER_TRANSFORMER_FACTORY, new DefaultTransformerContext()); ObjectTransformer<Object, TLLibrary, DefaultTransformerContext> transformer = transformerFactory .getTransformer(jaxbLibrary, TLLibrary.class); library = transformer.transform(jaxbLibrary); library.setLibraryUrl(URLUtils.toURL(libraryFile)); } } } catch (Exception e) { // No action - method will return null } return library; } }