blob: a198134c97901d03ea9a9ecb64ef1a4431901cb7 [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.agent.impl;
import static org.apache.ace.agent.impl.ConnectionUtil.closeSilently;
import static org.apache.ace.agent.impl.ConnectionUtil.copy;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.SortedSet;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import org.apache.ace.agent.AgentUpdateHandler;
import org.apache.ace.agent.DownloadHandle;
import org.apache.ace.agent.RetryAfterException;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.framework.Version;
import org.osgi.util.tracker.ServiceTracker;
/**
* Default implementation of {@link AgentUpdateHandler}.
*/
public class AgentUpdateHandlerImpl extends UpdateHandlerBase implements AgentUpdateHandler {
private static final int TIMEOUT = 15000;
private static final String UPDATER_VERSION = "1.0.0";
private static final String UPDATER_SYMBOLICNAME = "org.apache.ace.agent.updater";
private BundleContext m_bundleContext;
public AgentUpdateHandlerImpl(BundleContext bundleContext) {
super("agentupdate");
m_bundleContext = bundleContext;
}
@Override
public SortedSet<Version> getAvailableVersions() throws RetryAfterException, IOException {
return getAvailableVersions(getEndpoint(getServerURL(), getIdentification(), null));
}
@Override
public DownloadHandle getDownloadHandle(Version version, boolean fixPackage) throws RetryAfterException {
return getDownloadHandle(getEndpoint(getServerURL(), getIdentification(), version));
}
@Override
public InputStream getInputStream(Version version, boolean fixPackage) throws RetryAfterException, IOException {
return getInputStream(getEndpoint(getServerURL(), getIdentification(), version));
}
@Override
public Version getInstalledVersion() {
return m_bundleContext.getBundle().getVersion();
}
@Override
public String getName() {
return "agent";
}
@Override
public long getSize(Version version, boolean fixPackage) throws RetryAfterException, IOException {
return getPackageSize(getEndpoint(getServerURL(), getIdentification(), version));
}
@Override
public void install(InputStream stream) throws IOException {
try {
InputStream currentBundleVersion = getInputStream(m_bundleContext.getBundle().getVersion(), false /* fixPackage */);
Bundle bundle = m_bundleContext.installBundle("agent-updater", generateBundle());
bundle.start();
ServiceTracker<Object, Object> st = new ServiceTracker<>(m_bundleContext, m_bundleContext.createFilter("(" + Constants.OBJECTCLASS + "=org.apache.ace.agent.updater.Activator)"), null);
st.open(true);
Object service = st.waitForService(TIMEOUT);
if (service != null) {
Method method = service.getClass().getMethod("update", Bundle.class, InputStream.class, InputStream.class);
try {
method.invoke(service, m_bundleContext.getBundle(), currentBundleVersion, stream);
}
catch (InvocationTargetException e) {
bundle.uninstall();
}
finally {
st.close();
}
}
else {
throw new IOException("No update service found after launching temporary bundle.");
}
}
catch (Exception e) {
throw new IOException("Could not update management agent.", e);
}
}
@Override
public void onStart() throws Exception {
// at this point we know the agent has started, so any updater bundle that
// might still be running can be uninstalled
uninstallUpdaterBundle();
}
private Manifest createBundleManifest() {
Manifest manifest = new Manifest();
Attributes main = manifest.getMainAttributes();
main.put(Attributes.Name.MANIFEST_VERSION, "1.0");
main.put(new Attributes.Name("Bundle-ManifestVersion"), "2");
main.put(new Attributes.Name("Bundle-SymbolicName"), UPDATER_SYMBOLICNAME);
main.put(new Attributes.Name("Bundle-Version"), UPDATER_VERSION);
main.put(new Attributes.Name("Import-Package"), "org.osgi.framework");
main.put(new Attributes.Name("Bundle-Activator"), "org.apache.ace.agent.updater.Activator");
return manifest;
}
/** Generates an input stream that contains a complete bundle containing our update code for the agent. */
private InputStream generateBundle() throws IOException {
final String activatorClass = "org/apache/ace/agent/updater/Activator.class";
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream is = null;
JarOutputStream os = null;
try {
is = getClass().getResourceAsStream("/".concat(activatorClass));
os = new JarOutputStream(baos, createBundleManifest());
os.putNextEntry(new JarEntry(activatorClass));
try {
copy(is, os);
}
finally {
os.closeEntry();
}
}
finally {
closeSilently(is);
closeSilently(os);
}
return new ByteArrayInputStream(baos.toByteArray());
}
private URL getEndpoint(URL serverURL, String identification, Version version) {
try {
return new URL(serverURL, "agent/" + identification + "/" + m_bundleContext.getBundle().getSymbolicName() + "/versions/" + (version == null ? "" : version.toString()));
}
catch (MalformedURLException e) {
throw new IllegalStateException(e);
}
}
private void uninstallUpdaterBundle() throws BundleException {
for (Bundle b : m_bundleContext.getBundles()) {
if (UPDATER_SYMBOLICNAME.equals(b.getSymbolicName())) {
try {
b.uninstall();
}
catch (BundleException e) {
logError("Failed to uninstall updater bundle. Will try to stop it instead.", e);
b.stop();
throw e;
}
}
}
}
}