blob: 08134a2d192befd9f8053fd1bebc3679ad309dfb [file] [log] [blame]
/*
* 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);
}
}
}