/* * RHQ Management Platform * Copyright (C) 2005-2008 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, as * published by the Free Software Foundation, and/or the GNU Lesser * General Public License, version 2.1, also as published by the Free * Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License and the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.rhq.core.domain.shared; import org.rhq.core.domain.measurement.AvailabilityType; import org.rhq.core.domain.measurement.ResourceAvailability; import org.rhq.core.domain.resource.InventoryStatus; import org.rhq.core.domain.resource.Resource; import org.rhq.core.domain.resource.ResourceCategory; import org.rhq.core.domain.resource.ResourceType; import java.math.BigInteger; import java.util.ArrayList; import java.util.List; import java.util.Random; /** * ResoureBuilder is a builder object that creates Resource objects. The builder ensures that a Resource is created * in a valid state, specifically, fields that are not nullable are required to have non-null values. Using a builder * should help make the intent of tests clearer and more self-documenting. * <br/><br/> * Note that this class currently does not yet provide support for all Resource fields/properties. * * @author John Sanda */ public class ResourceBuilder { private ResourceBuilder parentBuilder; private Resource resource; private Random random; private boolean useDefaultResourceType; private ResourceCategory category; private List<ResourceBuilder> childBuilders = new ArrayList<ResourceBuilder>(); public static class AssociationBuilder { private ResourceBuilder resourceBuilder; private int count; AssociationBuilder(ResourceBuilder builder, int count) { resourceBuilder = builder; this.count = count; } public ResourceBuilder randomChildServers() { for (int i = 0; i < count; ++i) { ResourceBuilder childBuilder = new ResourceBuilder(ResourceCategory.SERVER, resourceBuilder); resourceBuilder.childBuilders.add(childBuilder.createRandomServer()); } return resourceBuilder; } public ResourceBuilder randomChildServices() { for (int i = 0; i < count; ++i) { ResourceBuilder childBuilder = new ResourceBuilder(ResourceCategory.SERVICE, resourceBuilder); resourceBuilder.childBuilders.add(childBuilder.createRandomService()); } return resourceBuilder; } } public static class ChildrenResourceBuilder { private ResourceBuilder parentBuilder; ChildrenResourceBuilder(ResourceBuilder builder, ResourceCategory category, int numChildren) { parentBuilder = builder; parentBuilder.childBuilders = new ArrayList<ResourceBuilder>(numChildren); for (int i = 0; i < numChildren; ++i) { parentBuilder.childBuilders.add(new ResourceBuilder(category, parentBuilder).createResource()); } } public ChildrenResourceBuilder inInventory() { for (ResourceBuilder childBuilder : parentBuilder.childBuilders) { childBuilder.inInventory(); } return this; } public ChildrenResourceBuilder notInInventory() { for (ResourceBuilder childBuilder : parentBuilder.childBuilders) { childBuilder.notInInventory(); } return this; } public ResourceBuilder included() { return parentBuilder; } } public ResourceBuilder() { } private ResourceBuilder(ResourceCategory category, ResourceBuilder parentBuilder) { this.category = category; this.parentBuilder = parentBuilder; } public ResourceBuilder createResource() { resource = new Resource(); random = new Random(); return this; } public ResourceBuilder createPlatform() { category = ResourceCategory.PLATFORM; return createResource(); } public ResourceBuilder createServer() { category = ResourceCategory.SERVER; return createResource(); } public ResourceBuilder createRandomServer() { category = ResourceCategory.SERVER; createResource(); withRandomId(); withRandomName("server:"); withRandomResourceKey("server:"); withRandomUuid("server:"); withDefaultServerResourceType(); return this; } public ResourceBuilder createService() { category = ResourceCategory.SERVICE; return createResource(); } public ResourceBuilder createRandomService() { category = ResourceCategory.SERVICE; createResource(); withRandomId(); withRandomName("service:"); withRandomResourceKey("service:"); withRandomUuid("service:"); withDefaultServiceResourceType(); return this; } /** * Using a default resource type results in <code>Resource.resourceType</code> being assigned to a new * ResourceType object that has some default values applied to it. If the Resource being created is a platform, then * the ResourceType will be a platform. More specifically, <code>ResourceType.category</code> will be assigned a * value of {@link ResourceCategory#PLATFORM}. Likewise, if the Resource being created is a server, then its * ResourceType object will have a category of {@link ResourceCategory#SERVER}. And if the Resource is a service, * then the ResourceType category will be {@link ResourceCategory#SERVICE}. * <br/><br/> * The resource type name defaults to the name of resource. And the plugin name (as specified by * ResourceType.plugin) defaults to <code>Resource.name + " Plugin"</code> * <br/><br/> * When using a default resource type, the resource must be created using one of {@link #createPlatform()}, * {@link #createServer()}, or {@link #createService()}; otherwise, an exception will be thrown since the builder * will not have sufficient information to create the resource type. * <br/><br/> * Lastly, if you specify that a default resource type by calling this method and also specify the resource tye * with {@link #withResourceType(org.rhq.core.domain.resource.ResourceType)}, the latter will be overwritten regardless of when it is called. The * default will be used instead. * * @return The builder * */ public ResourceBuilder usingDefaultResourceType() { useDefaultResourceType = true; return this; } public ResourceBuilder withId(int id) { resource.setId(id); return this; } public ResourceBuilder withRandomId() { resource.setId(random.nextInt()); return this; } public ResourceBuilder withResourceKey(String key) { resource.setResourceKey(key); return this; } public ResourceBuilder withRandomResourceKey(String prefix) { resource.setResourceKey(prefix + randomString()); return this; } public ResourceBuilder withRandomResourceKey() { return withRandomResourceKey(""); } public ResourceBuilder withName(String name) { resource.setName(name); return this; } public ResourceBuilder withRandomName(String prefix) { resource.setName(prefix + randomString()); return this; } public ResourceBuilder withRandomName() { return withRandomName(""); } public ResourceBuilder withResourceType(ResourceType resourceType) { resource.setResourceType(resourceType); return this; } public ResourceBuilder withUuid(String uuid) { resource.setUuid(uuid); return this; } public ResourceBuilder withRandomUuid(String prefix) { resource.setUuid(prefix + randomString()); return this; } public ResourceBuilder withRandomUuid() { return withRandomUuid(""); } public ResourceBuilder withVersion(String version) { resource.setVersion(version); return this; } public ResourceBuilder withCurrentAvailability(AvailabilityType availabilityType) { ResourceAvailability availability = new ResourceAvailability(resource, availabilityType); resource.setCurrentAvailability(availability); return this; } public ResourceBuilder withInventoryStatus(InventoryStatus inventoryStatus) { resource.setInventoryStatus(inventoryStatus); return this; } /** * Set the <code>inventoryStatus</code> to {@link InventoryStatus#COMMITTED} * * @return The builder */ public ResourceBuilder inInventory() { resource.setInventoryStatus(InventoryStatus.COMMITTED); return this; } /** * Set the <code>inventoryStatus</code> to {@link InventoryStatus#NEW} * * @return The builder */ public ResourceBuilder notInInventory() { resource.setInventoryStatus(InventoryStatus.NEW); return this; } public AssociationBuilder with(int count) { return new AssociationBuilder(this, count); } public ResourceBuilder withChildService() { ResourceBuilder childBuilder = new ResourceBuilder(ResourceCategory.SERVICE, this); childBuilders.add(childBuilder.createService()); return childBuilder; } public ResourceBuilder included() { return this.parentBuilder; } public Resource build() { String errors = validate(); if (errors != null) { throw new BuilderException(errors); } if (useDefaultResourceType) { withDefaultResourceType(); } for (ResourceBuilder childBuilder : childBuilders) { resource.addChildResource(childBuilder.build()); } return resource; } private String validate() { StringBuilder errors = new StringBuilder(); if (resource.getUuid() == null) { // Making uuid required since it is used in equals/hashCode errors.append("uuid is a required property\n"); } if (resource.getName() == null) { errors.append("name is a required property\n"); } if (useDefaultResourceType && category == null) { errors.append("When using default resource type, the resource must be created with one of " + "createPlatform(), createServer(), or createService()\n"); } // We only care that resourceType is set if we are not using a default type. If we are using a default, // then the resourceType property will be set after validation, assuming there are no validation errors. if (!useDefaultResourceType && resource.getResourceType() == null) { errors.append("resourceType is a required property\n"); } for (ResourceBuilder childBuilder : childBuilders) { String childErrors = childBuilder.validate(); if (childErrors != null) { errors.append("The following child resource errors were found:\n" + childErrors); } } if (errors.length() == 0) { return null; } return "Unable to build Resource instance due to the following validation errors:\n" + errors; } private ResourceBuilder withDefaultResourceType() { switch (category) { case PLATFORM: return withDefaultPlatformResourceType(); case SERVER: return withDefaultServerResourceType(); default: return withDefaultServiceResourceType(); } } /** * The default platform resource type is as its name implies a platform type whose name defaults to the resource * name and the plugin name (i.e., ResourceType.plugin property) defaults to resource name + 'Plugin'. * * @return The builder */ private ResourceBuilder withDefaultPlatformResourceType() { resource.setResourceType(new ResourceTypeBuilder().createPlatformResourceType() .withName(resource.getName()) .withPlugin(resource.getName() + " Plugin") .withParentResourceType(resource.getResourceType()) .build()); return this; } /** * The default server resource type is as its name implies a server type whose name defaults to the resource name * and the plugin name (i.e., ResourceType.plugin property) defaults to resource name + 'Plugin' * * @return The builder */ private ResourceBuilder withDefaultServerResourceType() { resource.setResourceType(new ResourceTypeBuilder().createServerResourceType() .withName(resource.getName()) .withPlugin(resource.getName() + " Plugin") .withParentResourceType(resource.getResourceType()) .build()); return this; } /** * The default service resource type is as its name implies a service type whose name defaults to the resource name * and the plugin name (i.e., ResourceType.plugin property) defaults to resource name + 'Plugin' * * @return The builder */ private ResourceBuilder withDefaultServiceResourceType() { resource.setResourceType(new ResourceTypeBuilder().createServerResourceType() .withName(resource.getName()) .withPlugin(resource.getName() + " Plugin") .withParentResourceType(resource.getResourceType()) .build()); return this; } private String randomString() { return new BigInteger(16, random).toString(32); } }