blob: e053daaabae9b9084505cc438c8b5868fde62977 [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.brooklyn.core.typereg;
import java.util.Map;
import javax.annotation.Nullable;
import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
import org.apache.brooklyn.api.mgmt.rebind.RebindSupport;
import org.apache.brooklyn.api.typereg.ManagedBundle;
import org.apache.brooklyn.api.typereg.OsgiBundleWithUrl;
import org.apache.brooklyn.config.ConfigKey;
import org.apache.brooklyn.core.mgmt.rebind.BasicManagedBundleRebindSupport;
import org.apache.brooklyn.core.objs.AbstractBrooklynObject;
import org.apache.brooklyn.core.objs.BrooklynObjectInternal;
import org.apache.brooklyn.util.http.auth.Credentials;
import org.apache.brooklyn.util.osgi.VersionedName;
import org.apache.brooklyn.util.text.BrooklynVersionSyntax;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
public class BasicManagedBundle extends AbstractBrooklynObject implements ManagedBundle, BrooklynObjectInternal {
private String symbolicName;
private String version;
private String checksum;
private Boolean deleteable;
private String osgiUniqueUrl;
private String format;
private String url;
private Credentials credentials;
/** pretty much redundant as it is put in the delta if changed, and included even if not needed when full checkpoint requested */
private transient boolean persistenceNeeded = false;
/** Creates an empty one, with an ID, expecting other fields will be populated. */
public BasicManagedBundle() {}
/** @deprecated since 1.1 use larger constructor */ @Deprecated
public BasicManagedBundle(String name, String version, String url, @Nullable String checksum) {
this(name, version, url, null, null, checksum);
}
/** @deprecated since 1.1 use larger constructor */ @Deprecated
public BasicManagedBundle(String name, String version, String url, Credentials credentials, @Nullable String checksum) {
this(name, version, url, null, credentials, checksum);
}
/** @deprecated since 1.1 use larger constructor */ @Deprecated
public BasicManagedBundle(String name, String version, String url, String format, Credentials credentials, @Nullable String checksum) {
init(name, version, url, format, credentials, checksum, null);
}
public BasicManagedBundle(String name, String version, String url, String format, Credentials credentials, @Nullable String checksum, @Nullable Boolean deleteable) {
init(name, version, url, format, credentials, checksum, deleteable);
}
private void init(String name, String version, String url, String format, Credentials credentials, @Nullable String checksum, @Nullable Boolean deleteable) {
if (name == null && version == null) {
Preconditions.checkNotNull(url, "Either a URL or both name and version are required");
} else {
Preconditions.checkNotNull(name, "Either a URL or both name and version are required");
Preconditions.checkNotNull(version, "Either a URL or both name and version are required");
}
this.symbolicName = name;
this.version = version;
this.url = url;
this.format = format;
this.credentials = credentials;
this.checksum = checksum;
this.deleteable = deleteable;
}
private BasicManagedBundle(String id, String name, String version, String url, String format, Credentials credentials, @Nullable String checksum, @Nullable Boolean deleteable) {
super(id);
init(name, version, url, format, credentials, checksum, deleteable);
}
/** used when updating a persisted bundle, we want to use the coords (ID and OSGI unique URL) of the second with the checksum of the former;
* the other fields should be the same between the two but if in doubt use the first argument
*/
public static BasicManagedBundle copyFirstWithCoordsOfSecond(ManagedBundle update, ManagedBundle oldOneForCoordinates) {
BasicManagedBundle result = new BasicManagedBundle(oldOneForCoordinates.getId(), update.getSymbolicName(), update.getSuppliedVersionString(), update.getUrl(), update.getFormat(), update.getUrlCredential(), update.getChecksum(), update.getDeleteable());
result.tags().addTags(update.tags().getTags());
// we have secondary logic which should accept a change in the OSGi unique URL,
// but more efficient if we use the original URL
result.osgiUniqueUrl = oldOneForCoordinates.getOsgiUniqueUrl();
return result;
}
@Override
public boolean isNameResolved() {
return symbolicName != null && version != null;
}
@Override
public Boolean getDeleteable() {
return deleteable;
}
@Override
public String getSymbolicName() {
return symbolicName;
}
public void setSymbolicName(String symbolicName) {
this.symbolicName = symbolicName;
}
@Override
public String getSuppliedVersionString() {
return version;
}
@Override
public String getOsgiVersionString() {
return version==null ? null : BrooklynVersionSyntax.toValidOsgiVersion(version);
}
public void setVersion(String version) {
this.version = version;
}
@Override
public VersionedName getVersionedName() {
if (symbolicName==null) return null;
return new VersionedName(symbolicName, version);
}
public void setFormat(String format) {
this.format = format;
}
@Override
public String getFormat() {
return format;
}
@Override
public String getUrl() {
return url;
}
@Override
public Credentials getUrlCredential() {
return credentials;
}
public void setUrl(String url) {
this.url = url;
}
/**
* Gets the (internal) value to be used as the location in bundleContext.install(location) / bundleContext.getBundle(location).
* It thus allows us to tell if a cached OSGi bundle should be considered as replacement
* for the one we are about to install (e.g. one we get from persisted state),
* or have retrieved from the initial catalog with a unique URL for a bundle symbolic-name + version.
* This is typically not a real URL, as the same bundle may come from various locations.
* Typically we use a checksum or internal ID.
* In the case of same-version (eg snapshot or forced) bundle updates we remember and re-use the _original_ checksum.
* However code should be tolerant if this is not unique and use other more-expensive mechanisms for secondary de-duplication.
*
* Care should be taken to set the checksum <em>before</em> using the OSGi unique url.
*/
@Override
public String getOsgiUniqueUrl() {
if (osgiUniqueUrl==null) {
osgiUniqueUrl = "brooklyn:" + (checksum != null ? checksum : getId());
}
return osgiUniqueUrl;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("symbolicName", symbolicName)
.add("version", version)
.add("format", format)
.add("url", url)
.add("osgiUniqueUrl", osgiUniqueUrl)
.toString();
}
@Override
public int hashCode() {
// checksum deliberately omitted here to match with OsgiBundleWithUrl
return Objects.hashCode(symbolicName, getOsgiVersionString(), url);
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
OsgiBundleWithUrl other = (OsgiBundleWithUrl) obj;
if (!Objects.equal(symbolicName, other.getSymbolicName())) return false;
if (!Objects.equal(getOsgiVersionString(), other.getOsgiVersionString())) return false;
if (!Objects.equal(url, other.getUrl())) return false;
if (other instanceof ManagedBundle) {
// checksum compared if available, but not required;
// this makes equality with other OsgiBundleWithUrl items symmetric,
// but for two MB's we look additionally at checksum
if (!Objects.equal(checksum, ((ManagedBundle)other).getChecksum())) return false;
// only equal if have the same ManagedBundle uid; important for persistence.changeListener().unmanage()
if (!Objects.equal(getId(), ((ManagedBundle)other).getId())) return false;
}
return true;
}
// ---
@Override
public String getDisplayName() {
return null;
}
@Override
public <T> T getConfig(ConfigKey<T> key) {
throw new UnsupportedOperationException();
}
@Override
public RebindSupport<?> getRebindSupport() {
return new BasicManagedBundleRebindSupport(this);
}
@Override
public ConfigurationSupportInternal config() {
throw new UnsupportedOperationException();
}
@Override
public SubscriptionSupportInternal subscriptions() {
throw new UnsupportedOperationException();
}
@Override
public void setDisplayName(String newName) {
throw new UnsupportedOperationException();
}
@Override
public String getChecksum() {
return checksum;
}
public void setChecksum(String md5Checksum) {
this.checksum = md5Checksum;
}
@Override
protected BrooklynObjectInternal configure(Map<?, ?> flags) {
throw new UnsupportedOperationException();
}
public static ManagedBundle of(CatalogBundle bundle) {
String checksum = (bundle instanceof ManagedBundle) ? ((ManagedBundle)bundle).getChecksum() : null;
return new BasicManagedBundle(
bundle.getSymbolicName(),
bundle.getSuppliedVersionString(),
bundle.getUrl(),
null,
bundle.getUrlCredential(),
checksum,
bundle instanceof ManagedBundle ? ((ManagedBundle)bundle).getDeleteable() : null);
}
public void setPersistenceNeeded(boolean val) {
persistenceNeeded = val;
}
public boolean getPersistenceNeeded() {
return persistenceNeeded;
}
}