| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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.apache.tuscany.sca.contribution.service.impl; |
| |
| import java.io.BufferedInputStream; |
| import java.io.BufferedOutputStream; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileNotFoundException; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.OutputStreamWriter; |
| import java.io.PrintWriter; |
| import java.net.MalformedURLException; |
| import java.net.URI; |
| import java.net.URL; |
| import java.net.URLConnection; |
| import java.security.AccessController; |
| import java.security.PrivilegedAction; |
| import java.security.PrivilegedActionException; |
| import java.security.PrivilegedExceptionAction; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import javax.xml.stream.XMLInputFactory; |
| import javax.xml.stream.XMLStreamConstants; |
| import javax.xml.stream.XMLStreamReader; |
| |
| import org.apache.tuscany.sca.contribution.Contribution; |
| import org.apache.tuscany.sca.contribution.service.ContributionRepository; |
| import org.apache.tuscany.sca.contribution.service.util.FileHelper; |
| import org.apache.tuscany.sca.contribution.service.util.IOHelper; |
| import org.apache.tuscany.sca.monitor.Monitor; |
| import org.apache.tuscany.sca.monitor.Problem; |
| import org.apache.tuscany.sca.monitor.Problem.Severity; |
| import org.apache.tuscany.sca.monitor.impl.ProblemImpl; |
| |
| /** |
| * The default implementation of ContributionRepository |
| * |
| * @version $Rev$ $Date$ |
| */ |
| public class ContributionRepositoryImpl implements ContributionRepository { |
| private static final String NS = "http://tuscany.apache.org/xmlns/1.0"; |
| private static final String DOMAIN_INDEX_FILENAME = "sca-domain.xml"; |
| private boolean initialized = false; |
| private String repository = null; |
| private File rootFile = null; |
| private Map<String, String> contributionLocations = new HashMap<String, String>(); |
| |
| private Map<String, Contribution> contributionMap = new HashMap<String, Contribution>(); |
| private List<Contribution> contributions = new ArrayList<Contribution>(); |
| |
| private URI domain; |
| private XMLInputFactory factory; |
| private Monitor monitor; |
| |
| /** |
| * Marshals warnings into the monitor |
| * |
| * @param message |
| * @param model |
| * @param messageParameters |
| */ |
| protected void warning(String message, Object model, String... messageParameters) { |
| if (monitor != null){ |
| Problem problem = new ProblemImpl(this.getClass().getName(), "contribution-impl-validation-messages", Severity.WARNING, model, message, (Object[])messageParameters); |
| monitor.problem(problem); |
| } |
| } |
| |
| /** |
| * Marshals errors into the monitor |
| * |
| * @param problems |
| * @param message |
| * @param model |
| */ |
| protected void error(String message, Object model, Object... messageParameters) { |
| if (monitor != null) { |
| Problem problem = new ProblemImpl(this.getClass().getName(), "contribution-impl-validation-messages", Severity.ERROR, model, message, (Object[])messageParameters); |
| monitor.problem(problem); |
| } |
| } |
| |
| /** |
| * Marshals exceptions into the monitor |
| * |
| * @param problems |
| * @param message |
| * @param model |
| */ |
| protected void error(String message, Object model, Exception ex) { |
| if (monitor != null) { |
| Problem problem = new ProblemImpl(this.getClass().getName(), "contribution-impl-validation-messages", Severity.ERROR, model, message, ex); |
| monitor.problem(problem); |
| } |
| } |
| |
| /** |
| * Constructor with repository root |
| * |
| * @param repository |
| * @param factory |
| */ |
| public ContributionRepositoryImpl(final String repository, XMLInputFactory factory, Monitor monitor) throws IOException { |
| this.monitor = monitor; |
| this.repository = repository; |
| this.factory = factory; |
| } |
| |
| |
| public URI getDomain() { |
| return domain; |
| } |
| |
| public URL store(final String contribution, URL sourceURL, InputStream contributionStream) throws IOException { |
| if(! initialized) { |
| try { |
| initializeRepository(); |
| } catch(Exception e) { |
| //ignore |
| } |
| } |
| |
| |
| // where the file should be stored in the repository |
| final File location = mapToFile(sourceURL); |
| FileHelper.forceMkdir(location.getParentFile()); |
| |
| copy(contributionStream, location); |
| |
| // add contribution to repositoryContent |
| // Allow ability to read user.dir property. Requires PropertyPermission in security policy. |
| URL contributionURL; |
| try { |
| contributionURL= AccessController.doPrivileged(new PrivilegedExceptionAction<URL>() { |
| public URL run() throws IOException { |
| URL contributionURL = location.toURL(); |
| URI relative = rootFile.toURI().relativize(location.toURI()); |
| contributionLocations.put(contribution, relative.toString()); |
| return contributionURL; |
| } |
| }); |
| } catch (PrivilegedActionException e) { |
| error("PrivilegedActionException", location, (IOException)e.getException()); |
| throw (IOException)e.getException(); |
| } |
| saveMap(); |
| |
| return contributionURL; |
| } |
| |
| public URL store(String contribution, URL sourceURL) throws IOException { |
| if(! initialized) { |
| try { |
| initializeRepository(); |
| } catch(Exception e) { |
| //ignore |
| } |
| } |
| |
| // where the file should be stored in the repository |
| File location = mapToFile(sourceURL); |
| File source = FileHelper.toFile(sourceURL); |
| if (source == null || source.isFile()) { |
| URLConnection connection = sourceURL.openConnection(); |
| connection.setUseCaches(false); |
| InputStream is = connection.getInputStream(); |
| try { |
| return store(contribution, sourceURL, is); |
| } finally { |
| IOHelper.closeQuietly(is); |
| } |
| } |
| |
| FileHelper.forceMkdir(location); |
| FileHelper.copyDirectory(source, location); |
| |
| // add contribution to repositoryContent |
| URI relative = rootFile.toURI().relativize(location.toURI()); |
| contributionLocations.put(contribution, relative.toString()); |
| saveMap(); |
| |
| return location.toURL(); |
| } |
| |
| public URL find(String contribution) { |
| if(! initialized) { |
| try { |
| initializeRepository(); |
| } catch(Exception e) { |
| //ignore |
| } |
| } |
| |
| |
| if (contribution == null) { |
| return null; |
| } |
| String location = contributionLocations.get(contribution); |
| if (location == null) { |
| return null; |
| } |
| try { |
| return new File(rootFile, location).toURL(); |
| } catch (MalformedURLException e) { |
| // Should not happen |
| error("MalformedURLException", location, new AssertionError(e)); |
| throw new AssertionError(e); |
| } |
| } |
| |
| public void remove(String contribution) { |
| if(! initialized) { |
| try { |
| initializeRepository(); |
| } catch(Exception e) { |
| //ignore |
| } |
| } |
| |
| |
| URL contributionURL = this.find(contribution); |
| if (contributionURL != null) { |
| // remove |
| try { |
| FileHelper.forceDelete(FileHelper.toFile(contributionURL)); |
| this.contributionLocations.remove(contribution); |
| saveMap(); |
| } catch (IOException ioe) { |
| // handle file could not be removed |
| } |
| } |
| } |
| |
| public List<String> list() { |
| return new ArrayList<String>(contributionLocations.keySet()); |
| } |
| |
| /** |
| * Contribution Registry methods |
| */ |
| |
| |
| public void addContribution(Contribution contribution) { |
| contributionMap.put(contribution.getURI(), contribution); |
| contributions.add(contribution); |
| } |
| |
| public void removeContribution(Contribution contribution) { |
| contributionMap.remove(contribution.getURI()); |
| contributions.remove(contribution); |
| } |
| |
| public void updateContribution(Contribution contribution) { |
| Contribution oldContribution = contributionMap.remove(contribution.getURI()); |
| contributions.remove(oldContribution); |
| contributionMap.put(contribution.getURI(), contribution); |
| contributions.add(contribution); |
| } |
| |
| public Contribution getContribution(String uri) { |
| return contributionMap.get(uri); |
| } |
| |
| public List<Contribution> getContributions() { |
| return Collections.unmodifiableList(contributions); |
| } |
| |
| |
| /** |
| * Resolve contribution location in the repository -> root repository / |
| * contribution file -> contribution group id / artifact id / version |
| * |
| * @param contribution |
| * @return |
| */ |
| private File mapToFile(URL sourceURL) { |
| String fileName = FileHelper.toFile(sourceURL).getName(); |
| return new File(rootFile, "contributions" + File.separator + fileName); |
| } |
| |
| |
| /** |
| * Persist contribution state to xml file in the repository |
| */ |
| private void saveMap() { |
| File domainFile = new File(rootFile, DOMAIN_INDEX_FILENAME); |
| FileOutputStream os = null; |
| try { |
| os = new FileOutputStream(domainFile); |
| PrintWriter writer = new PrintWriter(new OutputStreamWriter(os, "UTF-8")); |
| writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); |
| writer.println("<domain uri=\"" + getDomain() + "\" xmlns=\"" + NS + "\">"); |
| for (Map.Entry<String, String> e : contributionLocations.entrySet()) { |
| writer.println(" <contribution uri=\"" + e.getKey() + "\" location=\"" + e.getValue() + "\"/>"); |
| } |
| writer.println("</domain>"); |
| writer.flush(); |
| } catch (IOException e) { |
| IllegalArgumentException ae = new IllegalArgumentException(e); |
| error("IllegalArgumentException", os, ae); |
| throw ae; |
| } finally { |
| IOHelper.closeQuietly(os); |
| } |
| } |
| |
| /** |
| * Initialize contribution repository |
| * @throws IOException |
| */ |
| private void initializeRepository() throws IOException { |
| String root = this.repository; |
| if (root == null) { |
| root = AccessController.doPrivileged(new PrivilegedAction<String>() { |
| public String run() { |
| // Default to <user.home>/.tuscany/domains/local/ |
| String userHome = System.getProperty("user.home"); |
| String slash = File.separator; |
| return userHome + slash + ".tuscany" + slash + "domains" + slash + "local" + slash; |
| } |
| }); |
| } |
| |
| // Allow privileged access to File. Requires FilePermission in security policy file. |
| final String finalRoot = root; |
| this.rootFile = AccessController.doPrivileged(new PrivilegedAction<File>() { |
| public File run() { |
| return new File(finalRoot); |
| } |
| }); |
| |
| // Allow privileged access to File. Requires FilePermission in security policy file. |
| this.domain = AccessController.doPrivileged(new PrivilegedAction<URI>() { |
| public URI run() { |
| return rootFile.toURI(); |
| } |
| }); |
| |
| // Allow privileged access to mkdir. Requires FilePermission in security policy file. |
| try { |
| AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() { |
| public Object run() throws IOException { |
| FileHelper.forceMkdir(rootFile); |
| return null; |
| } |
| }); |
| } catch (PrivilegedActionException e) { |
| error("PrivilegedActionException", rootFile, (IOException)e.getException()); |
| throw (IOException)e.getException(); |
| } |
| |
| // Allow privileged access to test file. Requires FilePermissions in security policy file. |
| Boolean notDirectory = AccessController.doPrivileged(new PrivilegedAction<Boolean>() { |
| public Boolean run() { |
| return (!rootFile.exists() || !rootFile.isDirectory() || !rootFile.canRead()); |
| } |
| }); |
| if (notDirectory) { |
| error("RootNotDirectory", rootFile, repository); |
| throw new IOException("The root is not a directory: " + repository); |
| } |
| |
| } |
| |
| |
| /** |
| * |
| */ |
| void init() { |
| if(! initialized) { |
| try { |
| initializeRepository(); |
| } catch(Exception e) { |
| //ignore |
| } |
| } |
| |
| File domainFile = new File(rootFile, "sca-domain.xml"); |
| if (!domainFile.isFile()) { |
| return; |
| } |
| FileInputStream is; |
| try { |
| is = new FileInputStream(domainFile); |
| } catch (FileNotFoundException e) { |
| warning("DomainFileNotFound", domainFile, domainFile.getAbsolutePath()); |
| return; |
| } |
| try { |
| XMLStreamReader reader = factory.createXMLStreamReader(new InputStreamReader(is, "UTF-8")); |
| while (reader.hasNext()) { |
| switch (reader.getEventType()) { |
| case XMLStreamConstants.START_ELEMENT: |
| String name = reader.getName().getLocalPart(); |
| if ("domain".equals(name)) { |
| String uri = reader.getAttributeValue(null, "uri"); |
| if (uri != null) { |
| domain = URI.create(uri); |
| } |
| } |
| if ("contribution".equals(name)) { |
| String uri = reader.getAttributeValue(null, "uri"); |
| String location = reader.getAttributeValue(null, "location"); |
| contributionLocations.put(uri, location); |
| } |
| break; |
| default: |
| break; |
| } |
| reader.next(); |
| } |
| } catch (Exception e) { |
| // Ignore |
| } finally { |
| IOHelper.closeQuietly(is); |
| } |
| } |
| |
| /** |
| * Utility functions |
| */ |
| |
| /** |
| * Write a specific source InputStream to a file on disk |
| * |
| * @param source contents of the file to be written to disk |
| * @param target file to be written |
| * @throws IOException |
| */ |
| private static void copy(InputStream source, File target) throws IOException { |
| BufferedOutputStream out = null; |
| BufferedInputStream in = null; |
| |
| try { |
| out = new BufferedOutputStream(new FileOutputStream(target)); |
| in = new BufferedInputStream(source); |
| IOHelper.copy(in, out); |
| } finally { |
| IOHelper.closeQuietly(out); |
| IOHelper.closeQuietly(in); |
| } |
| } |
| |
| } |