diff --git a/blobstore/src/main/java/org/jclouds/blobstore/domain/Blob.java b/blobstore/src/main/java/org/jclouds/blobstore/domain/Blob.java index b006f179f2..bccc10a3e5 100644 --- a/blobstore/src/main/java/org/jclouds/blobstore/domain/Blob.java +++ b/blobstore/src/main/java/org/jclouds/blobstore/domain/Blob.java @@ -22,6 +22,8 @@ import org.jclouds.javax.annotation.Nullable; import com.google.common.collect.Multimap; +import org.jclouds.management.annotations.ManagedAttribute; +import org.jclouds.management.annotations.ManagedType; /** * Value type for an HTTP Blob service. Blobs are stored in containers and consist of a @@ -30,6 +32,7 @@ * * @author Adrian Cole */ +@ManagedType public interface Blob extends PayloadEnclosing, Comparable { /** * Allows you to construct blobs without knowing the implementation type @@ -45,6 +48,7 @@ public interface Factory { /** * @return System and User metadata relevant to this object. */ + @ManagedAttribute(description = "The metadata related to the blob") MutableBlobMetadata getMetadata(); /** diff --git a/blobstore/src/main/java/org/jclouds/blobstore/domain/BlobMetadata.java b/blobstore/src/main/java/org/jclouds/blobstore/domain/BlobMetadata.java index 176e4c335d..14ef50e579 100644 --- a/blobstore/src/main/java/org/jclouds/blobstore/domain/BlobMetadata.java +++ b/blobstore/src/main/java/org/jclouds/blobstore/domain/BlobMetadata.java @@ -25,12 +25,15 @@ import org.jclouds.javax.annotation.Nullable; import com.google.inject.ImplementedBy; +import org.jclouds.management.annotations.ManagedAttribute; +import org.jclouds.management.annotations.ManagedType; /** * System and user Metadata for the {@link Blob}. * * @author Adrian Cole */ +@ManagedType @ImplementedBy(BlobMetadataImpl.class) public interface BlobMetadata extends StorageMetadata { /** @@ -46,7 +49,9 @@ public interface BlobMetadata extends StorageMetadata { * @return the container holding this blob */ @Nullable + @ManagedAttribute(description = "The container holding the blob") String getContainer(); + @ManagedAttribute(description = "The container metadata") ContentMetadata getContentMetadata(); } diff --git a/blobstore/src/main/java/org/jclouds/blobstore/domain/MutableBlobMetadata.java b/blobstore/src/main/java/org/jclouds/blobstore/domain/MutableBlobMetadata.java index 3f631e58d8..2836be1fa4 100644 --- a/blobstore/src/main/java/org/jclouds/blobstore/domain/MutableBlobMetadata.java +++ b/blobstore/src/main/java/org/jclouds/blobstore/domain/MutableBlobMetadata.java @@ -25,18 +25,22 @@ import org.jclouds.javax.annotation.Nullable; import com.google.inject.ImplementedBy; +import org.jclouds.management.annotations.ManagedAttribute; +import org.jclouds.management.annotations.ManagedType; /** * System and user Metadata for the {@link Blob}. * * @author Adrian Cole */ +@ManagedType @ImplementedBy(MutableBlobMetadataImpl.class) public interface MutableBlobMetadata extends BlobMetadata, MutableStorageMetadata { /** * {@inheritDoc} */ @Override + @ManagedAttribute(description = "The content metadata") MutableContentMetadata getContentMetadata(); /** diff --git a/blobstore/src/main/java/org/jclouds/blobstore/domain/StorageMetadata.java b/blobstore/src/main/java/org/jclouds/blobstore/domain/StorageMetadata.java index a658b3d497..64be4ba4d6 100644 --- a/blobstore/src/main/java/org/jclouds/blobstore/domain/StorageMetadata.java +++ b/blobstore/src/main/java/org/jclouds/blobstore/domain/StorageMetadata.java @@ -26,12 +26,15 @@ import org.jclouds.domain.ResourceMetadata; import com.google.inject.ImplementedBy; +import org.jclouds.management.annotations.ManagedAttribute; +import org.jclouds.management.annotations.ManagedType; /** * Identifies containers, files, etc. * * @author Adrian Cole */ +@ManagedType @ImplementedBy(StorageMetadataImpl.class) public interface StorageMetadata extends ResourceMetadata { @@ -49,6 +52,7 @@ public interface StorageMetadata extends ResourceMetadata { * @see org.jclouds.blobstore.attr.ContainerCapability#CONTAINER_METADATA */ @Override + @ManagedAttribute(description = "The provider id") String getProviderId(); /** @@ -57,6 +61,7 @@ public interface StorageMetadata extends ResourceMetadata { * */ @Override + @ManagedAttribute(description = "The name of the resource") String getName(); /** @@ -85,6 +90,7 @@ public interface StorageMetadata extends ResourceMetadata { /** * Creation date of the resource, possibly null. */ + @ManagedAttribute(description = "Creation Date") Date getCreationDate(); /** @@ -94,6 +100,7 @@ public interface StorageMetadata extends ResourceMetadata { * @see org.jclouds.blobstore.attr.ContainerCapability#BLOB_LAST_MODIFIED * @see org.jclouds.blobstore.attr.ContainerCapability#MILLISECOND_PRECISION */ + @ManagedAttribute(description = "Last modification date") Date getLastModified(); } diff --git a/blobstore/src/main/java/org/jclouds/blobstore/internal/BlobStoreContextImpl.java b/blobstore/src/main/java/org/jclouds/blobstore/internal/BlobStoreContextImpl.java index ba16fcad33..a974e4ccd8 100644 --- a/blobstore/src/main/java/org/jclouds/blobstore/internal/BlobStoreContextImpl.java +++ b/blobstore/src/main/java/org/jclouds/blobstore/internal/BlobStoreContextImpl.java @@ -20,10 +20,12 @@ import static com.google.common.base.Preconditions.checkNotNull; +import javax.annotation.PostConstruct; import javax.inject.Inject; import javax.inject.Singleton; import org.jclouds.Context; +import org.jclouds.management.annotations.Management; import org.jclouds.blobstore.AsyncBlobStore; import org.jclouds.blobstore.BlobMap; import org.jclouds.blobstore.BlobRequestSigner; @@ -31,10 +33,11 @@ import org.jclouds.blobstore.BlobStoreContext; import org.jclouds.blobstore.InputStreamMap; import org.jclouds.blobstore.attr.ConsistencyModel; +import org.jclouds.blobstore.management.BlobStoreManagement; import org.jclouds.blobstore.options.ListContainerOptions; import org.jclouds.internal.BaseView; import org.jclouds.location.Provider; -import org.jclouds.rest.RestContext; +import org.jclouds.management.ManagementContext; import org.jclouds.rest.Utils; import com.google.common.io.Closeables; @@ -52,6 +55,7 @@ public class BlobStoreContextImpl extends BaseView implements BlobStoreContext { private final ConsistencyModel consistencyModel; private final Utils utils; private final BlobRequestSigner blobRequestSigner; + private BlobStoreManagement blobStoreManagement; @Inject public BlobStoreContextImpl(@Provider Context backend, @Provider TypeToken backendType, @@ -66,6 +70,15 @@ public BlobStoreContextImpl(@Provider Context backend, @Provider TypeToken locationToTabular = ToTabularData.from(Location.class); + private final ToCompositeData blobToComposite = ToCompositeData.from(Blob.class); + private final ToTabularData storageMdToTabular = ToTabularData.from(StorageMetadata.class); + private final ToCompositeData blobMdToComposite = ToCompositeData.from(BlobMetadata.class); + + + public BlobStoreManagement(BlobStoreContext context) { + this.blobStore = context.getBlobStore(); + } + + + /** + * {@inheritDoc} + */ + @Override + public TabularData listAssignableLocations() throws OpenDataException { + return locationToTabular.apply(blobStore.listAssignableLocations()); + } + + /** + * {@inheritDoc} + */ + @Override + public TabularData list() throws OpenDataException { + return storageMdToTabular.apply(blobStore.list()); + } + + /** + * {@inheritDoc} + */ + @Override + public TabularData list(String container) throws OpenDataException { + return storageMdToTabular.apply(blobStore.list(container)); + } + + /** + * {@inheritDoc} + */ + @Override + public CompositeData blobMetadata(String container, String name) throws OpenDataException { + return blobMdToComposite.apply(blobStore.blobMetadata(container, name)); + } + + /** + * {@inheritDoc} + */ + @Override + public CompositeData getBlob(String container, String name) throws OpenDataException { + return blobToComposite.apply(blobStore.getBlob(container, name)); + } + + + /** + * {@inheritDoc} + */ + @Override + public boolean containerExists(String container) { + return blobStore.containerExists(container); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean createContainerInLocation(@Nullable String locationId, String container) { + Optional location = Iterables.tryFind(blobStore.listAssignableLocations(), new LocationPredicate(locationId)); + + if (location.isPresent()) { + return blobStore.createContainerInLocation(location.get(), container); + } else { + return false; + } + } + + + /** + * {@inheritDoc} + */ + @Override + public void clearContainer(String container) { + blobStore.clearContainer(container); + } + + + /** + * {@inheritDoc} + */ + @Override + public void deleteContainer(String container) { + blobStore.deleteContainer(container); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean directoryExists(String container, String directory) { + return blobStore.directoryExists(container, directory); + } + + /** + * {@inheritDoc} + */ + @Override + public void createDirectory(String container, String directory) { + blobStore.createDirectory(container, directory); + } + + /** + * {@inheritDoc} + */ + @Override + public void deleteDirectory(String containerName, String name) { + blobStore.deleteDirectory(containerName, name); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean blobExists(String container, String name) { + return blobStore.blobExists(container, name); + } + + /** + * {@inheritDoc} + */ + @Override + public void removeBlob(String container, String name) { + blobStore.removeBlob(container, name); + } + + /** + * {@inheritDoc} + */ + @Override + public long countBlobs(String container) { + return blobStore.countBlobs(container); + } + + /** + * Returns the type of the MBean. + * + * @return + */ + @Override + public String getType() { + return "blobstore"; + } + + /** + * Returns the name of the MBean. + * + * @return + */ + @Override + public String getName() { + return blobStore.getContext().unwrap().getName(); + } + + private static final class LocationPredicate implements Predicate { + private final String id; + + private LocationPredicate(String id) { + this.id = id; + } + + @Override + public boolean apply(@Nullable Location input) { + return input.getId().equals(id); + } + } +} diff --git a/blobstore/src/main/java/org/jclouds/blobstore/management/BlobStoreManagementMBean.java b/blobstore/src/main/java/org/jclouds/blobstore/management/BlobStoreManagementMBean.java new file mode 100644 index 0000000000..031e5e2623 --- /dev/null +++ b/blobstore/src/main/java/org/jclouds/blobstore/management/BlobStoreManagementMBean.java @@ -0,0 +1,181 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.blobstore.management; + +import org.jclouds.javax.annotation.Nullable; + +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.TabularData; + +public interface BlobStoreManagementMBean { + + /** + * The get locations command returns all the valid locations for containers. A location has a + * scope, which is typically region or zone. A region is a general area, like eu-west, where a + * zone is similar to a datacenter. If a location has a parent, that implies it is within that + * location. For example a location can be a rack, whose parent is likely to be a zone. + */ + TabularData listAssignableLocations() throws OpenDataException; + + /** + * Lists all root-level resources available to the identity. + */ + TabularData list() throws OpenDataException; + + /** + * Lists all resources in a container non-recursive. + * + * @param container + * what to list + * @return a list that may be incomplete, depending on whether PageSet#getNextMarker is set + */ + TabularData list(String container) throws OpenDataException; + + /** + * Retrieves the metadata of a {@code Blob} at location {@code container/name} + * + * @param container + * container where this exists. + * @param name + * fully qualified name relative to the container. + * @return null if name isn't present or the blob you intended to receive. + * @throws org.jclouds.blobstore.ContainerNotFoundException + * if the container doesn't exist + */ + CompositeData blobMetadata(String container, String name) throws OpenDataException; + + /** + * Retrieves a {@code Blob} representing the data at location {@code container/name} + * + * + * @param container + * container where this exists. + * @param name + * fully qualified name relative to the container. + * @return the blob you intended to receive or null, if it doesn't exist. + * @throws org.jclouds.blobstore.ContainerNotFoundException + * if the container doesn't exist + */ + CompositeData getBlob(String container, String name) throws OpenDataException; + + + /** + * determines if a service-level container exists + */ + boolean containerExists(String container); + + /** + * Creates a namespace for your blobs + *

+ * + * A container is a namespace for your objects. Depending on the service, the scope can be + * global, identity, or sub-identity scoped. For example, in Amazon S3, containers are called + * buckets, and they must be uniquely named such that no-one else in the world conflicts. In + * other blobstores, the naming convention of the container is less strict. All blobstores allow + * you to list your containers and also the contents within them. These contents can either be + * blobs, folders, or virtual paths. + * + * @param location + * some blobstores allow you to specify a location, such as US-EAST, for where this + * container will exist. null will choose a default location + * @param container + * namespace. Typically constrained to lowercase alpha-numeric and hyphens. + * @return true if the container was created, false if it already existed. + */ + boolean createContainerInLocation(@Nullable String location, String container); + + + /** + * This will delete the contents of a container at its root path without deleting the container + * + * @param container + * what to clear + */ + void clearContainer(String container); + + + /** + * This will delete everything inside a container recursively. + * + * @param container + * what to delete + */ + void deleteContainer(String container); + + /** + * Determines if a directory exists + * + * @param container + * container where the directory resides + * @param directory + * full path to the directory + */ + boolean directoryExists(String container, String directory); + + /** + * Creates a folder or a directory marker depending on the service + * + * @param container + * container to create the directory in + * @param directory + * full path to the directory + */ + void createDirectory(String container, String directory); + + /** + * Deletes a folder or a directory marker depending on the service + * + * @param container + * container to delete the directory from + * @param directory + * full path to the directory to delete + */ + void deleteDirectory(String containerName, String name); + + /** + * Determines if a blob exists + * + * @param container + * container where the blob resides + * @param directory + * full path to the blob + */ + boolean blobExists(String container, String name); + + + /** + * Deletes a {@code Blob} representing the data at location {@code container/name} + * + * @param container + * container where this exists. + * @param name + * fully qualified name relative to the container. + * @throws org.jclouds.blobstore.ContainerNotFoundException + * if the container doesn't exist + */ + void removeBlob(String container, String name); + + /** + * @return a count of all blobs in the container, excluding directory markers + */ + long countBlobs(String container); + +} diff --git a/compute/src/main/java/org/jclouds/compute/domain/ComputeMetadata.java b/compute/src/main/java/org/jclouds/compute/domain/ComputeMetadata.java index 347acb0b95..7875abd669 100644 --- a/compute/src/main/java/org/jclouds/compute/domain/ComputeMetadata.java +++ b/compute/src/main/java/org/jclouds/compute/domain/ComputeMetadata.java @@ -25,11 +25,14 @@ import org.jclouds.javax.annotation.Nullable; import com.google.inject.ImplementedBy; +import org.jclouds.management.annotations.ManagedAttribute; +import org.jclouds.management.annotations.ManagedType; /** * @author Ivan Meredith * @author Adrian Cole */ +@ManagedType @ImplementedBy(ComputeMetadataImpl.class) public interface ComputeMetadata extends ResourceMetadata { /** @@ -60,6 +63,7 @@ public interface ComputeMetadata extends ResourceMetadata { * * @return unique id within your account on the provider */ + @ManagedAttribute(description = "The id") public String getId(); /** diff --git a/compute/src/main/java/org/jclouds/compute/domain/ExecResponse.java b/compute/src/main/java/org/jclouds/compute/domain/ExecResponse.java index 178dcfd983..54ff5a7ad9 100644 --- a/compute/src/main/java/org/jclouds/compute/domain/ExecResponse.java +++ b/compute/src/main/java/org/jclouds/compute/domain/ExecResponse.java @@ -21,10 +21,13 @@ import org.jclouds.compute.config.CustomizationResponse; import com.google.common.base.Objects; +import org.jclouds.management.annotations.ManagedAttribute; +import org.jclouds.management.annotations.ManagedType; /** * @author Adrian Cole */ +@ManagedType public class ExecResponse implements CustomizationResponse { private final String output; @@ -37,14 +40,17 @@ public ExecResponse(String output, String error, int exitStatus) { this.exitStatus = exitStatus; } + @ManagedAttribute(description = "The standard error") public String getError() { return error; } + @ManagedAttribute(description = "The standard output") public String getOutput() { return output; } + @ManagedAttribute(description = "The exit status") public int getExitStatus() { return exitStatus; } diff --git a/compute/src/main/java/org/jclouds/compute/domain/Hardware.java b/compute/src/main/java/org/jclouds/compute/domain/Hardware.java index 14fd35af0e..4822d752d4 100644 --- a/compute/src/main/java/org/jclouds/compute/domain/Hardware.java +++ b/compute/src/main/java/org/jclouds/compute/domain/Hardware.java @@ -25,6 +25,8 @@ import com.google.common.base.Predicate; import com.google.inject.ImplementedBy; +import org.jclouds.management.annotations.ManagedAttribute; +import org.jclouds.management.annotations.ManagedType; /** * Size of a node. @@ -32,6 +34,7 @@ * @author Adrian Cole */ @ImplementedBy(HardwareImpl.class) +@ManagedType public interface Hardware extends ComputeMetadata { /** @@ -42,6 +45,7 @@ public interface Hardware extends ComputeMetadata { /** * Amount of RAM provided in MB (256M, 1740) */ + @ManagedAttribute int getRam(); /** @@ -58,5 +62,6 @@ public interface Hardware extends ComputeMetadata { * @return hypervisor type, if this is a virtual machine and the type is known */ @Nullable + @ManagedAttribute String getHypervisor(); } diff --git a/compute/src/main/java/org/jclouds/compute/domain/Image.java b/compute/src/main/java/org/jclouds/compute/domain/Image.java index ebfc8adcec..010f0d2ed3 100644 --- a/compute/src/main/java/org/jclouds/compute/domain/Image.java +++ b/compute/src/main/java/org/jclouds/compute/domain/Image.java @@ -23,12 +23,15 @@ import com.google.common.annotations.Beta; import com.google.inject.ImplementedBy; +import org.jclouds.management.annotations.ManagedAttribute; +import org.jclouds.management.annotations.ManagedType; /** * Running Operating system * * @author Adrian Cole */ +@ManagedType @ImplementedBy(ImageImpl.class) public interface Image extends ComputeMetadataIncludingStatus { @@ -60,16 +63,19 @@ public static enum Status { * The operating system installed on this image */ @Beta + @ManagedAttribute( description = "The Operating System of the Image") OperatingSystem getOperatingSystem(); /** * Version of the image */ + @ManagedAttribute( description = "The version of the Image") String getVersion(); /** * Description of the image. */ + @ManagedAttribute( description = "The description of the Image") String getDescription(); /** diff --git a/compute/src/main/java/org/jclouds/compute/domain/NodeMetadata.java b/compute/src/main/java/org/jclouds/compute/domain/NodeMetadata.java index 7d49fa68d4..e63f4a2397 100644 --- a/compute/src/main/java/org/jclouds/compute/domain/NodeMetadata.java +++ b/compute/src/main/java/org/jclouds/compute/domain/NodeMetadata.java @@ -26,12 +26,15 @@ import org.jclouds.javax.annotation.Nullable; import com.google.inject.ImplementedBy; +import org.jclouds.management.annotations.ManagedAttribute; +import org.jclouds.management.annotations.ManagedType; /** * @author Adrian Cole * @author Ivan Meredith */ @ImplementedBy(NodeMetadataImpl.class) +@ManagedType public interface NodeMetadata extends ComputeMetadataIncludingStatus { public static enum Status { @@ -70,6 +73,7 @@ public static enum Status { * */ @Nullable + @ManagedAttribute( description = "The hostname of the node") String getHostname(); /** @@ -80,6 +84,7 @@ public static enum Status { * */ @Nullable + @ManagedAttribute( description = "The group of the node") String getGroup(); /** @@ -87,6 +92,7 @@ public static enum Status { * The hardware this node is running, if possible to determine. */ @Nullable + @ManagedAttribute( description = "The hardware of the node") Hardware getHardware(); /** @@ -94,6 +100,7 @@ public static enum Status { * The id of the image this node was created from, if possible to correlate. */ @Nullable + @ManagedAttribute( description = "The id of the image the node is running") String getImageId(); /** @@ -101,11 +108,13 @@ public static enum Status { * The operating system this node is running, if possible to determine. */ @Nullable + @ManagedAttribute( description = "The operating system the node is running") OperatingSystem getOperatingSystem(); /** * @return the TCP port used for terminal connections. Generally, this is port 22 for ssh. */ + @ManagedAttribute( description = "The login port of the node") int getLoginPort(); /** @@ -120,11 +129,13 @@ public static enum Status { /** * All public IP addresses, potentially including shared ips. */ + @ManagedAttribute( description = "The set of public addresses assigned to the node") Set getPublicAddresses(); /** * All private IP addresses. */ + @ManagedAttribute( description = "The set of private addresses assigned to the node") Set getPrivateAddresses(); } diff --git a/compute/src/main/java/org/jclouds/compute/domain/OperatingSystem.java b/compute/src/main/java/org/jclouds/compute/domain/OperatingSystem.java index fa7598796f..a8db5d7fc0 100644 --- a/compute/src/main/java/org/jclouds/compute/domain/OperatingSystem.java +++ b/compute/src/main/java/org/jclouds/compute/domain/OperatingSystem.java @@ -26,6 +26,8 @@ import com.google.common.annotations.Beta; import com.google.common.base.Objects; import com.google.common.base.Objects.ToStringHelper; +import org.jclouds.management.annotations.ManagedAttribute; +import org.jclouds.management.annotations.ManagedType; /** * Running Operating system @@ -33,6 +35,7 @@ * @author Adrian Cole */ @Beta +@ManagedType public class OperatingSystem { public static Builder builder() { @@ -148,6 +151,7 @@ public OperatingSystem(@Nullable OsFamily family, @Nullable String name, @Nullab * approach. */ @Nullable + @ManagedAttribute( description = "The operating system family") public OsFamily getFamily() { return family; } @@ -162,6 +166,7 @@ public OsFamily getFamily() { * @return operating system name or null if it couldn't be determined. */ @Nullable + @ManagedAttribute( description = "The name of the operating system") public String getName() { return name; } @@ -180,6 +185,7 @@ public String getName() { * @return operating system architecture or null if it couldn't be determined. */ @Nullable + @ManagedAttribute( description = "The operating system architecture") public String getArch() { return arch; } @@ -197,6 +203,7 @@ public String getArch() { * @return operating system version or null if it couldn't be determined. */ @Nullable + @ManagedAttribute( description = "The operating system version") public String getVersion() { return version; } @@ -210,6 +217,7 @@ public String getVersion() { * * @return operating system description */ + @ManagedAttribute( description = "The operating system description") public String getDescription() { return description; } @@ -218,6 +226,7 @@ public String getDescription() { * * @return whether this operating system supports 64 bit computation. */ + @ManagedAttribute( description = "Marks if operating system is 64bit") public boolean is64Bit() { return is64Bit; } diff --git a/compute/src/main/java/org/jclouds/compute/domain/OsFamily.java b/compute/src/main/java/org/jclouds/compute/domain/OsFamily.java index 7dd52d7b9a..f47a8dbd6f 100644 --- a/compute/src/main/java/org/jclouds/compute/domain/OsFamily.java +++ b/compute/src/main/java/org/jclouds/compute/domain/OsFamily.java @@ -18,6 +18,9 @@ */ package org.jclouds.compute.domain; +import org.jclouds.management.annotations.ManagedAttribute; +import org.jclouds.management.annotations.ManagedType; + import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.CaseFormat.*; @@ -28,6 +31,7 @@ * * @author Adrian Cole */ +@ManagedType public enum OsFamily { UNRECOGNIZED, AIX, ARCH, CENTOS, DARWIN, DEBIAN, ESX, FEDORA, FREEBSD, GENTOO, HPUX, LINUX, /** @@ -52,6 +56,7 @@ public enum OsFamily { */ GCEL, SIGAR, SLACKWARE, SOLARIS, SUSE, TURBOLINUX, CLOUD_LINUX, UBUNTU, WINDOWS; + @ManagedAttribute public String value() { return UPPER_UNDERSCORE.to(LOWER_HYPHEN, name()); } diff --git a/compute/src/main/java/org/jclouds/compute/domain/Processor.java b/compute/src/main/java/org/jclouds/compute/domain/Processor.java index 03c6f41f08..c58d61367d 100644 --- a/compute/src/main/java/org/jclouds/compute/domain/Processor.java +++ b/compute/src/main/java/org/jclouds/compute/domain/Processor.java @@ -23,12 +23,15 @@ import com.google.common.base.Objects; import com.google.common.base.Objects.ToStringHelper; import com.google.common.collect.ComparisonChain; +import org.jclouds.management.annotations.ManagedAttribute; +import org.jclouds.management.annotations.ManagedType; /** * Processor (or CPU) as a part of {@link Hardware} of a {@link NodeMetadata} * * @author Adrian Cole */ +@ManagedType public class Processor implements Comparable { private final double cores; private final double speed; @@ -51,6 +54,7 @@ public int compareTo(Processor that) { /** * Amount of virtual or physical cores provided */ + @ManagedAttribute( description = "The number of cores") public double getCores() { return cores; } @@ -59,6 +63,7 @@ public double getCores() { * Speed, not necessarily in ghz, but certainly relevant to other processors * in the same provider. */ + @ManagedAttribute( description = "The Processor speed") public double getSpeed() { return speed; } diff --git a/compute/src/main/java/org/jclouds/compute/domain/Volume.java b/compute/src/main/java/org/jclouds/compute/domain/Volume.java index 793142358f..06a2cf4441 100644 --- a/compute/src/main/java/org/jclouds/compute/domain/Volume.java +++ b/compute/src/main/java/org/jclouds/compute/domain/Volume.java @@ -22,6 +22,8 @@ import org.jclouds.javax.annotation.Nullable; import com.google.inject.ImplementedBy; +import org.jclouds.management.annotations.ManagedAttribute; +import org.jclouds.management.annotations.ManagedType; /** * Describes what appears as a disk to an {@link OperatingSystem} @@ -29,6 +31,7 @@ * @author Adrian Cole */ @ImplementedBy(VolumeImpl.class) +@ManagedType public interface Volume { /** @@ -56,11 +59,13 @@ public enum Type { * */ @Nullable + @ManagedAttribute(description = "The id of the volume") String getId(); /** * Describes the cardinal type of a volume; used to determine scope and exclusivity. */ + @ManagedAttribute(description = "the type of the volum") Type getType(); /** @@ -68,6 +73,7 @@ public enum Type { * */ @Nullable + @ManagedAttribute(description = "The volume size") Float getSize(); /** @@ -75,18 +81,21 @@ public enum Type { * @return device this volume relates to on an operating system, if available */ @Nullable + @ManagedAttribute(description = "The device") String getDevice(); /** * * @return true if this survives restarts */ + @ManagedAttribute(description = "Marks if durable") boolean isDurable(); /** * * @return true if this is the boot device */ + @ManagedAttribute(description = "Marks if bootable") boolean isBootDevice(); } diff --git a/compute/src/main/java/org/jclouds/compute/internal/ComputeServiceContextImpl.java b/compute/src/main/java/org/jclouds/compute/internal/ComputeServiceContextImpl.java index a5aaf956b7..4af62451fb 100644 --- a/compute/src/main/java/org/jclouds/compute/internal/ComputeServiceContextImpl.java +++ b/compute/src/main/java/org/jclouds/compute/internal/ComputeServiceContextImpl.java @@ -20,17 +20,21 @@ import static com.google.common.base.Preconditions.checkNotNull; +import javax.annotation.PostConstruct; import javax.inject.Inject; import javax.inject.Singleton; import org.jclouds.Context; +import org.jclouds.management.annotations.Management; import org.jclouds.compute.ComputeService; import org.jclouds.compute.ComputeServiceContext; import org.jclouds.compute.Utils; +import org.jclouds.compute.management.ComputeServiceManagement; import org.jclouds.internal.BaseView; import org.jclouds.location.Provider; import com.google.common.reflect.TypeToken; +import org.jclouds.management.ManagementContext; /** * @author Adrian Cole @@ -39,6 +43,7 @@ public class ComputeServiceContextImpl extends BaseView implements ComputeServiceContext { private final ComputeService computeService; private final Utils utils; + private final ComputeServiceManagement computeServiceManagement; @Inject public ComputeServiceContextImpl(@Provider Context backend, @Provider TypeToken backendType, @@ -46,16 +51,30 @@ public ComputeServiceContextImpl(@Provider Context backend, @Provider TypeToken< super(backend, backendType); this.computeService = checkNotNull(computeService, "computeService"); this.utils = checkNotNull(utils, "utils"); + this.computeServiceManagement = new ComputeServiceManagement(this); } - + + @PostConstruct + public void init() { + ManagementContext managementContext = unwrap().getManagementContext(); + if (managementContext != null) { + managementContext.manage(computeServiceManagement); + } + } + @Override public ComputeService getComputeService() { return computeService; } + @Override public void close() { delegate().close(); + ManagementContext managementContext = unwrap().getManagementContext(); + if (managementContext != null) { + managementContext.unmanage(computeServiceManagement); + } } @Override diff --git a/compute/src/main/java/org/jclouds/compute/management/ComputeServiceManagement.java b/compute/src/main/java/org/jclouds/compute/management/ComputeServiceManagement.java new file mode 100644 index 0000000000..1d869a4be8 --- /dev/null +++ b/compute/src/main/java/org/jclouds/compute/management/ComputeServiceManagement.java @@ -0,0 +1,164 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.compute.management; + +import org.jclouds.compute.ComputeService; +import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.compute.domain.ComputeMetadata; +import org.jclouds.compute.domain.ExecResponse; +import org.jclouds.compute.domain.Hardware; +import org.jclouds.compute.domain.Image; +import org.jclouds.domain.Location; +import org.jclouds.management.JcloudsManagedBean; +import org.jclouds.management.functions.ToCompositeData; +import org.jclouds.management.functions.ToTabularData; + +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.TabularData; + +public class ComputeServiceManagement implements ComputeServiceManagementMBean, JcloudsManagedBean { + + private final ComputeService service; + private final ToCompositeData imageToComposite = ToCompositeData.from(Image.class); + private final ToTabularData imageToTabular = ToTabularData.from(Image.class); + private final ToTabularData hardwareToTabular = ToTabularData.from(Hardware.class); + private final ToTabularData locationToTabular = ToTabularData.from(Location.class); + private final ToCompositeData nodeToTComposite = ToCompositeData.from(ComputeMetadata.class); + private final ToTabularData nodeToTabular = ToTabularData.from(ComputeMetadata.class); + private final ToCompositeData execToComposite = ToCompositeData.from(ExecResponse.class); + + + public ComputeServiceManagement(ComputeServiceContext context) { + this.service = context.getComputeService(); + } + + /** + * {@inheritDoc} + */ + @Override + public TabularData listHardwareProfiles() throws OpenDataException { + return hardwareToTabular.apply(service.listHardwareProfiles()); + } + + /** + * {@inheritDoc} + */ + @Override + public TabularData listImages() throws OpenDataException { + return imageToTabular.apply(service.listImages()); + } + + /** + * {@inheritDoc} + */ + @Override + public CompositeData getImage(String id) throws OpenDataException { + return imageToComposite.apply(service.getImage(id)); + } + + /** + * {@inheritDoc} + */ + @Override + public TabularData listNodes() throws OpenDataException { + return nodeToTabular.apply(service.listNodes()); + } + + /** + * {@inheritDoc} + */ + @Override + public TabularData listAssignableLocations() throws OpenDataException { + return locationToTabular.apply(service.listAssignableLocations()); + } + + + /** + * {@inheritDoc} + */ + @Override + public void resumeNode(String id) { + service.resumeNode(id); + } + + /** + * {@inheritDoc} + */ + @Override + public void suspendNode(String id) { + service.suspendNode(id); + } + + + /** + * destroy the node, given its id. If it is the only node in a tag set, the dependent resources + * will also be destroyed. + */ + @Override + public void destroyNode(String id) { + service.destroyNode(id); + } + + + /** + * reboot the node, given its id. + */ + @Override + public void rebootNode(String id) { + service.destroyNode(id); + } + + /** + * Find a node by its id. + */ + @Override + public CompositeData getNode(String id) throws OpenDataException { + return nodeToTComposite.apply(service.getNodeMetadata(id)); + } + + /** + * @see #runScriptOnNode(String, String, org.jclouds.compute.options.RunScriptOptions) + */ + @Override + public CompositeData runScriptOnNode(String id, String runScript) throws OpenDataException { + return execToComposite.apply(service.runScriptOnNode(id, runScript)); + } + + /** + * Returns the type of the MBean. + * + * @return + */ + @Override + public String getType() { + return "compute"; + } + + /** + * Returns the anme of the MBean. + * + * @return + */ + @Override + public String getName() { + return service.getContext().unwrap().getName(); + } +} diff --git a/compute/src/main/java/org/jclouds/compute/management/ComputeServiceManagementMBean.java b/compute/src/main/java/org/jclouds/compute/management/ComputeServiceManagementMBean.java new file mode 100644 index 0000000000..b29103eab4 --- /dev/null +++ b/compute/src/main/java/org/jclouds/compute/management/ComputeServiceManagementMBean.java @@ -0,0 +1,130 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.compute.management; + + +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.TabularData; + + +public interface ComputeServiceManagementMBean { + + /** + * The list hardware profiles command shows you the options including virtual cpu count, memory, + * and disks. cpu count is not a portable quantity across clouds, as they are measured + * differently. However, it is a good indicator of relative speed within a cloud. memory is + * measured in megabytes and disks in gigabytes. + *

+ *

note

+ *

+ * This is a cached collection + */ + TabularData listHardwareProfiles() throws OpenDataException; + + /** + * Images define the operating system and metadata related to a node. In some clouds, Images are + * bound to a specific region, and their identifiers are different across these regions. For this + * reason, you should consider matching image requirements like operating system family with + * TemplateBuilder as opposed to choosing an image explicitly. + *

+ *

note

+ *

+ * This is a cached collection + */ + TabularData listImages() throws OpenDataException; + + + /** + * all nodes available to the current user by id. If possible, the returned set will include + * {@link org.jclouds.compute.domain.NodeMetadata} objects. + */ + TabularData listNodes() throws OpenDataException; + + /** + * The list locations command returns all the valid locations for nodes. A location has a scope, + * which is typically region or zone. A region is a general area, like eu-west, where a zone is + * similar to a datacenter. If a location has a parent, that implies it is within that location. + * For example a location can be a rack, whose parent is likely to be a zone. + *

+ *

note

+ *

+ * This is a cached collection + */ + TabularData listAssignableLocations() throws OpenDataException; + + /** + * Find an image by its id. + *

+ *

note

+ *

+ * This is an uncached call to the backend service + */ + CompositeData getImage(String id) throws OpenDataException; + + /** + * Find a node by its id. + */ + CompositeData getNode(String id) throws OpenDataException; + + + /** + * @see #runScriptOnNode(String, String, RunScriptOptions) + */ + CompositeData runScriptOnNode(String id, String runScript) throws OpenDataException; + + + /** + * resume the node from {@link org.jclouds.compute.domain.NodeState#SUSPENDED suspended} state, + * given its id. + * + *

note

+ * + * affected nodes may not resume with the same IP address(es) + */ + void resumeNode(String id); + + /** + * suspend the node, given its id. This will result in + * {@link org.jclouds.compute.domain.NodeState#SUSPENDED suspended} state. + * + *

note

+ * + * affected nodes may not resume with the same IP address(es) + * + * @throws UnsupportedOperationException + * if the underlying provider doesn't support suspend/resume + */ + void suspendNode(String id); + + + /** + * destroy the node, given its id. If it is the only node in a tag set, the dependent resources + * will also be destroyed. + */ + void destroyNode(String id); + + + /** + * reboot the node, given its id. + */ + void rebootNode(String id); + +} diff --git a/core/pom.xml b/core/pom.xml index 9d5dc214c4..2f541625a8 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -44,7 +44,9 @@ org.nnsoft.guice.rocoto*;version="[6.1,7)", * - org.jclouds*;version=${project.version};-noimport:=true + + org.jclouds*;version=${project.version};-noimport:=true + org.jclouds.osgi.Activator @@ -100,12 +102,12 @@ 4.2.0 provided - - org.osgi - org.osgi.compendium - 4.2.0 - provided - + + org.osgi + org.osgi.compendium + 4.2.0 + provided + diff --git a/core/src/main/java/org/jclouds/Context.java b/core/src/main/java/org/jclouds/Context.java index 5236a093c1..1915e1c38a 100644 --- a/core/src/main/java/org/jclouds/Context.java +++ b/core/src/main/java/org/jclouds/Context.java @@ -23,6 +23,9 @@ import org.jclouds.apis.ApiMetadata; import org.jclouds.domain.Location; import org.jclouds.internal.ContextImpl; +import org.jclouds.management.ManagementContext; +import org.jclouds.management.annotations.ManagedAttribute; +import org.jclouds.management.annotations.ManagedType; import org.jclouds.providers.ProviderMetadata; import org.jclouds.rest.Utils; @@ -39,6 +42,7 @@ * */ @ImplementedBy(ContextImpl.class) +@ManagedType public interface Context extends Location, Closeable { /** @@ -47,18 +51,21 @@ public interface Context extends Location, Closeable { * multiple properties or have explicit knowledge of how the context was created. * @return */ + @ManagedAttribute(description = "Context Identifier") String getName(); /** * @return the providerMetadata used to create this context * @see ContextBuilder#newBuilder(org.jclouds.providers.ProviderMetadata) */ + @ManagedAttribute(description = "The provider metadata") ProviderMetadata getProviderMetadata(); /** * @return the current login user, access key, email, or whatever the 'identity' field was building the context. * @see ApiMetadata#getDefaultIdentity */ + @ManagedAttribute(description = "The identity") String getIdentity(); Utils getUtils(); @@ -68,6 +75,12 @@ public interface Context extends Location, Closeable { */ Utils utils(); + /** + * Returns the {@link ManagementContext} of the Context. + * It is used by backed Contexts, to export themselves. + */ + ManagementContext getManagementContext(); + /** * Closes all connections, including executor service */ diff --git a/core/src/main/java/org/jclouds/ContextBuilder.java b/core/src/main/java/org/jclouds/ContextBuilder.java index 2d28474026..c8d668e4b6 100644 --- a/core/src/main/java/org/jclouds/ContextBuilder.java +++ b/core/src/main/java/org/jclouds/ContextBuilder.java @@ -56,6 +56,7 @@ import org.jclouds.concurrent.SingleThreaded; import org.jclouds.concurrent.config.ConfiguresExecutorService; import org.jclouds.concurrent.config.ExecutorServiceModule; +import org.jclouds.config.BindManagementToContext; import org.jclouds.config.BindNameToContext; import org.jclouds.config.BindPropertiesToExpandedValues; import org.jclouds.config.BindRestContextWithWildcardExtendsExplicitAndRawType; @@ -69,6 +70,8 @@ import org.jclouds.lifecycle.config.LifeCycleModule; import org.jclouds.logging.config.LoggingModule; import org.jclouds.logging.jdk.config.JDKLoggingModule; +import org.jclouds.management.internal.BaseManagementContext; +import org.jclouds.management.ManagementContext; import org.jclouds.providers.ProviderMetadata; import org.jclouds.providers.Providers; import org.jclouds.providers.config.BindProviderMetadataContextAndCredentials; @@ -189,6 +192,7 @@ public static ContextBuilder newBuilder(ProviderMetadata providerMetadata) { } protected Optional name = Optional.absent(); + protected Optional management = Optional.absent(); protected Optional providerMetadata = Optional.absent(); protected final String providerId; protected Optional endpoint = Optional.absent(); @@ -236,6 +240,12 @@ public ContextBuilder name(String name) { return this; } + public ContextBuilder management(ManagementContext managementContext) { + this.management = Optional.of(checkNotNull(managementContext, "managementContext")); + return this; + } + + /** * returns the current login credentials. jclouds will not cache this value. Use this when you need to change * credentials at runtime. @@ -322,7 +332,7 @@ public Injector buildInjector() { // is used to be something readable. return buildInjector(name.or(String.valueOf(Objects.hashCode(providerMetadata.getId(), providerMetadata.getEndpoint(), providerMetadata.getApiMetadata().getVersion(), credentialsSupplier))), - providerMetadata, credentialsSupplier, modules); + providerMetadata, credentialsSupplier, management.or(BaseManagementContext.INSTANCE), modules); } protected Supplier buildCredentialsSupplier(Properties expanded) { @@ -379,7 +389,7 @@ private Properties expandProperties(final Properties resolved) { return Guice.createInjector(new BindPropertiesToExpandedValues(resolved)).getInstance(Properties.class); } - public static Injector buildInjector(String name, ProviderMetadata providerMetadata, Supplier creds, List inputModules) { + public static Injector buildInjector(String name, ProviderMetadata providerMetadata, Supplier creds, ManagementContext managementContext, List inputModules) { List modules = newArrayList(); modules.addAll(inputModules); boolean restModuleSpecifiedByUser = restClientModulePresent(inputModules); @@ -396,6 +406,7 @@ public static Injector buildInjector(String name, ProviderMetadata providerMetad modules.add(new LifeCycleModule()); modules.add(new BindProviderMetadataContextAndCredentials(providerMetadata, creds)); modules.add(new BindNameToContext(name)); + modules.add(new BindManagementToContext(managementContext)); Injector returnVal = Guice.createInjector(Stage.PRODUCTION, modules); returnVal.getInstance(ExecutionList.class).execute(); return returnVal; diff --git a/core/src/main/java/org/jclouds/apis/ApiMetadata.java b/core/src/main/java/org/jclouds/apis/ApiMetadata.java index aa53f94fd4..f9396cf813 100644 --- a/core/src/main/java/org/jclouds/apis/ApiMetadata.java +++ b/core/src/main/java/org/jclouds/apis/ApiMetadata.java @@ -30,6 +30,8 @@ import com.google.common.base.Optional; import com.google.common.reflect.TypeToken; import com.google.inject.Module; +import org.jclouds.management.annotations.ManagedAttribute; +import org.jclouds.management.annotations.ManagedType; /** * The ApiMetadata interface allows jclouds to provide a plugin framework for @@ -39,6 +41,7 @@ * @since 1.5 */ @Beta +@ManagedType public interface ApiMetadata { public static interface Builder>{ @@ -147,12 +150,14 @@ public static interface Builder>{ * * @return the api's unique identifier (ex. vcloud, virtualbox) */ + @ManagedAttribute(description = "The api id") String getId(); /** * * @return the name (display name) of the api (ex. EC2 Base API) */ + @ManagedAttribute(description = "The api display name") String getName(); /** @@ -169,6 +174,7 @@ public static interface Builder>{ * @return the name (display name) of an endpoint to this api (ex. Keystone * url, vCloud Director URL). */ + @ManagedAttribute(description = "The endpoint name") String getEndpointName(); /** @@ -176,6 +182,7 @@ public static interface Builder>{ * @return the name (display name) of an identity on this api (ex. user, * email, account, apikey, tenantId:username) */ + @ManagedAttribute(description = "The identity name") String getIdentityName(); /** @@ -184,11 +191,13 @@ public static interface Builder>{ * @return the name (display name) of a credential on this api, if it is * required (ex. password, secret, rsaKey) */ + @ManagedAttribute(description = "The credential name") Optional getCredentialName(); /** * Explicitly identifies the version of an api. */ + @ManagedAttribute(description = "The api version") String getVersion(); /** @@ -199,6 +208,7 @@ public static interface Builder>{ * the build version is {@code 4.1.8r75467}. Or a vcloud endpoint may be api * version {@code 1.0} while the build is {@code 1.5.0.0.124312} */ + @ManagedAttribute(description = "The build version") Optional getBuildVersion(); /** @@ -219,6 +229,7 @@ public static interface Builder>{ * * @return the api's default endpoint, if known. */ + @ManagedAttribute(description = "The default endpoint") Optional getDefaultEndpoint(); /** @@ -226,6 +237,7 @@ public static interface Builder>{ * * @return the login identity into a provider, if known. */ + @ManagedAttribute(description = "The default identity") Optional getDefaultIdentity(); /** @@ -236,6 +248,7 @@ public static interface Builder>{ * @see #getDefaultIdentity * @see #getCredentialName */ + @ManagedAttribute(description = "The default credential") Optional getDefaultCredential(); /** diff --git a/core/src/main/java/org/jclouds/config/BindManagementToContext.java b/core/src/main/java/org/jclouds/config/BindManagementToContext.java new file mode 100644 index 0000000000..f9faba0bda --- /dev/null +++ b/core/src/main/java/org/jclouds/config/BindManagementToContext.java @@ -0,0 +1,38 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.config; + +import com.google.inject.AbstractModule; +import org.jclouds.management.ManagementContext; +import org.jclouds.management.annotations.Management; + +public class BindManagementToContext extends AbstractModule { + + private final ManagementContext managementContext; + + public BindManagementToContext(ManagementContext context) { + this.managementContext = context; + } + + @Override + protected void configure() { + bind(ManagementContext.class).annotatedWith(Management.class).toInstance(managementContext); + } +} diff --git a/core/src/main/java/org/jclouds/domain/Location.java b/core/src/main/java/org/jclouds/domain/Location.java index ffc1d7069c..2ab4fc64a1 100644 --- a/core/src/main/java/org/jclouds/domain/Location.java +++ b/core/src/main/java/org/jclouds/domain/Location.java @@ -18,6 +18,9 @@ */ package org.jclouds.domain; +import org.jclouds.management.annotations.ManagedAttribute; +import org.jclouds.management.annotations.ManagedType; + import java.util.Map; import java.util.Set; @@ -26,6 +29,7 @@ * * @author Adrian Cole */ +@ManagedType public interface Location { /** @@ -38,11 +42,13 @@ public interface Location { * Unique ID provided by the provider (us-standard, miami, etc) * */ + @ManagedAttribute( description = "The id of the location") String getId(); /** * Description of the location */ + @ManagedAttribute( description = "The Location description") String getDescription(); /** @@ -50,6 +56,14 @@ public interface Location { */ Location getParent(); + /** + * The id of the parent location. + * The method is used to avoid cycles cycles in the managed resource model. + * @return + */ + @ManagedAttribute( description = "The id of the parent") + String getParentId(); + /** * @return immutable set of metadata relating to this location */ diff --git a/core/src/main/java/org/jclouds/domain/internal/LocationImpl.java b/core/src/main/java/org/jclouds/domain/internal/LocationImpl.java index 9f44aa31ff..d188e1d99a 100644 --- a/core/src/main/java/org/jclouds/domain/internal/LocationImpl.java +++ b/core/src/main/java/org/jclouds/domain/internal/LocationImpl.java @@ -116,6 +116,14 @@ public Location getParent() { return parent; } + /** + * {@inheritDoc} + */ + @Override + public String getParentId() { + return parent != null ? parent.getParentId() : null; + } + /** * {@inheritDoc} */ diff --git a/core/src/main/java/org/jclouds/internal/ContextImpl.java b/core/src/main/java/org/jclouds/internal/ContextImpl.java index cd5eef88cc..f0b0cc2261 100644 --- a/core/src/main/java/org/jclouds/internal/ContextImpl.java +++ b/core/src/main/java/org/jclouds/internal/ContextImpl.java @@ -27,9 +27,12 @@ import java.util.Map; import java.util.Set; +import javax.annotation.PostConstruct; import javax.inject.Inject; import org.jclouds.Context; +import org.jclouds.management.ManagementContext; +import org.jclouds.management.annotations.Management; import org.jclouds.annotations.Name; import org.jclouds.domain.Credentials; import org.jclouds.domain.Location; @@ -55,23 +58,31 @@ public class ContextImpl implements Context { private final Utils utils; private final Closer closer; private final String name; + private final ManagementContext managementContext; @Inject protected ContextImpl(@Name String name, ProviderMetadata providerMetadata, @Provider Supplier creds, - Utils utils, Closer closer) { + @Management ManagementContext managementContext, Utils utils, Closer closer) { this.providerMetadata = checkNotNull(providerMetadata, "providerMetadata"); this.creds = checkNotNull(creds, "creds"); this.utils = checkNotNull(utils, "utils"); this.closer = checkNotNull(closer, "closer"); this.name = checkNotNull(name, "name"); + this.managementContext = checkNotNull(managementContext, "managementContext"); } + @PostConstruct + public void init() { + managementContext.register(this); + } + /** * {@inheritDoc} */ @Override public void close() { closeQuietly(closer); + managementContext.unregister(this); } /** @@ -111,6 +122,14 @@ public Utils utils() { return utils; } + /** + * {@inheritDoc} + */ + @Override + public ManagementContext getManagementContext() { + return managementContext; + } + /** * {@inheritDoc} */ @@ -184,6 +203,15 @@ public Location getParent() { return null; } + /** + /** + * {@inheritDoc} + */ + @Override + public String getParentId() { + return getParent() != null ? getParent().getId() : null; + } + /** * {@inheritDoc} */ diff --git a/core/src/main/java/org/jclouds/io/ContentMetadata.java b/core/src/main/java/org/jclouds/io/ContentMetadata.java index 2e1d8ec51a..c2d687ed08 100644 --- a/core/src/main/java/org/jclouds/io/ContentMetadata.java +++ b/core/src/main/java/org/jclouds/io/ContentMetadata.java @@ -31,10 +31,13 @@ import org.jclouds.javax.annotation.Nullable; import com.google.common.collect.ImmutableSet; +import org.jclouds.management.annotations.ManagedAttribute; +import org.jclouds.management.annotations.ManagedType; /** * @author Adrian Cole */ +@ManagedType public interface ContentMetadata { public static final Set HTTP_HEADERS = ImmutableSet.of(CONTENT_LENGTH, CONTENT_MD5, CONTENT_TYPE, CONTENT_DISPOSITION, CONTENT_ENCODING, CONTENT_LANGUAGE, EXPIRES); @@ -54,6 +57,7 @@ public interface ContentMetadata { * @see org.jclouds.http.options.GetOptions */ @Nullable + @ManagedAttribute(description = "The content length") Long getContentLength(); /** @@ -62,6 +66,7 @@ public interface ContentMetadata { * @see */ @Nullable + @ManagedAttribute(description = "Specifies presentational information") String getContentDisposition(); /** @@ -72,6 +77,7 @@ public interface ContentMetadata { * @see */ @Nullable + @ManagedAttribute(description = "Content encoding") String getContentEncoding(); /** @@ -82,6 +88,7 @@ public interface ContentMetadata { * @see */ @Nullable + @ManagedAttribute(description = "Content type") String getContentType(); @Nullable @@ -103,6 +110,7 @@ public interface ContentMetadata { * @see */ @Nullable + @ManagedAttribute(description = "Expiration Date") Date getExpires(); ContentMetadataBuilder toBuilder(); diff --git a/core/src/main/java/org/jclouds/io/MutableContentMetadata.java b/core/src/main/java/org/jclouds/io/MutableContentMetadata.java index ecebe17bd4..30c89df207 100644 --- a/core/src/main/java/org/jclouds/io/MutableContentMetadata.java +++ b/core/src/main/java/org/jclouds/io/MutableContentMetadata.java @@ -21,10 +21,12 @@ import java.util.Date; import org.jclouds.javax.annotation.Nullable; +import org.jclouds.management.annotations.ManagedType; /** * @author Adrian Cole */ +@ManagedType public interface MutableContentMetadata extends ContentMetadata { void setContentLength(@Nullable Long contentLength); diff --git a/core/src/main/java/org/jclouds/io/Payload.java b/core/src/main/java/org/jclouds/io/Payload.java index 530ecb01a3..b232c94906 100644 --- a/core/src/main/java/org/jclouds/io/Payload.java +++ b/core/src/main/java/org/jclouds/io/Payload.java @@ -22,10 +22,13 @@ import java.io.InputStream; import com.google.common.io.InputSupplier; +import org.jclouds.management.annotations.ManagedAttribute; +import org.jclouds.management.annotations.ManagedType; /** * @author Adrian Cole */ +@ManagedType public interface Payload extends InputSupplier, WriteTo, Closeable { /** @@ -41,6 +44,7 @@ public interface Payload extends InputSupplier, WriteTo, Closeable /** * Tells if the payload is capable of producing its data more than once. */ + @ManagedAttribute(description = "Tells if the payload is capable of producing its data more than once") boolean isRepeatable(); /** @@ -48,6 +52,7 @@ public interface Payload extends InputSupplier, WriteTo, Closeable */ void release(); + @ManagedAttribute(description = "Content Metadata") MutableContentMetadata getContentMetadata(); void setContentMetadata(MutableContentMetadata in); diff --git a/core/src/main/java/org/jclouds/io/PayloadEnclosing.java b/core/src/main/java/org/jclouds/io/PayloadEnclosing.java index a880fef909..0d30780f7a 100644 --- a/core/src/main/java/org/jclouds/io/PayloadEnclosing.java +++ b/core/src/main/java/org/jclouds/io/PayloadEnclosing.java @@ -22,11 +22,14 @@ import java.io.InputStream; import org.jclouds.javax.annotation.Nullable; +import org.jclouds.management.annotations.ManagedAttribute; +import org.jclouds.management.annotations.ManagedType; /** * * @author Adrian Cole */ +@ManagedType public interface PayloadEnclosing { /** @@ -48,6 +51,7 @@ public interface PayloadEnclosing { void setPayload(String data); @Nullable + @ManagedAttribute (description = "The enclosed payload") Payload getPayload(); } diff --git a/core/src/main/java/org/jclouds/management/JcloudsManagedBean.java b/core/src/main/java/org/jclouds/management/JcloudsManagedBean.java new file mode 100644 index 0000000000..c2b6e21329 --- /dev/null +++ b/core/src/main/java/org/jclouds/management/JcloudsManagedBean.java @@ -0,0 +1,42 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.management; + +/** + * An interface that describes jclouds managed beans. + * It's used for generating the {@link javax.management.ObjectName}. + */ +public interface JcloudsManagedBean { + + /** + * Returns the type of the MBean. + * @return + */ + String getType(); + + + /** + * Returns the name of the MBean. + * @return + */ + String getName(); + + +} diff --git a/core/src/main/java/org/jclouds/management/JcloudsManagementCore.java b/core/src/main/java/org/jclouds/management/JcloudsManagementCore.java new file mode 100644 index 0000000000..ba8c126ae8 --- /dev/null +++ b/core/src/main/java/org/jclouds/management/JcloudsManagementCore.java @@ -0,0 +1,140 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.management; + +import com.google.common.base.Charsets; + +import com.google.common.base.Strings; +import com.google.common.io.Closeables; +import org.jclouds.Context; +import org.jclouds.ContextBuilder; +import org.jclouds.apis.ApiMetadata; +import org.jclouds.apis.Apis; +import org.jclouds.management.functions.ToCompositeData; +import org.jclouds.management.functions.ToTabularData; +import org.jclouds.management.internal.BaseManagementContext; +import org.jclouds.providers.ProviderMetadata; +import org.jclouds.providers.Providers; + +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.TabularData; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.Properties; + +/** + * Core Jclouds MBean for displaying available {@link ApiMetadata}, {@link ProviderMetadata} and {@link Context}s. + * Also useful for creating contexts. + */ +public class JcloudsManagementCore implements JcloudsManagementCoreMBean, JcloudsManagedBean { + + private final ManagementContext managementContext; + + private final ToCompositeData apiToComposite = ToCompositeData.from(ApiMetadata.class); + private final ToTabularData apiToTabular = ToTabularData.from(ApiMetadata.class); + private final ToCompositeData providerToComposite = ToCompositeData.from(ProviderMetadata.class); + private final ToTabularData providerToTabular = ToTabularData.from(ProviderMetadata.class); + private final ToCompositeData contextToComposite = ToCompositeData.from(Context.class); + private final ToTabularData contextToTabular = ToTabularData.from(Context.class); + + public JcloudsManagementCore() { + this(BaseManagementContext.INSTANCE); + + } + + public JcloudsManagementCore(ManagementContext managementContext) { + this.managementContext = managementContext; + } + + /** + * {@inheritDoc} + */ + @Override + public TabularData getApis() throws OpenDataException { + return apiToTabular.apply(Apis.all()); + } + + /** + * {@inheritDoc} + */ + @Override + public CompositeData findApiById(String id) throws OpenDataException { + return apiToComposite.apply(Apis.withId(id)); + } + + /** + * {@inheritDoc} + */ + @Override + public TabularData getProviders() throws OpenDataException { + return providerToTabular.apply(Providers.all()); + } + + /** + * {@inheritDoc} + */ + @Override + public CompositeData findProviderById(String id) throws OpenDataException { + return providerToComposite.apply(Providers.withId(id)); + } + + /** + * {@inheritDoc} + */ + @Override + public TabularData getContexts() throws OpenDataException { + return contextToTabular.apply(managementContext.listContexts()); + } + + /** + * {@inheritDoc} + */ + @Override + public CompositeData createContext(String id, String name, String identity, String credential, String endpoint, String overrides) throws IOException, OpenDataException { + Properties props = new Properties(); + if (!Strings.isNullOrEmpty(overrides)) { + ByteArrayInputStream bis = null; + try { + bis = new ByteArrayInputStream(overrides.getBytes(Charsets.UTF_8)); + props.load(bis); + } finally { + Closeables.close(bis, true); + } + } + return contextToComposite.apply(ContextBuilder.newBuilder(id).name(name).credentials(identity, credential).endpoint(endpoint).overrides(props).build()); + } + + /** + * {@inheritDoc} + */ + @Override + public String getType() { + return "management"; + } + + /** + * {@inheritDoc} + */ + @Override + public String getName() { + return "core"; + } +} diff --git a/core/src/main/java/org/jclouds/management/JcloudsManagementCoreMBean.java b/core/src/main/java/org/jclouds/management/JcloudsManagementCoreMBean.java new file mode 100644 index 0000000000..464022101d --- /dev/null +++ b/core/src/main/java/org/jclouds/management/JcloudsManagementCoreMBean.java @@ -0,0 +1,77 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.management; + +import org.jclouds.Context; +import org.jclouds.apis.ApiMetadata; +import org.jclouds.providers.ProviderMetadata; + +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.TabularData; +import java.io.IOException; + + +public interface JcloudsManagementCoreMBean { + + /** + * Lists all available {@link ApiMetadata}. + * @return + */ + TabularData getApis() throws OpenDataException; + + /** + * Find {@link ApiMetadata} by id. + * @return + */ + CompositeData findApiById(String id) throws OpenDataException; + + + /** + * Lists all available {@link ProviderMetadata} + * @return + */ + TabularData getProviders() throws OpenDataException; + + + /** + * Find {@link ProviderMetadata} by id. + * @return + */ + CompositeData findProviderById(String id) throws OpenDataException; + + /** + * Lists all {@link Context} objects. + * @return + */ + TabularData getContexts() throws OpenDataException; + + /** + * Creates a {@link Context}. + * @param id + * @param name + * @param identity + * @param credential + * @param endpoint + * @param overrides + * @return + */ + public CompositeData createContext(String id, String name, String identity, String credential, String endpoint, String overrides) throws IOException, OpenDataException; +} diff --git a/core/src/main/java/org/jclouds/management/ManagementContext.java b/core/src/main/java/org/jclouds/management/ManagementContext.java new file mode 100644 index 0000000000..9f8cc5d501 --- /dev/null +++ b/core/src/main/java/org/jclouds/management/ManagementContext.java @@ -0,0 +1,90 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.management; + +import org.jclouds.Context; + +import javax.management.MBeanServer; + +/** + * The management context, keeps track of the {@link JcloudsManagedBean} objects that have been created. + * It is responsible for exporting beans to the {@link MBeanServer}, whenever it becomes available. + * It also keeps track of {@link Context}s created, so that they can be accessed via JMX. + */ +public interface ManagementContext { + + /** + * Register a {@link JcloudsManagedBean} to the MBeanServer. + * @param mBean + */ + void manage(JcloudsManagedBean mBean); + + /** + * Un-registers a {@link JcloudsManagedBean} to the MBeanServer. + * @param mBean + */ + void unmanage(JcloudsManagedBean mBean); + + + /** + * Bind an {@link MBeanServer} to the context. + * This is mostly useful for dynamic environments where an {@link MBeanServer} may come and go. + * The context should re-register the {@link JcloudsManagedBean} objects that have been added to the context. + * @param mBeanServer + */ + void bind(MBeanServer mBeanServer); + + /** + * Unbind an {@link MBeanServer} to the context. + * This is mostly useful for dynamic environments where an {@link MBeanServer} may come and go. + * The context should unregister the {@link JcloudsManagedBean} objects that have been added to the context. + * @param mBeanServer + */ + void unbind(MBeanServer mBeanServer); + + /** + * Register {@link org.jclouds.Context}. + * @param context + * @param + */ + void register(C context); + + /** + * Un-register {@link Context}. + * @param context + * @param + */ + void unregister(C context); + + /** + * List all registered {@link Context} objects. + * @return + */ + Iterable listContexts(); + + + /** + * Returns {@link Context} by name. + * @param name + * @param + */ + C getContext(String name); + +} diff --git a/core/src/main/java/org/jclouds/management/annotations/ManagedAttribute.java b/core/src/main/java/org/jclouds/management/annotations/ManagedAttribute.java new file mode 100644 index 0000000000..748a133da4 --- /dev/null +++ b/core/src/main/java/org/jclouds/management/annotations/ManagedAttribute.java @@ -0,0 +1,36 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.management.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.METHOD, ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +public @interface ManagedAttribute { + + String description() default ""; +} diff --git a/core/src/main/java/org/jclouds/management/annotations/ManagedType.java b/core/src/main/java/org/jclouds/management/annotations/ManagedType.java new file mode 100644 index 0000000000..9fe442b722 --- /dev/null +++ b/core/src/main/java/org/jclouds/management/annotations/ManagedType.java @@ -0,0 +1,36 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.management.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +public @interface ManagedType { + + String name() default ""; +} diff --git a/core/src/main/java/org/jclouds/management/annotations/Management.java b/core/src/main/java/org/jclouds/management/annotations/Management.java new file mode 100644 index 0000000000..5712cf594a --- /dev/null +++ b/core/src/main/java/org/jclouds/management/annotations/Management.java @@ -0,0 +1,36 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.management.annotations; + +import javax.inject.Qualifier; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Target( { ANNOTATION_TYPE, FIELD, METHOD, PARAMETER }) +@Retention(RUNTIME) +@Qualifier +public @interface Management { +} diff --git a/core/src/main/java/org/jclouds/management/functions/ToCompositeData.java b/core/src/main/java/org/jclouds/management/functions/ToCompositeData.java new file mode 100644 index 0000000000..b7e104d394 --- /dev/null +++ b/core/src/main/java/org/jclouds/management/functions/ToCompositeData.java @@ -0,0 +1,223 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.management.functions; + +import com.google.common.base.Function; +import com.google.common.base.Optional; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.Iterables; +import com.google.common.reflect.Invokable; +import com.google.common.reflect.TypeToken; +import org.jclouds.javax.annotation.Nullable; +import org.jclouds.management.internal.ManagedTypeModel; + +import javax.management.openmbean.ArrayType; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.SimpleType; +import javax.management.openmbean.TabularData; +import javax.management.openmbean.TabularType; +import java.lang.reflect.InvocationTargetException; +import java.util.concurrent.ExecutionException; + +import static org.jclouds.reflect.Reflection2.getPropertyValue; +import static org.jclouds.reflect.Reflection2.isAssignable; +import static org.jclouds.reflect.Reflection2.method; +import static org.jclouds.reflect.Reflection2.typeParameterOf; + +/** + * A {@link Function} that converts an Object to {@link CompositeData}. + * @param + */ +public final class ToCompositeData implements Function { + + private final Class type; + private final ManagedTypeModel model; + private static final LoadingCache, ToCompositeData> CACHE = CacheBuilder.newBuilder().build(new CacheLoader, ToCompositeData>() { + + @Override + public ToCompositeData load(Class key) throws Exception { + return new ToCompositeData(key); + } + }); + + /** + * Constructor. + * Only available to cache. + * @param type + */ + private ToCompositeData(Class type) { + this.type = type; + this.model = ManagedTypeModel.of(type); + } + + /** + * Factory method. + * @param type + * @param + * @return + */ + public static ToCompositeData from(Class type) { + try { + return (ToCompositeData) CACHE.get(type); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } + } + + /** + * Conversion Function. + * @param input + * @return + */ + @Override + public CompositeData apply(@Nullable T input) { + int index = 0; + int size = model.getNames().size(); + String[] attributeNames = model.getNames().toArray(new String[size]); + Object[] attributeValues = new Object[size]; + + for (String attributeName : attributeNames) { + TypeToken raw = model.getTypeToken(attributeName); + OpenType openType = model.getOpenType(attributeName); + try { + attributeValues[index] = convertValue(getValue(input, attributeName), raw, openType); + } catch (Exception ex) { + attributeValues[index] = null; + } + index++; + } + + try { + return new CompositeDataSupport(model.getCompositeType(), attributeNames, attributeValues); + } catch (OpenDataException e) { + throw new RuntimeException(e); + } + } + + /** + * Converts a value to the appropriate type. + * + * @param value + * @param raw + * @param openType + * @return + */ + private Object convertValue(Object value, TypeToken raw, OpenType openType) throws OpenDataException { + if (value == null) { + return null; + } else if (isAssignable(Optional.class, value.getClass())) { + //Unwarp Optional type. + return convertOptional((Optional) value, raw, openType); + } else if (isAssignable(Iterable.class, value.getClass()) && isAssignable(ArrayType.class, openType.getClass())) { + //Convert to array. + return convertToArray((Iterable) value, raw, (ArrayType) openType); + } else if (isAssignable(Iterable.class, value.getClass()) && isAssignable(TabularType.class, openType.getClass())) { + //Convert to tabular + return convertToTabular((Iterable) value, raw, (TabularType) openType); + } else if (isAssignable(CompositeType.class, openType.getClass())) { + //Convert to complex type. + return convertToComposite(value, raw, (CompositeType) openType); + //Get String value of. + } else if (SimpleType.STRING.equals(openType) && !String.class.isAssignableFrom(value.getClass())) { + return String.valueOf(value); + } + return value; + } + + /** + * Converts an object to the matching composite type. + * + * @param object + * @param raw + * @param openType + * @return + */ + private CompositeData convertToComposite(Object object, TypeToken raw, CompositeType openType) throws OpenDataException { + return ToCompositeData.from(raw.getRawType()).apply(object); + } + + /** + * Converts an object to the matching composite type. + * + * @param iterable + * @param raw + * @param openType + * @return + */ + private TabularData convertToTabular(Iterable iterable, TypeToken raw, TabularType openType) throws OpenDataException { + return ToTabularData.from(raw.getRawType()).apply(iterable); + } + + /** + * Converts an Itearble to an Object array. + * + * @param iterable + * @param raw + * @param openType + * @return + */ + private Object[] convertToArray(Iterable iterable, TypeToken raw, ArrayType openType) { + return Iterables.toArray(iterable, typeParameterOf(raw.getType())); + } + + + /** + * Converts an {@link Optional} object to the matching composite type. + * + * @param optional + * @param raw + * @param openType + * @return + */ + private Object convertOptional(Optional optional, TypeToken raw, OpenType openType) throws OpenDataException { + return convertValue(optional.orNull(), raw, openType); + } + + /** + * Extracts the attribute value from the target object. + * The attribute is retrieved from a matching getter, filed or method. + * + * @param target + * @param attribute + * @return + * @throws java.lang.reflect.InvocationTargetException + * + * @throws IllegalAccessException + * @throws NoSuchMethodException + */ + private Object getValue(Object target, String attribute) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { + try { + return getPropertyValue(target, attribute); + } catch (Exception ex) { + Invokable invokable = method(target.getClass(), attribute); + if (invokable != null) { + return invokable.invoke(target); + } else { + throw new NoSuchMethodException("No fields, getter or methods found for property " + attribute + " on " + target.getClass()); + } + } + } +} diff --git a/core/src/main/java/org/jclouds/management/functions/ToOpenType.java b/core/src/main/java/org/jclouds/management/functions/ToOpenType.java new file mode 100644 index 0000000000..06c0b2fd93 --- /dev/null +++ b/core/src/main/java/org/jclouds/management/functions/ToOpenType.java @@ -0,0 +1,140 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.management.functions; + +import com.google.common.base.Function; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.reflect.TypeToken; +import org.jclouds.javax.annotation.Nullable; +import org.jclouds.management.internal.ManagedTypeModel; + +import javax.management.openmbean.ArrayType; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.SimpleType; +import javax.management.openmbean.TabularType; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.URI; +import java.net.URL; +import java.util.Collection; +import java.util.Date; +import static org.jclouds.reflect.Reflection2.isAssignable; + +/** + * A singleton function that is used to map java {@link Type} to mbean {@link OpenType}. + * The method will map native types to the corresponding {@link OpenType}s. + * Classes that are annotated with {@link org.jclouds.management.annotations.ManagedType} will be mapped to ComplexTypes. + * Collections, will be mapped to ArrayType or TabularType for native and managed collection types respectively. + * Optional types will be mapped to the generic type. + * + */ +public enum ToOpenType implements Function { + + FUNCTION; + + private static final LoadingCache OPEN_TYPE_MAPPING = CacheBuilder.newBuilder().build(new CacheLoader() { + @Override + public OpenType load(Type key) throws Exception { + + if (key instanceof ParameterizedType) { + ParameterizedType p = (ParameterizedType) key; + Type generic = p.getActualTypeArguments()[0]; + Type raw = p.getRawType(); + OpenType genericOpenType = ToOpenType.FUNCTION.apply(generic); + + //1. Collections of primitives should be mapped to ArrayType. + //2. Collections of SimpleType should be mapped to ArrayType. + //3. Collections of CompositeType should be mapped to TabularType. + //4. All other generics should map the generic type. + + if (isAssignable(Collection.class, raw) && TypeToken.of(generic).getRawType().isPrimitive()) { + return new ArrayType((SimpleType) genericOpenType, true); + } else if (isAssignable(Collection.class, raw) && isAssignable(SimpleType.class, genericOpenType.getClass())) { + return new ArrayType((SimpleType) genericOpenType, false); + } else if (isAssignable(Collection.class, raw) && isAssignable(CompositeType.class, genericOpenType.getClass())) { + CompositeType compositeType = (CompositeType) genericOpenType; + return new TabularType(compositeType.getTypeName(), compositeType.getDescription(), compositeType, compositeType.keySet().toArray(new String[compositeType.keySet().size()])); + } else { + return genericOpenType; + } + } else if (key instanceof Class) { + Class clazz = (Class) key; + return ManagedTypeModel.of(clazz).getCompositeType(); + } + return null; + } + }); + + //Pre-loading of the standard mapping. + static { + OPEN_TYPE_MAPPING.put(boolean.class, SimpleType.BOOLEAN); + OPEN_TYPE_MAPPING.put(Boolean.class, SimpleType.BOOLEAN); + OPEN_TYPE_MAPPING.put(byte.class, SimpleType.BYTE); + OPEN_TYPE_MAPPING.put(Byte.class, SimpleType.BYTE); + OPEN_TYPE_MAPPING.put(char.class, SimpleType.CHARACTER); + OPEN_TYPE_MAPPING.put(Character.class, SimpleType.CHARACTER); + OPEN_TYPE_MAPPING.put(short.class, SimpleType.SHORT); + OPEN_TYPE_MAPPING.put(Short.class, SimpleType.SHORT); + OPEN_TYPE_MAPPING.put(int.class, SimpleType.INTEGER); + OPEN_TYPE_MAPPING.put(Integer.class, SimpleType.INTEGER); + OPEN_TYPE_MAPPING.put(long.class, SimpleType.LONG); + OPEN_TYPE_MAPPING.put(Long.class, SimpleType.LONG); + OPEN_TYPE_MAPPING.put(double.class, SimpleType.DOUBLE); + OPEN_TYPE_MAPPING.put(Double.class, SimpleType.DOUBLE); + OPEN_TYPE_MAPPING.put(float.class, SimpleType.FLOAT); + OPEN_TYPE_MAPPING.put(Float.class, SimpleType.FLOAT); + OPEN_TYPE_MAPPING.put(BigDecimal.class, SimpleType.BIGDECIMAL); + OPEN_TYPE_MAPPING.put(BigInteger.class, SimpleType.BIGINTEGER); + OPEN_TYPE_MAPPING.put(Date.class, SimpleType.DATE); + OPEN_TYPE_MAPPING.put(String.class, SimpleType.STRING); + //Indirect mappings + //Classes that have no mapping available, but we can live with their String representation. + OPEN_TYPE_MAPPING.put(URI.class, SimpleType.STRING); + OPEN_TYPE_MAPPING.put(URL.class, SimpleType.STRING); + } + + + /** + * Converts a {@link Type} to its corresponding {@link OpenType}. + * + * @throws IllegalArgumentException if type contains cyclic references or isn't applicable for any other reason. + */ + @Override + public OpenType apply(@Nullable Type type) { + OpenType result = null; + try { + result = OPEN_TYPE_MAPPING.get(type); + } catch (Exception e) { + //We are mostly catching types with cyclic references, which are illegal. + throw new IllegalArgumentException("Failed to map " + type + " to OpenType.", e); + } + + if (result == null) { + throw new IllegalArgumentException("Type " + type + " not mappable to OpenType."); + } else { + return result; + } + } +} diff --git a/core/src/main/java/org/jclouds/management/functions/ToTabularData.java b/core/src/main/java/org/jclouds/management/functions/ToTabularData.java new file mode 100644 index 0000000000..d9215a0482 --- /dev/null +++ b/core/src/main/java/org/jclouds/management/functions/ToTabularData.java @@ -0,0 +1,91 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.management.functions; + + +import com.google.common.base.Function; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import org.jclouds.javax.annotation.Nullable; +import org.jclouds.management.internal.ManagedTypeModel; + +import javax.management.openmbean.TabularData; +import javax.management.openmbean.TabularDataSupport; +import java.util.concurrent.ExecutionException; + +/** + * A {@link Function} that converts an {@link Iterable} to {@link TabularData}. + * @param + */ +public final class ToTabularData implements Function, TabularData> { + + private final ManagedTypeModel model; + private final ToCompositeData compositeConverter; + private static final LoadingCache, ToTabularData> CACHE = CacheBuilder.newBuilder().build(new CacheLoader, ToTabularData>() { + + @Override + public ToTabularData load(Class key) throws Exception { + return new ToTabularData(key); + } + }); + + /** + * Constructor. + * Only available from the internal cache. + * @param type + */ + private ToTabularData(Class type) { + this.model = ManagedTypeModel.of(type); + this.compositeConverter = ToCompositeData.from(type); + } + + /** + * Factory method. + * @param type + * @param + * @return + */ + public static ToTabularData from(Class type) { + try { + return (ToTabularData) CACHE.get(type); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } + } + + /** + * Conversion function. + * @param input + * @return + */ + @Override + public TabularData apply(@Nullable Iterable input) { + TabularDataSupport table = new TabularDataSupport(model.getTabularType()); + for (T obj : input) { + try { + table.put(compositeConverter.apply(obj)); + } catch (Exception ex) { + ex.printStackTrace(System.out); + } + } + return table; + } +} diff --git a/core/src/main/java/org/jclouds/management/internal/BaseManagementContext.java b/core/src/main/java/org/jclouds/management/internal/BaseManagementContext.java new file mode 100644 index 0000000000..436b336686 --- /dev/null +++ b/core/src/main/java/org/jclouds/management/internal/BaseManagementContext.java @@ -0,0 +1,118 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.management.internal; + +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import org.jclouds.Context; +import org.jclouds.management.JcloudsManagedBean; +import org.jclouds.management.ManagementContext; + +import javax.management.MBeanServer; +import java.lang.management.ManagementFactory; +import java.util.Map; +import java.util.Set; + +public enum BaseManagementContext implements ManagementContext { + + INSTANCE; + + private final Map contexts = Maps.newHashMap(); + private final Set mbeans = Sets.newHashSet(); + + private MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); + + /** + * {@inheritDoc} + */ + @Override + public synchronized void manage(JcloudsManagedBean mBean) { + if (mBeanServer != null) { + ManagementUtils.register(mBeanServer, mBean, mBean.getType(), mBean.getName()); + } + mbeans.add(mBean); + } + + /** + * {@inheritDoc} + */ + @Override + public synchronized void unmanage(JcloudsManagedBean mBean) { + if (mBeanServer != null) { + ManagementUtils.register(mBeanServer, mBean, mBean.getType(), mBean.getName()); + } + mbeans.remove(mBean); + } + + /** + * {@inheritDoc} + */ + @Override + public synchronized void bind(MBeanServer server) { + this.mBeanServer = server; + for(JcloudsManagedBean mBean : mbeans) { + ManagementUtils.register(server, mBean, mBean.getType(), mBean.getName()); + } + } + + /** + * {@inheritDoc} + */ + @Override + public synchronized void unbind(MBeanServer server) { + for(JcloudsManagedBean mBean : mbeans) { + ManagementUtils.unregister(server, mBean.getType(), mBean.getName()); + } + this.mBeanServer = null; + } + + + /** + * {@inheritDoc} + */ + @Override + public void register(C context) { + contexts.put(context.getName(), context); + } + + /** + * {@inheritDoc} + */ + @Override + public void unregister(C context) { + contexts.remove(context.getName()); + } + + /** + * {@inheritDoc} + */ + @Override + public Iterable listContexts() { + return contexts.values(); + } + + /** + * {@inheritDoc} + */ + @Override + public Context getContext(String name) { + return contexts.get(name); + } +} \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/management/internal/ManagedTypeModel.java b/core/src/main/java/org/jclouds/management/internal/ManagedTypeModel.java new file mode 100644 index 0000000000..321ca80bea --- /dev/null +++ b/core/src/main/java/org/jclouds/management/internal/ManagedTypeModel.java @@ -0,0 +1,294 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.management.internal; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Strings; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.Maps; +import com.google.common.reflect.Invokable; +import com.google.common.reflect.TypeToken; +import org.jclouds.management.annotations.ManagedAttribute; +import org.jclouds.management.annotations.ManagedType; +import org.jclouds.management.functions.ToOpenType; + +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.SimpleType; +import javax.management.openmbean.TabularType; +import java.lang.reflect.Field; +import java.lang.reflect.Type; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ExecutionException; + +import static org.jclouds.reflect.Reflection2.fieldsAnnotatedWith; +import static org.jclouds.reflect.Reflection2.methodsAnnotatedWith; +import static org.jclouds.reflect.Reflection2.shortNameOf; +import static org.jclouds.reflect.Reflection2.typeToken; + +/** + * A Class that describes a {@link org.jclouds.management.annotations.ManagedType}. + * + * @param + */ +public final class ManagedTypeModel { + + private final Class type; + private final Map descriptions; + private final Map typeTokens; + private final Map openTypes; + private final CompositeType compositeType; + private final TabularType tabularType; + + private static final LoadingCache MANAGED_RESOURCE_CACHE = CacheBuilder.newBuilder().build(new CacheLoader() { + @Override + public ManagedTypeModel load(Class key) throws Exception { + return create(key); + } + }); + + + /** + * Constructor + * + * @param type + * @param openTypes + */ + private ManagedTypeModel(Class type, Map descriptions, Map typeTokens, Map openTypes) { + this.type = type; + this.descriptions = descriptions; + this.typeTokens = typeTokens; + this.openTypes = openTypes; + this.compositeType = createCompositeType(type, descriptions.keySet(), descriptions, typeTokens); + this.tabularType = createTabularType(type, descriptions.keySet(), compositeType); + } + + public static synchronized ManagedTypeModel of(Class type) { + try { + return MANAGED_RESOURCE_CACHE.get(type); + } catch (ExecutionException ex) { + throw new RuntimeException(ex); + } + } + + /** + * Creates the ManagedType info for the specified class. + * + * @param in + * @param + * @return + */ + private static ManagedTypeModel create(Class in) throws OpenDataException { + Map descriptions = Maps.newLinkedHashMap(); + Map typeTokens = Maps.newLinkedHashMap(); + Map openTypes = Maps.newLinkedHashMap(); + + boolean foundAnnotation = false; + TypeToken typeToken = TypeToken.of(in); + for (TypeToken token : typeToken.getTypes()) { + Class type = token.getRawType(); + if (type.isAnnotationPresent(ManagedType.class)) { + foundAnnotation = true; + for (Invokable invokable : methodsAnnotatedWith(type, ManagedAttribute.class)) { + String name = shortNameOf(invokable); + String description = managedAttributeDescriptionOf(invokable); + TypeToken t = invokable.getReturnType(); + descriptions.put(name, description); + typeTokens.put(name, t); + //Avoid infinite loop when. + if (type.equals(t)) { + openTypes.put(name, SimpleType.STRING); + } else { + openTypes.put(name, ToOpenType.FUNCTION.apply(t.getType())); + } + } + + for (Field field : fieldsAnnotatedWith(type, ManagedAttribute.class)) { + String name = field.getName(); + Type t = field.getGenericType(); + descriptions.put(name, managedAttributeDescriptionOf(field)); + typeTokens.put(name, typeToken(t)); + //Avoid infinite loop when. + if (type.equals(t)) { + openTypes.put(name, SimpleType.STRING); + } else { + openTypes.put(name, ToOpenType.FUNCTION.apply(t)); + } + } + + } + } + if (foundAnnotation) { + return new ManagedTypeModel(in, descriptions, typeTokens, openTypes); + } else { + throw new IllegalArgumentException("Type " + in + " is not annotated as ManagedType."); + } + } + + /** + * Creates a {@link CompositeType}. + * @param type + * @param names + * @param descriptions + * @param types + * @return + */ + private static CompositeType createCompositeType(Class type, Set names, Map descriptions, Map types) { + try { + String description = "Composite type for " + type.getCanonicalName(); + int index = 0; + int size = names.size(); + String[] attributeNames = new String[size]; + String[] attributeDescriptions = new String[size]; + OpenType[] attributeTypes = new OpenType[size]; + + + for (String name : names) { + attributeNames[index] = name; + attributeDescriptions[index] = !Strings.isNullOrEmpty(descriptions.get(name)) ? descriptions.get(name) : name; + try { + attributeTypes[index] = ToOpenType.FUNCTION.apply(types.get(name).getType()); + } catch (Exception ex) { + //We want to catch cycles here. + attributeTypes[index] = SimpleType.STRING; + } + index++; + } + + return new CompositeType(type.getCanonicalName(), description, attributeNames, + attributeDescriptions, attributeTypes); + } catch (Exception e) { + throw new IllegalStateException("Unable to build " + type.getName() + " composite type.", e); + } + } + + /** + * Creates a {@link TabularType}. + * @param type + * @param names + * @param compositeType + * @return + */ + private static TabularType createTabularType(Class type, Set names, CompositeType compositeType) { + try { + String description = "Tabular type for " + type.getCanonicalName(); + return new TabularType(type.getCanonicalName(), description, + compositeType, (String[]) names.toArray(new String[names.size()])); + } catch (Exception e) { + throw new IllegalStateException("Unable to build " + type.getName() + " tabular type.", e); + } + } + + public Class getType() { + return type; + } + + public Set getNames() { + return typeTokens.keySet(); + } + + public String getDescription(String attribute) { + return descriptions.get(attribute); + } + + public Type getType(String attribute) { + return typeTokens.get(attribute).getType(); + } + + public TypeToken getTypeToken(String attribute) { + return typeTokens.get(attribute); + } + + public OpenType getOpenType(String attribute) { + return openTypes.get(attribute); + } + + + /** + * Returns the {@link CompositeType} of this model. + * + * @return + */ + public CompositeType getCompositeType() { + return this.compositeType; + } + + /** + * Returns the {@link TabularType} of this model. + * + * @return + */ + public TabularType getTabularType() { + return this.tabularType; + } + + + /** + * Returns the description on the {@link ManagedAttribute}. + * + * @param field + * @return + */ + @VisibleForTesting + private static String managedAttributeDescriptionOf(Field field) { + ManagedAttribute attribute = field.getAnnotation(ManagedAttribute.class); + if (attribute != null) { + return attribute.description(); + } + return ""; + } + + /** + * Returns the description on the {@link ManagedAttribute}. + * + * @param invokable + * @return + */ + @VisibleForTesting + private static String managedAttributeDescriptionOf(Invokable invokable) { + ManagedAttribute attribute = invokable.getAnnotation(ManagedAttribute.class); + if (attribute != null) { + return attribute.description(); + } + return ""; + } + + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ManagedTypeModel that = (ManagedTypeModel) o; + + if (type != null ? !type.equals(that.type) : that.type != null) return false; + + return true; + } + + @Override + public int hashCode() { + return type != null ? type.hashCode() : 0; + } +} diff --git a/core/src/main/java/org/jclouds/management/internal/ManagementUtils.java b/core/src/main/java/org/jclouds/management/internal/ManagementUtils.java new file mode 100644 index 0000000000..0e518e0c30 --- /dev/null +++ b/core/src/main/java/org/jclouds/management/internal/ManagementUtils.java @@ -0,0 +1,85 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.management.internal; + +import org.jclouds.JcloudsVersion; + +import javax.management.MBeanServer; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; + +public final class ManagementUtils { + + private static final JcloudsVersion VERSION = JcloudsVersion.get(); + private static final String OBJECT_NAME_FORMAT = "org.jclouds:type=%s,name=%s,version=%d.%d"; + + private ManagementUtils() { + //Utility Class + } + + /** + * Registers a managed object to the mbean server. + * + * @param mBeanServer + * @param mbean + * @param type + * @param name + */ + public static void register(MBeanServer mBeanServer, Object mbean, String type, String name) { + try { + ObjectName objectName = objectNameFor(type, name); + if (!mBeanServer.isRegistered(objectName)) { + mBeanServer.registerMBean(mbean, objectName); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Un-registers a managed object to the mbean server. + * + * @param mBeanServer + * @param type + * @param name + */ + public static void unregister(MBeanServer mBeanServer, String type, String name) { + try { + ObjectName objectName = objectNameFor(type, name); + if (!mBeanServer.isRegistered(objectName)) { + mBeanServer.unregisterMBean(objectName); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + + /** + * Creates a jclouds {@link ObjectName} for the mbean type. + * + * @param type + * @return + * @throws MalformedObjectNameException + */ + public static ObjectName objectNameFor(String type, String name) throws MalformedObjectNameException { + return new ObjectName(String.format(OBJECT_NAME_FORMAT, type, name, VERSION.majorVersion, VERSION.minorVersion)); + } +} diff --git a/core/src/main/java/org/jclouds/osgi/Activator.java b/core/src/main/java/org/jclouds/osgi/Activator.java index 8f58deee99..84b83c5810 100644 --- a/core/src/main/java/org/jclouds/osgi/Activator.java +++ b/core/src/main/java/org/jclouds/osgi/Activator.java @@ -18,16 +18,22 @@ */ package org.jclouds.osgi; +import org.jclouds.management.JcloudsManagementCore; +import org.jclouds.management.internal.BaseManagementContext; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; import org.osgi.util.tracker.ServiceTracker; +import javax.management.MBeanServer; + public class Activator implements BundleActivator { private ServiceTracker providerListenerTracker = null; private ServiceTracker apiListenerTracker = null; + private ServiceTracker mbeanServerTracker = null; private MetadataBundleListener bundleListener = new MetadataBundleListener(); + private final JcloudsManagementCore jcloudsManagementCore = new JcloudsManagementCore(); /** * Called when this bundle is started so the Framework can perform the bundle-specific activities necessary to start @@ -84,8 +90,35 @@ public void removedService(ServiceReference reference, Object service) { } }; + mbeanServerTracker = new ServiceTracker(context, MBeanServer.class.getName(), null) { + + @Override + public Object addingService(ServiceReference reference) { + Object obj = super.addingService(reference); + if (MBeanServer.class.isAssignableFrom(obj.getClass())) { + BaseManagementContext.INSTANCE.bind((MBeanServer) obj); + BaseManagementContext.INSTANCE.manage(jcloudsManagementCore); + } + return obj; + } + + @Override + public void modifiedService(ServiceReference reference, Object service) { + super.modifiedService(reference, service); + } + + @Override + public void removedService(ServiceReference reference, Object service) { + if (MBeanServer.class.isAssignableFrom(service.getClass())) { + BaseManagementContext.INSTANCE.unbind((MBeanServer) service); + } + super.removedService(reference, service); + } + }; + providerListenerTracker.open(); apiListenerTracker.open(); + mbeanServerTracker.open(); } /** @@ -115,5 +148,8 @@ public void stop(BundleContext context) throws Exception { if (providerListenerTracker != null) { providerListenerTracker.close(); } + if (mbeanServerTracker != null) { + mbeanServerTracker.close(); + } } } diff --git a/core/src/main/java/org/jclouds/providers/ProviderMetadata.java b/core/src/main/java/org/jclouds/providers/ProviderMetadata.java index 8c3d8266d7..5aa1e0fcbc 100644 --- a/core/src/main/java/org/jclouds/providers/ProviderMetadata.java +++ b/core/src/main/java/org/jclouds/providers/ProviderMetadata.java @@ -26,6 +26,8 @@ import org.jclouds.javax.annotation.Nullable; import com.google.common.base.Optional; +import org.jclouds.management.annotations.ManagedAttribute; +import org.jclouds.management.annotations.ManagedType; /** * The ProviderMetadata interface allows jclouds to provide a plugin framework @@ -33,6 +35,7 @@ * * @author Jeremy Whitlock , Adrian Cole */ +@ManagedType public interface ProviderMetadata { /** @@ -122,12 +125,14 @@ public static interface Builder { * * @return the provider's unique identifier (ex. aws-ec2, trystack-nova) */ + @ManagedAttribute(description = "The provider id") public String getId(); /** * * @return the name (display name) of the provider (ex. GoGrid) */ + @ManagedAttribute(description = "The display name") public String getName(); /** @@ -135,12 +140,14 @@ public static interface Builder { * @return the provider's api * @since 1.5 */ + @ManagedAttribute(description = "The api metadata") public ApiMetadata getApiMetadata(); /** * @see ApiMetadata#getEndpoint * @return the url for the provider's api */ + @ManagedAttribute(description = "The endpoint") public String getEndpoint(); /** @@ -156,12 +163,14 @@ public static interface Builder { * * @return the url for the provider's console, or absent if one doesn't exist */ + @ManagedAttribute(description = "The provider console uri") Optional getConsole(); /** * * @return the url for the provider's homepage, or absent if unknown */ + @ManagedAttribute(description = "The home page") Optional getHomepage(); /** @@ -169,6 +178,7 @@ public static interface Builder { * @return ids of all known {@link ProviderMetadata providers} which have the * same account as this. */ + @ManagedAttribute(description = "Linked Services") Set getLinkedServices(); /** @@ -176,5 +186,6 @@ public static interface Builder { * * @return all known region/location ISO 3166 codes */ + @ManagedAttribute(description = "ISO 3116 codes") Set getIso3166Codes(); } diff --git a/core/src/main/java/org/jclouds/reflect/Reflection2.java b/core/src/main/java/org/jclouds/reflect/Reflection2.java index 767f071e3c..b8ed4cc7a5 100644 --- a/core/src/main/java/org/jclouds/reflect/Reflection2.java +++ b/core/src/main/java/org/jclouds/reflect/Reflection2.java @@ -23,39 +23,53 @@ import static com.google.common.base.Throwables.propagate; import static com.google.common.collect.Iterables.tryFind; +import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.Locale; import java.util.Set; import java.util.concurrent.ExecutionException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import com.google.common.annotations.Beta; import com.google.common.base.Function; import com.google.common.base.Objects; import com.google.common.base.Optional; import com.google.common.base.Predicate; +import com.google.common.base.Strings; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.reflect.Invokable; import com.google.common.reflect.Parameter; import com.google.common.reflect.TypeToken; import com.google.common.util.concurrent.UncheckedExecutionException; +import org.jclouds.javax.annotation.Nullable; /** * Utilities that allow access to {@link Invokable}s with {@link Invokable#getOwnerType() owner types}. - * + * * @since 1.6 */ @Beta public class Reflection2 { + private static final Pattern GETTER_PATTERN = Pattern.compile("(get|is)([A-Z]+[a-zA-Z0-9]*)"); + private static final Pattern SETTER_PATTERN = Pattern.compile("set[A-Z]+[a-zA-Z0-9]*]"); + /** * gets a {@link TypeToken} for the given type. */ @@ -64,6 +78,16 @@ public static TypeToken typeToken(Type in) { return (TypeToken) get(typeTokenForType, checkNotNull(in, "class")); } + /** + * Checks if type is assignable from another type. + * @param type The type to check if is assignable. + * @param from The target type. + * @return + */ + public static boolean isAssignable(Type type, Type from) { + return get(typeTokenForType,type).isAssignableFrom(from); + } + /** * gets a {@link TypeToken} for the given class. */ @@ -74,12 +98,12 @@ public static TypeToken typeToken(Class in) { /** * returns an {@link Invokable} object that reflects a constructor present in the {@link TypeToken} type. - * + * * @param ownerType * corresponds to {@link Invokable#getOwnerType()} * @param parameterTypes * corresponds to {@link Constructor#getParameterTypes()} - * + * * @throws IllegalArgumentException * if the constructor doesn't exist or a security exception occurred */ @@ -91,7 +115,7 @@ public static Invokable constructor(Class ownerType, Class... pa /** * return all constructors present in the class as {@link Invokable}s. - * + * * @param ownerType * corresponds to {@link Invokable#getOwnerType()} */ @@ -102,7 +126,7 @@ public static Collection> constructors(TypeToken ownerTyp /** * returns an {@link Invokable} object that links the {@code method} to its owner. - * + * * @param ownerType * corresponds to {@link Invokable#getOwnerType()} * @param method @@ -117,14 +141,14 @@ public static Invokable method(TypeToken ownerType, Method metho * returns an {@link Invokable} object that reflects a method present in the {@link TypeToken} type. * If there are multiple methods of the same name and parameter list, returns the method in the nearest * ancestor with the most specific return type (see {@link Class#getDeclaredMethod}). - * + * * @param ownerType * corresponds to {@link Invokable#getOwnerType()} * @param name * name of the method to be returned * @param parameterTypes * corresponds to {@link Method#getParameterTypes()} - * + * * @throws IllegalArgumentException * if the method doesn't exist or a security exception occurred */ @@ -136,7 +160,7 @@ public static Invokable method(Class ownerType, String name, Cla /** * return all methods present in the class as {@link Invokable}s. - * + * * @param ownerType * corresponds to {@link Invokable#getOwnerType()} */ @@ -145,6 +169,163 @@ public static Collection> methods(Class ownerType) { return Collection.class.cast(get(methodsForTypeToken, typeToken(ownerType))); } + + /** + * Return true if {@link Method} is a getter. + * + * @param invokable + * @return + */ + public static boolean isGetter(Invokable invokable) { + String name = invokable.getName(); + TypeToken typeToken = invokable.getReturnType(); + + TypeVariable params[] = invokable.getTypeParameters(); + + if (!GETTER_PATTERN.matcher(name).matches()) { + return false; + } + + // special for isXXX boolean + if (name.startsWith("is")) { + return params.length == 0 && typeToken.getRawType().getSimpleName().equalsIgnoreCase("boolean"); + } + + return params.length == 0 && !typeToken.equals(Void.TYPE); + } + + + /** + * Returns the property name that corresponds to the {@link Method}. + * If method is a getter, it strips the get/is prefix and returns the rest, with the first character to lower case. + * + * @param invokable + * @return + */ + public static String shortNameOf(Invokable invokable) { + if (!isGetter(invokable)) { + return invokable.getName(); + } + String name = invokable.getName(); + Matcher matcher = GETTER_PATTERN.matcher(name); + if (matcher.matches()) { + String propertyName = matcher.group(2); + if (!Strings.isNullOrEmpty(propertyName)) { + return propertyName.substring(0, 1).toLowerCase(Locale.ENGLISH) + propertyName.substring(1); + } + } + return name; + } + + /** + * Returns a getter {@link Invokable} for property. + * + * @param type + * @param property + * @return + */ + public static Optional> getPropertyGetter(Type type, String property) { + final String getMethodName = "get" + property.substring(0, 1).toUpperCase() + property.substring(1); + final String isMethodName = "is" + property.substring(0, 1).toUpperCase() + property.substring(1); + + TypeToken typeToken = TypeToken.of(type); + return Iterables.tryFind(get(methodsForTypeToken, typeToken), new Predicate>() { + @Override + public boolean apply(@Nullable Invokable input) { + return input.getName().equals(getMethodName) || input.getName().equals(isMethodName); + } + }); + } + + /** + * Returns all methods annotated with the specified annotation. + * @param type + * @param annotation + * @return + */ + public static Iterable> methodsAnnotatedWith(Type type, final Class annotation) { + TypeToken typeToken = TypeToken.of(type); + return Iterables.filter(get(methodsForTypeToken, typeToken), new Predicate>() { + @Override + public boolean apply(@Nullable Invokable input) { + return input.isAnnotationPresent(annotation); + } + }); + } + + /** + * Returns all methods annotated with the specified annotation. + * + * @param type + * @param annotation + * @return + */ + public static Iterable fieldsAnnotatedWith(Type type, final Class annotation) { + TypeToken typeToken = TypeToken.of(type); + return Iterables.filter(get(fieldsForTypeToken, typeToken), new Predicate() { + @Override + public boolean apply(@Nullable Field input) { + return input.isAnnotationPresent(annotation); + } + }); + } + + /** + * Returns a getter {@link Invokable} for property. + * @param type + * @param property + * @return + */ + public static Optional getPropertyField(Type type, final String property) { + TypeToken typeToken = TypeToken.of(type); + return Iterables.tryFind(get(fieldsForTypeToken, typeToken), new Predicate() { + @Override + public boolean apply(@Nullable Field input) { + return input.getName().equals(property); + } + }); + } + + /** + * Returns the property value of the target object. + * It first tries to find a getter for the property, then tries to find a field and finally it falls back to finding a + * method that matches the property name. + * @param target + * @param property + * @return + * @throws InvocationTargetException, IllegalAccessException, NoSuchMethodException + */ + public static Object getPropertyValue(Object target, final String property) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { + Optional> getter = getPropertyGetter(target.getClass(), property); + if (getter.isPresent()) { + return ((Invokable) getter.get()).invoke(target); + } + + Optional field = getPropertyField(target.getClass(), property); + if (field.isPresent()) { + return field.get().get(target); + } else { + throw new NoSuchMethodException("No getters or field found for property:" + property + " on " + target.getClass()); + } + } + + + /** + * Returns the type parameter of t. + * @param t + * @return + */ + public static Class typeParameterOf(Type t) { + if (t instanceof ParameterizedType) { + Type[] arguments = ((ParameterizedType) t).getActualTypeArguments(); + return (Class) arguments[0]; + } else { + throw new IllegalArgumentException("Type " + t + " is not parameterized."); + } + } + + + /** * this gets all declared constructors, not just public ones. makes them accessible, as well. */ @@ -297,6 +478,28 @@ public String toString() { } }); + /** + * this gets all declared fields, not just public ones. makes them accessible. Does not include Object methods. + * Fields for a type are ordered so all fields on a subtype are always listed before fields on a + * supertype (see {@link TypeToken#getTypes()}). + */ + private static LoadingCache, Set> fieldsForTypeToken = CacheBuilder + .newBuilder().build(new CacheLoader, Set>() { + public Set load(TypeToken key) { + ImmutableSet.Builder builder = ImmutableSet. builder(); + for (TypeToken token : key.getTypes()) { + Class raw = token.getRawType(); + if (raw == Object.class) + continue; + for (Field field : raw.getDeclaredFields()) { + field.setAccessible(true); + builder.add(field); + } + } + return builder.build(); + } + }); + /** * ensures that exceptions are not doubly-wrapped */ diff --git a/core/src/main/java/org/jclouds/rest/internal/RestContextImpl.java b/core/src/main/java/org/jclouds/rest/internal/RestContextImpl.java index 84085b4ea5..71449df3b8 100644 --- a/core/src/main/java/org/jclouds/rest/internal/RestContextImpl.java +++ b/core/src/main/java/org/jclouds/rest/internal/RestContextImpl.java @@ -22,6 +22,9 @@ import javax.inject.Inject; +import org.jclouds.javax.annotation.Nullable; +import org.jclouds.management.ManagementContext; +import org.jclouds.management.annotations.Management; import org.jclouds.annotations.Name; import org.jclouds.domain.Credentials; import org.jclouds.internal.ContextImpl; @@ -48,9 +51,10 @@ public class RestContextImpl extends ContextImpl implements RestContext creds, Utils utils, Closer closer, Injector injector, TypeLiteral syncApi, + @Provider Supplier creds, @Management ManagementContext managementContext, Utils utils, Closer closer, + Injector injector, TypeLiteral syncApi, TypeLiteral asyncApi) { - super(name, providerMetadata, creds, utils, closer); + super(name, providerMetadata, creds, managementContext, utils, closer); checkNotNull(injector, "injector"); this.asyncApi = injector.getInstance(Key.get(checkNotNull(asyncApi, "asyncApi"))); this.syncApi = injector.getInstance(Key.get(checkNotNull(syncApi, "syncApi"))); diff --git a/core/src/test/java/org/jclouds/ContextBuilderTest.java b/core/src/test/java/org/jclouds/ContextBuilderTest.java index a6a89d8ee3..80aa33bcc1 100644 --- a/core/src/test/java/org/jclouds/ContextBuilderTest.java +++ b/core/src/test/java/org/jclouds/ContextBuilderTest.java @@ -20,6 +20,7 @@ import static com.google.common.base.Suppliers.ofInstance; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; import java.net.URI; import java.util.Arrays; @@ -38,6 +39,7 @@ import org.jclouds.logging.config.LoggingModule; import org.jclouds.logging.config.NullLoggingModule; import org.jclouds.logging.jdk.config.JDKLoggingModule; +import org.jclouds.management.internal.BaseManagementContext; import org.jclouds.providers.AnonymousProviderMetadata; import org.jclouds.providers.ProviderMetadata; import org.jclouds.rest.ConfiguresRestClient; @@ -90,6 +92,16 @@ public void testContextName() { assertEquals(context.getName(), "mytest"); } + @Test + public void testManagementContext() { + ContextBuilder builder = testContextBuilder().endpoint("http://${jclouds.identity}.service.com").name("mytest") + .credentials("foo", "bar"); + Context context = builder.build(); + assertEquals(context, BaseManagementContext.INSTANCE.getContext("mytest")); + context.close(); + assertNull(BaseManagementContext.INSTANCE.getContext("mytest")); + } + @Test public void testProviderMetadataBoundWithCorrectEndpoint() { ContextBuilder withVariablesToReplace = testContextBuilder().endpoint("http://${jclouds.identity}.service.com") diff --git a/core/src/test/java/org/jclouds/config/BindRestContextWithWildcardExtendsExplicitAndRawTypeTest.java b/core/src/test/java/org/jclouds/config/BindRestContextWithWildcardExtendsExplicitAndRawTypeTest.java index 4e47094141..00a26fbe6d 100644 --- a/core/src/test/java/org/jclouds/config/BindRestContextWithWildcardExtendsExplicitAndRawTypeTest.java +++ b/core/src/test/java/org/jclouds/config/BindRestContextWithWildcardExtendsExplicitAndRawTypeTest.java @@ -28,6 +28,7 @@ import org.jclouds.domain.Credentials; import org.jclouds.http.IntegrationTestAsyncClient; import org.jclouds.http.IntegrationTestClient; +import org.jclouds.management.internal.BaseManagementContext; import org.jclouds.providers.AnonymousProviderMetadata; import org.jclouds.providers.ProviderMetadata; import org.jclouds.providers.config.BindProviderMetadataContextAndCredentials; @@ -76,6 +77,7 @@ public void testRawAndExplicit() { private Injector injectorFor(ProviderMetadata md) { return Guice.createInjector( new BindNameToContext("test"), + new BindManagementToContext(BaseManagementContext.INSTANCE), new BindProviderMetadataContextAndCredentials(md, ofInstance(new Credentials("user", "pass"))), new BindRestContextWithWildcardExtendsExplicitAndRawType(RestApiMetadata.class.cast(md .getApiMetadata())), diff --git a/core/src/test/java/org/jclouds/internal/BaseViewTest.java b/core/src/test/java/org/jclouds/internal/BaseViewTest.java index 9b947080a3..9fbb22fa65 100644 --- a/core/src/test/java/org/jclouds/internal/BaseViewTest.java +++ b/core/src/test/java/org/jclouds/internal/BaseViewTest.java @@ -26,6 +26,7 @@ import org.jclouds.domain.Credentials; import org.jclouds.lifecycle.Closer; +import org.jclouds.management.internal.BaseManagementContext; import org.jclouds.providers.ProviderMetadata; import org.jclouds.rest.Utils; import org.testng.annotations.Test; @@ -44,7 +45,7 @@ public class BaseViewTest { private static class Water extends ContextImpl { protected Water() { - super("water", createMock(ProviderMetadata.class), creds, createMock(Utils.class), createMock(Closer.class)); + super("water", createMock(ProviderMetadata.class), creds, BaseManagementContext.INSTANCE, createMock(Utils.class), createMock(Closer.class)); } public void close() { @@ -54,7 +55,7 @@ public void close() { private static class PeanutButter extends ContextImpl { protected PeanutButter() { - super("peanutbutter", createMock(ProviderMetadata.class), creds, createMock(Utils.class), createMock(Closer.class)); + super("peanutbutter", createMock(ProviderMetadata.class), creds, BaseManagementContext.INSTANCE, createMock(Utils.class), createMock(Closer.class)); } public void close() { diff --git a/core/src/test/java/org/jclouds/management/ManagedTypeModelTest.java b/core/src/test/java/org/jclouds/management/ManagedTypeModelTest.java new file mode 100644 index 0000000000..0286b25b51 --- /dev/null +++ b/core/src/test/java/org/jclouds/management/ManagedTypeModelTest.java @@ -0,0 +1,80 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.management; + +import org.jclouds.management.internal.ManagedTypeModel; +import org.testng.annotations.Test; + +import javax.management.openmbean.ArrayType; +import javax.management.openmbean.SimpleType; +import javax.management.openmbean.TabularType; +import java.util.concurrent.ExecutionException; + +import static org.jclouds.reflect.Reflection2.typeParameterOf; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + + +@Test +public class ManagedTypeModelTest { + + @Test + void testDescription() throws ExecutionException { + ManagedTypeModel info = ManagedTypeModel.of(TypeA.class); + assertEquals("The id of type A", info.getDescription("id")); + assertEquals("The name of type A", info.getDescription("name")); + assertEquals("Some description", info.getDescription("description")); + assertEquals("An Optional attribute", info.getDescription("note")); + + assertEquals(Long.class, info.getType("id")); + assertEquals(String.class, info.getType("name")); + assertEquals(String.class, info.getType("description")); + assertEquals(String.class, typeParameterOf(info.getType("note"))); + } + + @Test + void testType() throws ExecutionException { + ManagedTypeModel info = ManagedTypeModel.of(TypeA.class); + assertEquals(Long.class, info.getType("id")); + assertEquals(String.class, info.getType("name")); + assertEquals(String.class, info.getType("description")); + assertEquals(String.class, typeParameterOf(info.getType("note"))); + } + + @Test + void testOpenType() throws ExecutionException { + ManagedTypeModel info = ManagedTypeModel.of(TypeA.class); + assertEquals(SimpleType.LONG, info.getOpenType("id")); + assertEquals(SimpleType.STRING, info.getOpenType("name")); + assertEquals(SimpleType.STRING, info.getOpenType("description")); + assertTrue(ArrayType.class.isAssignableFrom(info.getOpenType("stringSet").getClass())); + assertTrue(TabularType.class.isAssignableFrom(info.getOpenType("typeBSet").getClass())); + } + + + @Test + void testAttributeInheritance() throws ExecutionException { + ManagedTypeModel info = ManagedTypeModel.of(TypeA.class); + assertTrue(info.getNames().contains("parentProperty")); + assertEquals(SimpleType.STRING, info.getOpenType("parentProperty")); + } +} + + diff --git a/core/src/test/java/org/jclouds/management/ParentA.java b/core/src/test/java/org/jclouds/management/ParentA.java new file mode 100644 index 0000000000..b91f2ef88b --- /dev/null +++ b/core/src/test/java/org/jclouds/management/ParentA.java @@ -0,0 +1,30 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.management; + +import org.jclouds.management.annotations.ManagedAttribute; +import org.jclouds.management.annotations.ManagedType; + +@ManagedType +public class ParentA { + + @ManagedAttribute(description = "Parent property") + String parentProperty; +} diff --git a/core/src/test/java/org/jclouds/management/TypeA.java b/core/src/test/java/org/jclouds/management/TypeA.java new file mode 100644 index 0000000000..5469d545b0 --- /dev/null +++ b/core/src/test/java/org/jclouds/management/TypeA.java @@ -0,0 +1,69 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.management; + +import com.google.common.base.Optional; +import com.google.common.collect.Sets; +import org.jclouds.management.annotations.ManagedAttribute; +import org.jclouds.management.annotations.ManagedType; + +import java.util.Set; + +@ManagedType +public class TypeA extends ParentA { + + @ManagedAttribute(description = "The id of type A") + private final Long id; + @ManagedAttribute(description = "The name of type A") + private final String name; + @ManagedAttribute(description = "Some description") + private final String description; + + @ManagedAttribute(description = "An Optional attribute") + private final Optional note = Optional.of("A note."); + + @ManagedAttribute(description = "A collection of B") + private final Set typeBSet = Sets.newHashSet(new TypeB(1L), new TypeB(2L)); + + @ManagedAttribute(description = "Some strings") + private final Set stringSet = Sets.newHashSet("one", "two"); + + public TypeA(Long id, String name, String description) { + this.id = id; + this.name = name; + this.description = description; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public Optional getNote() { + return note; + } +} diff --git a/core/src/test/java/org/jclouds/management/TypeB.java b/core/src/test/java/org/jclouds/management/TypeB.java new file mode 100644 index 0000000000..c5629302b8 --- /dev/null +++ b/core/src/test/java/org/jclouds/management/TypeB.java @@ -0,0 +1,38 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.management; + +import org.jclouds.management.annotations.ManagedAttribute; +import org.jclouds.management.annotations.ManagedType; + +@ManagedType +public class TypeB { + + @ManagedAttribute(description = "The id of type B") + private final Long id; + + public TypeB(Long id) { + this.id = id; + } + + public Long getId() { + return id; + } +} diff --git a/core/src/test/java/org/jclouds/management/TypeC.java b/core/src/test/java/org/jclouds/management/TypeC.java new file mode 100644 index 0000000000..486c216c68 --- /dev/null +++ b/core/src/test/java/org/jclouds/management/TypeC.java @@ -0,0 +1,33 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.management; + +public class TypeC { + + private final Long id; + + public TypeC(Long id) { + this.id = id; + } + + public Long getId() { + return id; + } +} diff --git a/core/src/test/java/org/jclouds/management/functions/ToOpenTypeTest.java b/core/src/test/java/org/jclouds/management/functions/ToOpenTypeTest.java new file mode 100644 index 0000000000..0eed7f2214 --- /dev/null +++ b/core/src/test/java/org/jclouds/management/functions/ToOpenTypeTest.java @@ -0,0 +1,75 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.management.functions; + +import org.jclouds.management.TypeA; +import org.jclouds.management.TypeB; +import org.jclouds.management.TypeC; +import org.testng.annotations.Test; + +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.SimpleType; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Date; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.assertNull; + +@Test +public class ToOpenTypeTest { + + @Test + void testNativeTypes() { + assertEquals(SimpleType.BOOLEAN, ToOpenType.FUNCTION.apply(boolean.class)); + assertEquals(SimpleType.BOOLEAN, ToOpenType.FUNCTION.apply(Boolean.class)); + assertEquals(SimpleType.BYTE, ToOpenType.FUNCTION.apply(byte.class)); + assertEquals(SimpleType.BYTE, ToOpenType.FUNCTION.apply(Byte.class)); + assertEquals(SimpleType.CHARACTER, ToOpenType.FUNCTION.apply(char.class)); + assertEquals(SimpleType.CHARACTER, ToOpenType.FUNCTION.apply(Character.class)); + assertEquals(SimpleType.SHORT, ToOpenType.FUNCTION.apply(short.class)); + assertEquals(SimpleType.SHORT, ToOpenType.FUNCTION.apply(Short.class)); + assertEquals(SimpleType.INTEGER, ToOpenType.FUNCTION.apply(int.class)); + assertEquals(SimpleType.INTEGER, ToOpenType.FUNCTION.apply(Integer.class)); + assertEquals(SimpleType.LONG, ToOpenType.FUNCTION.apply(long.class)); + assertEquals(SimpleType.LONG, ToOpenType.FUNCTION.apply(Long.class)); + assertEquals(SimpleType.DOUBLE, ToOpenType.FUNCTION.apply(double.class)); + assertEquals(SimpleType.DOUBLE, ToOpenType.FUNCTION.apply(Double.class)); + assertEquals(SimpleType.FLOAT, ToOpenType.FUNCTION.apply(float.class)); + assertEquals(SimpleType.FLOAT, ToOpenType.FUNCTION.apply(Float.class)); + assertEquals(SimpleType.BIGDECIMAL, ToOpenType.FUNCTION.apply(BigDecimal.class)); + assertEquals(SimpleType.BIGINTEGER, ToOpenType.FUNCTION.apply(BigInteger.class)); + assertEquals(SimpleType.DATE, ToOpenType.FUNCTION.apply(Date.class)); + assertEquals(SimpleType.STRING, ToOpenType.FUNCTION.apply(String.class)); + } + + @Test + void testManagedAttributes() { + assertTrue(ToOpenType.FUNCTION.apply(TypeA.class) instanceof CompositeType); + assertTrue(ToOpenType.FUNCTION.apply(TypeB.class) instanceof CompositeType); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + void testIllegalTypes() { + assertNull(ToOpenType.FUNCTION.apply(TypeC.class)); + } +} diff --git a/core/src/test/java/org/jclouds/reflect/Reflection2Test.java b/core/src/test/java/org/jclouds/reflect/Reflection2Test.java index b1087cc666..dca1ec8c83 100644 --- a/core/src/test/java/org/jclouds/reflect/Reflection2Test.java +++ b/core/src/test/java/org/jclouds/reflect/Reflection2Test.java @@ -20,15 +20,26 @@ import static com.google.common.base.Functions.toStringFunction; import static org.jclouds.reflect.Reflection2.constructors; +import static org.jclouds.reflect.Reflection2.isGetter; import static org.jclouds.reflect.Reflection2.method; import static org.jclouds.reflect.Reflection2.methods; +import static org.jclouds.reflect.Reflection2.methodsAnnotatedWith; +import static org.jclouds.reflect.Reflection2.typeParameterOf; import static org.jclouds.reflect.Reflection2.typeToken; +import static org.jclouds.reflect.Reflection2.getPropertyGetter; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; import java.util.HashSet; +import java.util.List; import java.util.Set; import java.util.SortedSet; +import com.google.common.base.Optional; +import com.google.common.collect.Iterables; +import org.jclouds.domain.Location; +import org.jclouds.management.annotations.ManagedAttribute; import org.testng.annotations.Test; import com.google.common.base.Function; @@ -113,6 +124,29 @@ public void testMethodsSubClass() { .addAll(setMethods).build()); } + public void testIsGetter() { + assertTrue(isGetter(method(Location.class, "getId"))); + assertTrue(isGetter(method(Location.class, "getScope"))); + assertTrue(isGetter(method(Location.class, "getParent"))); + } + + public void testPropertyGetter() { + assertTrue(getPropertyGetter(Location.class, "id").isPresent()); + assertTrue(getPropertyGetter(Location.class, "scope").isPresent()); + assertTrue(getPropertyGetter(Location.class, "parent").isPresent()); + assertFalse(getPropertyGetter(Location.class,"notfound").isPresent()); + } + + public void testMethodsAnnotatedWith() { + assertFalse(Iterables.isEmpty(methodsAnnotatedWith(Location.class, ManagedAttribute.class))); + } + + public void testTypeParameterOf() { + assertEquals(String.class, typeParameterOf(new TypeToken>(){}.getType())); + assertEquals(String.class, typeParameterOf(new TypeToken>(){}.getType())); + assertEquals(String.class, typeParameterOf(new TypeToken>(){}.getType())); + } + static final Function, String> invokableToName = new Function, String>() { public String apply(Invokable input) { return input.getName();