/*
* #%L
* Gravia :: Resource
* %%
* Copyright (C) 2010 - 2014 JBoss by Red Hat
* %%
* Licensed 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.
* #L%
*/
package org.jboss.gravia.resource.spi;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.OpenDataException;
import org.jboss.gravia.resource.Attachable;
import org.jboss.gravia.resource.AttachmentKey;
import org.jboss.gravia.resource.Capability;
import org.jboss.gravia.resource.CompositeDataResourceType;
import org.jboss.gravia.resource.ContentCapability;
import org.jboss.gravia.resource.ContentNamespace;
import org.jboss.gravia.resource.IdentityNamespace;
import org.jboss.gravia.resource.Requirement;
import org.jboss.gravia.resource.Resource;
import org.jboss.gravia.resource.ResourceContent;
import org.jboss.gravia.resource.ResourceIdentity;
import org.jboss.gravia.resource.Version;
/**
* An abstract implementation of a {@link Resource}
*
* @author thomas.diesler@jboss.com
* @since 02-Jul-2010
*/
public abstract class AbstractResource implements Resource {
private final List<AbstractCapability> capabilities = new ArrayList<AbstractCapability>();
private final List<AbstractRequirement> requirements = new ArrayList<AbstractRequirement>();
private final AtomicBoolean mutable = new AtomicBoolean(true);
private Capability identityCapability;
private ResourceIdentity identity;
private transient Attachable attachments;
void addCapability(AbstractCapability cap) {
synchronized (capabilities) {
assertMutable();
capabilities.add(cap);
}
}
void addRequirement(AbstractRequirement req) {
synchronized (requirements) {
assertMutable();
requirements.add(req);
}
}
@Override
@SuppressWarnings("unchecked")
public <T> T adapt(Class<T> type) {
T result = null;
if (type.isAssignableFrom(getClass())) {
result = (T) this;
} else if (type.isAssignableFrom(CompositeData.class)) {
result = (T) getCompositeData();
} else if (type.isAssignableFrom(ResourceContent.class)) {
result = (T) getResourceContent();
}
return result;
}
private CompositeData getCompositeData() {
CompositeData compositeData;
try {
compositeData = new CompositeDataResourceType().getCompositeData(this);
} catch (OpenDataException ex) {
throw new IllegalStateException("Cannot construct composite data for: " + this, ex);
}
return compositeData;
}
/**
* Get the default content (i.e. from the first content capability)
*/
private ResourceContent getResourceContent() {
List<Capability> ccaps = getCapabilities(ContentNamespace.CONTENT_NAMESPACE);
if (ccaps.isEmpty())
return null;
ContentCapability ccap = ccaps.get(0).adapt(ContentCapability.class);
InputStream contentStream = ccap.getContentStream();
if (contentStream == null) {
URL contentURL = ccap.getContentURL();
if (contentURL != null) {
try {
contentStream = contentURL.openStream();
} catch (IOException ex) {
throw new IllegalStateException("Cannot access content URL: " + contentURL, ex);
}
}
}
ResourceContent resourceContent = null;
if (contentStream != null) {
final InputStream inputStream = contentStream;
resourceContent = new ResourceContent() {
@Override
public InputStream getContent() {
return inputStream;
}
};
}
return resourceContent;
}
@Override
public Capability getIdentityCapability() {
if (identityCapability == null) {
List<Capability> icaps = getCapabilities(IdentityNamespace.IDENTITY_NAMESPACE);
if (icaps.size() > 1)
throw new IllegalStateException("Multiple identity capabilities");
if (icaps.size() < 1)
throw new IllegalStateException("No identity capability");
Capability icap = icaps.get(0);
Object version = icap.getAttribute(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE);
if (!(version instanceof Version)) {
version = version == null ? Version.emptyVersion : Version.parseVersion(version.toString());
icap.getAttributes().put(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, version);
}
identityCapability = icap;
}
return identityCapability;
}
@Override
public ResourceIdentity getIdentity() {
if (identity == null) {
Capability icap = getIdentityCapability();
String symbolicName = (String) icap.getAttribute(IdentityNamespace.IDENTITY_NAMESPACE);
Version version = (Version) icap.getAttribute(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE);
identity = ResourceIdentity.create(symbolicName, version);
}
return identity;
}
protected void setMutable(boolean flag) {
mutable.set(flag);
}
protected boolean isMutable() {
return mutable.get();
}
void assertMutable() {
if (!isMutable())
throw new IllegalStateException("Invalid access to immutable resource");
}
void assertImmutable() {
if (isMutable())
throw new IllegalStateException("Invalid access to mutable resource");
}
@Override
public List<Capability> getCapabilities(String namespace) {
List<Capability> result = new ArrayList<Capability>();
synchronized (capabilities) {
for (Capability cap : capabilities) {
if (namespace == null || namespace.equals(cap.getNamespace())) {
result.add(cap);
}
}
}
return Collections.unmodifiableList(result);
}
@Override
public List<Requirement> getRequirements(String namespace) {
List<Requirement> result = new ArrayList<Requirement>();
synchronized (requirements) {
for (Requirement req : requirements) {
if (namespace == null || namespace.equals(req.getNamespace())) {
result.add(req);
}
}
}
return Collections.unmodifiableList(result);
}
@Override
public <T> T putAttachment(AttachmentKey<T> key, T value) {
return getAttachmentsInternal().putAttachment(key, value);
}
@Override
public <T> T getAttachment(AttachmentKey<T> key) {
return getAttachmentsInternal().getAttachment(key);
}
@Override
public <T> boolean hasAttachment(AttachmentKey<T> key) {
return getAttachmentsInternal().hasAttachment(key);
}
@Override
public <T> T removeAttachment(AttachmentKey<T> key) {
return getAttachmentsInternal().getAttachment(key);
}
private Attachable getAttachmentsInternal() {
if (attachments == null) {
attachments = new AttachableSupport();
}
return attachments;
}
boolean isValid() {
try {
validate();
return true;
} catch (RuntimeException rte) {
return false;
}
}
void validate() {
// Make sure we have an identity
getIdentity();
// Validate the capabilities
for (Capability cap : getCapabilities(null)) {
((AbstractCapability) cap).validate();
}
// Validate the requirements
for (Requirement req : getRequirements(null)) {
((AbstractRequirement) req).validate();
}
}
protected String getSimpleTypeName() {
return getClass().getSimpleName();
}
@Override
public String toString() {
ResourceIdentity id = identity;
String idstr = (id != null ? id.getSymbolicName() + ":" + id.getVersion() : "anonymous");
return getSimpleTypeName() + "[" + idstr + "]";
}
}