/* * Copyright (c) 2008, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * 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.wso2.carbon.registry.extensions.handlers; import org.apache.axiom.om.OMElement; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.xerces.xni.parser.XMLInputSource; import org.wso2.carbon.registry.core.*; import org.wso2.carbon.registry.core.config.RegistryContext; import org.wso2.carbon.registry.core.exceptions.RegistryException; import org.wso2.carbon.registry.core.jdbc.handlers.Handler; import org.wso2.carbon.registry.core.jdbc.handlers.RequestContext; import org.wso2.carbon.registry.core.utils.AuthorizationUtils; import org.wso2.carbon.registry.core.utils.RegistryUtils; import org.wso2.carbon.registry.extensions.handlers.utils.SchemaProcessor; import org.wso2.carbon.registry.extensions.handlers.utils.SchemaValidator; import org.wso2.carbon.registry.extensions.utils.CommonConstants; import org.wso2.carbon.registry.extensions.utils.CommonUtil; import org.wso2.carbon.registry.extensions.utils.WSDLValidationInfo; import org.wso2.carbon.user.mgt.UserMgtConstants; import javax.xml.namespace.QName; import java.io.*; import java.util.*; public class XSDMediaTypeHandler extends Handler { private static final Log log = LogFactory.getLog(XSDMediaTypeHandler.class); private String location = "/schema/"; // location will always has a leading '/' and trailing '/' private String locationTag = "location"; private boolean disableSchemaValidation = false; private boolean disableSymlinkCreation = true; private String defaultSchemaVersion = CommonConstants.SCHEMA_VERSION_DEFAULT_VALUE; public OMElement getLocationConfiguration() { return locationConfiguration; } public void setLocationConfiguration(OMElement locationConfiguration) throws RegistryException { Iterator confElements = locationConfiguration.getChildElements(); while (confElements.hasNext()) { OMElement confElement = (OMElement) confElements.next(); if (confElement.getQName().equals(new QName(locationTag))) { location = confElement.getText(); if (!location.startsWith(RegistryConstants.PATH_SEPARATOR)) { location = RegistryConstants.PATH_SEPARATOR + location; } if (!location.endsWith(RegistryConstants.PATH_SEPARATOR)) { location = location + RegistryConstants.PATH_SEPARATOR; } } } this.locationConfiguration = locationConfiguration; } public boolean isDisableSymlinkCreation() { return disableSymlinkCreation; } public void setDisableSymlinkCreation(String disableSymlinkCreation) { this.disableSymlinkCreation = Boolean.toString(true).equals(disableSymlinkCreation); } public void setDefaultServiceVersion(String defaultSchemaVersion) { this.defaultSchemaVersion = defaultSchemaVersion; } private OMElement locationConfiguration; public void put(RequestContext requestContext) throws RegistryException { if (!CommonUtil.isUpdateLockAvailable()) { return; } CommonUtil.acquireUpdateLock(); try { Resource resource = requestContext.getResource(); String resourcePath = requestContext.getResourcePath().getPath(); String parentPath = RegistryUtils.getParentPath(resourcePath); // String sourceURL = requestContext.getSourceURL(); Registry registry = requestContext.getRegistry(); // This is to distinguish operations on xsd and wsdl on remote mounting. String remotePut = resource.getProperty(RegistryConstants.REMOTE_MOUNT_OPERATION); if (remotePut != null) { CommonUtil.releaseUpdateLock(); resource.removeProperty(RegistryConstants.REMOTE_MOUNT_OPERATION); registry.put(resourcePath, resource); requestContext.setProcessingComplete(true); return; } String oldResourcePath = null; if (registry.resourceExists(resourcePath)) { // If the resource is already there and the content is not changed, perform the default processing. // logic to compare content, and return only if the content didn't change. Object newContent = resource.getContent(); if (newContent instanceof String) { newContent = RegistryUtils.encodeString(((String) newContent)); } Resource oldResource = registry.get(resourcePath); Object oldContent = oldResource.getContent(); String newContentString = null; String oldContentString = null; if (newContent != null) { if (newContent instanceof String) { newContentString = (String) newContent; } else { newContentString = RegistryUtils.decodeBytes((byte[]) newContent); } } if (oldContent != null) { if (oldContent instanceof String) { oldContentString = (String) oldContent; } else { oldContentString = RegistryUtils.decodeBytes((byte[]) oldContent); } } if ((newContent == null && oldContent == null) || (newContentString != null && newContentString.equals(oldContentString))) { // this will continue adding from the default path. return; } oldResourcePath = resourcePath; // keep the old resource path. } WSDLValidationInfo validationInfo = null; String savedName; requestContext.setSourceURL( requestContext.getResource().getProperty(CommonConstants.SOURCEURL_PARAMETER_NAME)); String sourceURL = requestContext.getSourceURL(); if (StringUtils.isNotBlank(sourceURL)) { if (requestContext.getSourceURL().toLowerCase() .startsWith("file:")) { String msg = "The source URL must not be file in the server's local file system"; throw new RegistryException(msg); } try { if (!disableSchemaValidation) { validationInfo = SchemaValidator.validate(new XMLInputSource(null, sourceURL, null)); } } catch (Exception e) { // Since SchemaValidator.validate method is throwing Exception need to catch it here throw new RegistryException("Exception occurred while validating the schema " + sourceURL, e); } savedName = processSchemaImport(requestContext, resourcePath, validationInfo); } else { Object resourceContent = resource.getContent(); if (resourceContent instanceof String) { resourceContent = RegistryUtils.encodeString(((String) resourceContent)); resource.setContent(resourceContent); } if (resourceContent instanceof byte[]) { try { InputStream in = new ByteArrayInputStream((byte[]) resourceContent); if (!disableSchemaValidation) { // PublicId, SystemId, BaseSystemId and Encoding set to null. validationInfo = SchemaValidator. validate(new XMLInputSource(null, null, null, in, null)); } } catch (Exception e) { // Since SchemaValidator.validate method is throwing Exception need to catch it here throw new RegistryException("Exception occurred while validating the schema", e); } } savedName = processSchemaUpload(requestContext, resourcePath, validationInfo); } if (StringUtils.isNotBlank(savedName)) { onPutCompleted(resourcePath, Collections.singletonMap(sourceURL, savedName), Collections.<String>emptyList(), requestContext); requestContext.setActualPath(savedName); } requestContext.setProcessingComplete(true); } finally { CommonUtil.releaseUpdateLock(); } } public void importResource(RequestContext requestContext) throws RegistryException { if (!CommonUtil.isUpdateLockAvailable()) { return; } CommonUtil.acquireUpdateLock(); try { String parentPath = RegistryUtils.getParentPath(requestContext.getResourcePath().getPath()); String resourcePath = requestContext.getResourcePath().getCompletePath(); String sourceURL = requestContext.getSourceURL(); if (requestContext.getSourceURL() != null && requestContext.getSourceURL().toLowerCase().startsWith("file:")) { String msg = "The source URL must not be file in the server's local file system"; throw new RegistryException(msg); } WSDLValidationInfo validationInfo = null; try { if (!disableSchemaValidation) { validationInfo = SchemaValidator.validate(new XMLInputSource(null, sourceURL, null)); } } catch (Exception e) { throw new RegistryException("Exception occured while validating the schema", e); } String savedName = processSchemaImport(requestContext, resourcePath, validationInfo); if (parentPath.endsWith(RegistryConstants.PATH_SEPARATOR)) { requestContext.setActualPath(parentPath + RegistryUtils.getResourceName(savedName)); } else { requestContext.setActualPath(parentPath + RegistryConstants.PATH_SEPARATOR + RegistryUtils.getResourceName(savedName)); } onPutCompleted(resourcePath, Collections.singletonMap(sourceURL, savedName), Collections.<String>emptyList(), requestContext); requestContext.setProcessingComplete(true); } finally { CommonUtil.releaseUpdateLock(); } } /** * Method that runs the schema upload procedure. * * @param requestContext the request context for the put operation * @param resourcePath the path of the resource * @param validationInfo the validation information * @return the path at which the schema was uploaded to * @throws RegistryException if the operation failed. */ protected String processSchemaUpload(RequestContext requestContext, String resourcePath, WSDLValidationInfo validationInfo) throws RegistryException { String registryPath = null; List<File> tempFiles = makeTempDirStructure(requestContext); try { SchemaProcessor schemaProcessor = buildSchemaProcessor(requestContext, validationInfo); registryPath = schemaProcessor .putSchemaToRegistry(requestContext, resourcePath, getChrootedLocation(requestContext.getRegistryContext()), true,disableSymlinkCreation); } finally { deleteTempFiles(tempFiles); } return registryPath; } /** * creates all the tmp dirs/files created in the tmp location in the file system to perform the XML Schema update. * * @param requestContext * @return * @throws RegistryException */ private List<File> makeTempDirStructure(RequestContext requestContext) throws RegistryException { final String resourcePath = requestContext.getResource().getPath(); final Registry registry = requestContext.getRegistry(); List<File> tempFiles = new ArrayList<File>(); if (resourcePath == null) { return tempFiles; } try { // creating temp files for the wsdl and all the dependencies. Set<String> registryPaths = new LinkedHashSet<String>(); // the first resourcePath is the current resource resourcePath. registryPaths.add(resourcePath); // get the associations. Association[] dependencies = CommonUtil.getDependenciesRecursively(registry, resourcePath); if (dependencies != null) { for (Association dependency : dependencies) { String targetPath = dependency.getDestinationPath(); if (targetPath.startsWith(RegistryConstants.ROOT_PATH)) { registryPaths.add(targetPath); } } } File referenceTempFile = File.createTempFile("xsd", ".ref"); File tempDir = new File(referenceTempFile.getAbsolutePath().substring(0, referenceTempFile.getAbsolutePath().length() - ".ref".length())); String tempDirPath = tempDir.getAbsolutePath(); // now add each of the registry paths to the the tempDir for (String registryPath : registryPaths) { if (!registryPath.startsWith(RegistryConstants.ROOT_PATH)) { continue; } String filePath = tempDirPath + registryPath; File tempFile = new File(filePath); makeDirs(tempFile.getParentFile()); Object resourceContent; if (registryPath.equals(resourcePath)) { // this is the xsd we want to update. resourceContent = requestContext.getResource().getContent(); } else { if (!registry.resourceExists(registryPath)) { continue; } Resource r = registry.get(registryPath); if (r == null) { continue; } resourceContent = r.getContent(); } byte[] resourceContentBytes; if (resourceContent == null) { resourceContentBytes = new byte[0]; } else if (resourceContent instanceof byte[]) { resourceContentBytes = (byte[]) resourceContent; } else if (resourceContent instanceof String) { resourceContentBytes = RegistryUtils.encodeString(((String) resourceContent)); } else { String msg = "Unknown type for the content resourcePath: " + registryPath + ", content type: " + resourceContent.getClass().getName() + "."; log.error(msg); throw new RegistryException(msg); } InputStream in = new ByteArrayInputStream(resourceContentBytes); BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(tempFile)); byte[] contentChunk = new byte[1024]; int byteCount; while ((byteCount = in.read(contentChunk)) != -1) { out.write(contentChunk, 0, byteCount); } out.flush(); out.close(); tempFiles.add(tempFile); } if (tempFiles.size() == 0) { // unreachable state, anyway better log and return. String msg = "Temporary files count is zero, when updating a xsd. " + "xsd resourcePath: " + resourcePath + "."; log.error(msg); } File tempFile = tempFiles.get(0); String uri = tempFile.toURI().toString(); if (uri != null) { if (uri.startsWith("file:")) { uri = uri.substring(5); } while (uri.startsWith("/")) { uri = uri.substring(1); } uri = "file:///" + uri; requestContext.setSourceURL(uri); } //adding the tmp dir for delete purposes. tempFiles.add(tempDir); //adding the root tmp dir created for delete purposes. tempFiles.add(referenceTempFile); } catch (IOException ioe) { String msg = "Error in updating the XML Schema. XML Schema resourcePath: " + resourcePath + "."; log.error(msg, ioe); throw new RegistryException(msg, ioe); } return tempFiles; } /** * deletes all the tmp dirs/files created in the tmp location in the file system to perform the XML Schema update. * * @param tempFiles * @throws IOException */ private void deleteTempFiles(List<File> tempFiles) throws RegistryException { try { // now we will delete each temp files, ref file and the temp directory. final int fileSize = tempFiles.size(); if (fileSize >= 2) { for (int i = 0; i < (fileSize - 2); i++) { FileUtils.forceDelete(tempFiles.get(i)); } //deleting the root tmp dir FileUtils.deleteDirectory(tempFiles.get(fileSize - 2)); //deleting the tmp file FileUtils.forceDelete(tempFiles.get(fileSize - 1)); } } catch (IOException ioe) { String msg = "Error in updating the XML Schema. XML Schema resourcePath: " + tempFiles.get(0) + "."; log.error(msg, ioe); throw new RegistryException(msg, ioe); } } /** * creates the parent directory structure for a given resource at a temp location in the file system. * * @param file * @throws IOException */ private void makeDirs(File file) throws IOException { if (file != null && !file.exists() && !file.mkdirs()) { log.warn("Failed to create directories at path: " + file.getAbsolutePath()); } } /** * Method to customize the Schema Processor. * * @param requestContext the request context for the import/put operation. * @param validationInfo the WSDL validation information. * @return the Schema Processor instance. */ @SuppressWarnings("unused") protected SchemaProcessor buildSchemaProcessor(RequestContext requestContext, WSDLValidationInfo validationInfo) { return new SchemaProcessor(requestContext, validationInfo); } /** * Method that runs the schema import procedure. * * @param requestContext the request context for the import operation * @param resourcePath the path of the resource * @param validationInfo the validation information * @return the path at which the schema was uploaded to * @throws RegistryException if the operation failed. */ protected String processSchemaImport(RequestContext requestContext, String resourcePath, WSDLValidationInfo validationInfo) throws RegistryException { SchemaProcessor schemaProcessor = buildSchemaProcessor(requestContext, validationInfo); return schemaProcessor .importSchemaToRegistry(requestContext, resourcePath, getChrootedLocation(requestContext.getRegistryContext()), true,disableSymlinkCreation); } /** * Method that will executed after the put operation has been done. * * @param path the path of the resource. * @param addedResources the resources that have been added to the registry. * @param otherResources the resources that have not been added to the registry. * @param requestContext the request context for the put operation. * @throws RegistryException if the operation failed. */ @SuppressWarnings("unused") protected void onPutCompleted(String path, Map<String, String> addedResources, List<String> otherResources, RequestContext requestContext) throws RegistryException { } private String getChrootedLocation(RegistryContext registryContext) { return RegistryUtils.getAbsolutePath(registryContext, RegistryConstants.GOVERNANCE_REGISTRY_BASE_PATH + location); } public void setDisableSchemaValidation(String disableSchemaValidation) { this.disableSchemaValidation = Boolean.toString(true).equals(disableSchemaValidation); } @Override public void delete(RequestContext requestContext) throws RegistryException { if (!CommonUtil.isUpdateLockAvailable()) { return; } CommonUtil.acquireUpdateLock(); try { Registry registry = requestContext.getRegistry(); ResourcePath resourcePath = requestContext.getResourcePath(); if (resourcePath == null) { throw new RegistryException("The resource path is not available."); } Resource resource = registry.get(resourcePath.getPath()); } finally { CommonUtil.releaseUpdateLock(); } } }