blob: 85a6623631ddd85603c55698ba56049f0741346a [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.ace.client.repository.helper.base;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import org.apache.ace.client.repository.helper.ArtifactPreprocessor;
import org.apache.ace.connectionfactory.ConnectionFactory;
import org.osgi.annotation.versioning.ConsumerType;
/**
* This class can be used as a base class for artifact preprocessors. It comes with its own upload() method, which will
* be used by all artifact preprocessors anyway.
*/
@ConsumerType
public abstract class ArtifactPreprocessorBase implements ArtifactPreprocessor {
/** 64k buffers should be enough for everybody... */
protected static final int BUFFER_SIZE = 64 * 1024;
protected final ConnectionFactory m_connectionFactory;
/**
* Creates a new {@link ArtifactPreprocessorBase} instance.
*
* @param connectionFactory
* the connection factory to use, cannot be <code>null</code>.
*/
protected ArtifactPreprocessorBase(ConnectionFactory connectionFactory) {
m_connectionFactory = connectionFactory;
}
/**
* Creates a new URL for given (file) name and OBR base URL.
*
* @param name
* the name of the file to create the URL for;
* @param obrBase
* the OBR base URL to use.
* @return a new URL for the file, never <code>null</code>.
* @throws MalformedURLException
* in case of invalid characters in the given name.
*/
protected URL determineNewUrl(String name, URL obrBase) throws MalformedURLException {
return new URL(obrBase, name);
}
/**
* Silently closes the given {@link Closeable} instance.
*
* @param closable
* the closeable to close, may be <code>null</code>.
*/
protected final void silentlyClose(Closeable closable) {
if (closable != null) {
try {
closable.close();
}
catch (IOException e) {
// Ignore; nothing we can/will do about here...
}
}
}
/**
* Uploads an artifact synchronously to an OBR.
*
* @param input
* A inputstream from which the artifact can be read.
* @param name
* The name of the artifact. If the name is not unique, an IOException will be thrown.
* @param obrBase
* The base URL of the obr to which this artifact should be written.
* @return A URL to the uploaded artifact; this is identical to calling <code>determineNewUrl(name, obrBase)</code>
* @throws IOException
* If there was an error reading from <code>input</code>, or if there was a problem communicating with
* the OBR.
*/
String upload(InputStream input, String name, String mimeType, URL obrBase) throws IOException {
if (obrBase == null) {
throw new IOException("There is no storage available for this artifact.");
}
if (name == null) {
throw new IllegalArgumentException("Name cannot be null.");
}
if (input == null) {
throw new IllegalArgumentException("Input stream cannot be null.");
}
OutputStream output = null;
String location = null;
try {
URL url = new URL(obrBase, "?filename=" + name + "&replace=true");
URLConnection connection = m_connectionFactory.createConnection(url);
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setUseCaches(false);
connection.setRequestProperty("Content-Type", mimeType);
if (connection instanceof HttpURLConnection) {
// ACE-294: enable streaming mode causing only small amounts of memory to be
// used for this commit. Otherwise, the entire input stream is cached into
// memory prior to sending it to the server...
((HttpURLConnection) connection).setChunkedStreamingMode(8192);
}
output = connection.getOutputStream();
byte[] buffer = new byte[BUFFER_SIZE];
for (int count = input.read(buffer); count != -1; count = input.read(buffer)) {
output.write(buffer, 0, count);
}
output.close();
if (connection instanceof HttpURLConnection) {
int responseCode = ((HttpURLConnection) connection).getResponseCode();
switch (responseCode) {
case HttpURLConnection.HTTP_CREATED:
location = connection.getHeaderField("Location");
break;
case HttpURLConnection.HTTP_CONFLICT:
throw new IOException("Artifact already exists in storage: " + name);
case HttpURLConnection.HTTP_INTERNAL_ERROR:
throw new IOException("The storage server returned an internal server error while trying to upload " + name);
default:
throw new IOException("The storage server returned code " + responseCode + " writing to " + url.toString());
}
}
}
finally {
silentlyClose(output);
}
return location;
}
}