blob: d00e33b1b3e3500cd2808521c5b2e91f5f9303b6 [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.deployment.service.impl;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import org.apache.ace.connectionfactory.ConnectionFactory;
import org.apache.ace.deployment.Deployment;
import org.apache.ace.deployment.service.DeploymentService;
import org.apache.ace.discovery.Discovery;
import org.apache.ace.identification.Identification;
import org.osgi.framework.Version;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventAdmin;
import org.osgi.service.log.LogService;
/**
* Provides an implementation for {@link DeploymentService}.
*/
public class DeploymentServiceImpl implements DeploymentService {
private final String TOPIC_DEPLOYMENTPACKAGE_INSTALL = "org/apache/ace/deployment/INSTALL";
// injected by dependencymanager
protected volatile Deployment m_deployer;
protected volatile Identification m_identification;
protected volatile Discovery m_discovery;
protected volatile LogService m_log;
protected volatile EventAdmin m_eventAdmin;
protected volatile ConnectionFactory m_connectionFactory;
/**
* @see org.apache.ace.deployment.service.DeploymentService#getHighestLocalVersion()
*/
public Version getHighestLocalVersion() {
Object[] installedPackages = m_deployer.list();
List<Version> versions = new ArrayList<>();
for (int i = 0; i < installedPackages.length; i++) {
if (m_deployer.getName(installedPackages[i]).equals(m_identification.getID())) {
versions.add(m_deployer.getVersion(installedPackages[i]));
}
}
return getHighestVersion(versions);
}
/**
* @see org.apache.ace.deployment.service.DeploymentService#getHighestRemoteVersion()
*/
public Version getHighestRemoteVersion() throws IOException {
SortedSet<Version> versions = getRemoteVersions(getURL());
return ((versions == null) || versions.isEmpty()) ? null : versions.last();
}
/**
* @see org.apache.ace.deployment.service.DeploymentService#getRemoteVersions()
*/
public SortedSet<Version> getRemoteVersions() throws IOException {
return getRemoteVersions(getURL());
}
/**
* @see org.apache.ace.deployment.service.DeploymentService#installVersion(org.osgi.framework.Version, org.osgi.framework.Version)
*/
public void installVersion(Version highestRemoteVersion, Version highestLocalVersion) throws IOException, Exception {
InputStream inputStream = null;
m_log.log(LogService.LOG_INFO, "Installing version: " + highestRemoteVersion);
try {
String version = highestRemoteVersion.toString();
URL baseURL = getURL();
boolean isFileBasedProtocol = "file".equals(baseURL.getProtocol());
if (highestLocalVersion != null && !isFileBasedProtocol) {
version += "?current=" + highestLocalVersion.toString();
}
URL dataURL = new URL(baseURL, version);
if (isFileBasedProtocol) {
File file = urlToFile(dataURL);
inputStream = new FileInputStream(file);
}
else {
inputStream = getContents(dataURL);
}
// Post event for auditlog
m_eventAdmin.postEvent(createEvent(version, dataURL));
m_deployer.install(inputStream);
}
finally {
if (inputStream != null) {
try {
inputStream.close();
}
catch (Exception ex) {
// Not much we can do.
}
}
}
}
/**
* @see org.apache.ace.deployment.service.DeploymentService#update(org.osgi.framework.Version)
*/
public void update(Version toVersion) throws Exception {
installVersion(toVersion, getHighestLocalVersion());
}
/**
* @param url
* @return
* @throws IOException
*/
final SortedSet<Version> getRemoteVersions(URL url) throws IOException {
if (url == null) {
return null;
}
if ("file".equals(url.getProtocol())) {
return getVersionsFromDirectory(url);
}
else {
return getVersionsFromServer(url);
}
}
/**
* @param version
* @param dataURL
* @return
*/
private Event createEvent(String version, URL dataURL) {
Dictionary<String, Object> properties = new Hashtable<>();
properties.put("deploymentpackage.url", dataURL.toString());
properties.put("deploymentpackage.version", version);
Event event = new Event(TOPIC_DEPLOYMENTPACKAGE_INSTALL, properties);
return event;
}
/**
* @param versions
* @return
*/
private Version getHighestVersion(List<Version> versions) {
Version highestVersion = null;
for (Version version : versions) {
if (highestVersion == null) {
highestVersion = version;
}
else if (version.compareTo(highestVersion) > 0) {
highestVersion = version;
}
}
return highestVersion;
}
/**
* @return
*/
private URL getURL() {
URL host = m_discovery.discover();
if (host == null) {
return null;
}
try {
return new URL(host, "deployment/" + m_identification.getID() + "/versions/");
}
catch (MalformedURLException e) {
m_log.log(LogService.LOG_WARNING, "Malformed URL", e);
return null;
}
}
/**
* @param url
* @return
*/
private SortedSet<Version> getVersionsFromDirectory(URL url) {
File file = urlToFile(url);
if (!file.isDirectory()) {
return null;
}
final File[] files = file.listFiles();
SortedSet<Version> versions = new TreeSet<>();
for (File f : files) {
try {
Version version = Version.parseVersion(f.getName());
if (version != Version.emptyVersion) {
versions.add(version);
}
}
catch (IllegalArgumentException e) {
// if the file is not a valid version, we skip it
}
}
return versions;
}
/**
* @param url
* @return
*/
private SortedSet<Version> getVersionsFromServer(URL url) {
BufferedReader bufReader = null;
try {
bufReader = new BufferedReader(new InputStreamReader(getContents(url)));
SortedSet<Version> versions = new TreeSet<>();
String versionString;
while ((versionString = bufReader.readLine()) != null) {
try {
Version version = Version.parseVersion(versionString);
if (version != Version.emptyVersion) {
versions.add(version);
}
}
catch (IllegalArgumentException iae) {
m_log.log(LogService.LOG_WARNING, "Received malformed version, ignoring: " + versionString);
}
}
return versions;
}
catch (IOException ioe) {
m_log.log(LogService.LOG_DEBUG, "I/O error accessing server!", ioe);
return null;
}
finally {
if (bufReader != null) {
try {
bufReader.close();
}
catch (Exception ex) {
// not much we can do
}
}
}
}
/**
* @param url
* @return
*/
private File urlToFile(URL url) {
File file;
// See: http://weblogs.java.net/blog/kohsuke/archive/2007/04/how_to_convert.html
// makes a best effort to convert a file URL to a File
try {
file = new File(url.toURI());
}
catch (URISyntaxException e) {
file = new File(url.getPath());
}
return file;
}
/**
* @param url the remote URL to connect to, cannot be <code>null</code>.
* @return an {@link InputStream} to the remote URL, never <code>null</code>.
* @throws IOException in case of I/O problems opening the remote connection.
*/
private InputStream getContents(URL url) throws IOException {
URLConnection conn = m_connectionFactory.createConnection(url);
return conn.getInputStream();
}
}