public <T> T removeAttribute(final TypedKey<T> key) {
   return allocationContext.remove(key);
 }
 public <T> T getAttribute(final TypedKey<T> key) {
   return allocationContext.get(key);
 }
 public <T> T setAttribute(final TypedKey<T> key, final T value) {
   return allocationContext.put(key, value);
 }
  public static class Allocation implements HasRequest {
    /** to be eliminated * */
    private final Context context;

    private final RunInstancesType request;
    /** values determined by the request * */
    private final UserFullName ownerFullName;

    private byte[] userData;
    private String credential;
    private String rootDirective;
    private final int minCount;
    private final int maxCount;
    private boolean usePrivateAddressing;
    private boolean disableApiTermination;
    private final boolean monitoring;
    @Nullable private final String clientToken;

    /** verified references determined by the request * */
    private Partition partition;

    private SshKeyPair sshKeyPair;
    private BootableSet bootSet;
    private VmType vmType;
    private Subnet subnet;
    private NetworkGroup primaryNetwork;
    private Map<String, NetworkGroup> networkGroups = Maps.newHashMap();
    private String iamInstanceProfileArn;
    private String iamInstanceProfileId;
    private String iamRoleArn;

    /** intermediate allocation state * */
    private final String reservationId;

    private final List<ResourceToken> allocationTokens = Lists.newArrayList();
    private final Long reservationIndex;
    private final Map<Integer, String> instanceIds;
    private final Map<Integer, String> instanceUuids;
    private Date expiration;

    /** */
    private final TypedContext allocationContext = TypedContext.newTypedContext();

    private final AtomicBoolean committed = new AtomicBoolean(false);

    private Allocation(final RunInstancesType request) {
      this.context = Contexts.lookup();
      this.instanceIds = Maps.newHashMap();
      this.instanceUuids = Maps.newHashMap();
      this.request = request;
      this.minCount = request.getMinCount();
      this.maxCount = request.getMaxCount();
      this.usePrivateAddressing = "private".equals(request.getAddressingType());
      this.disableApiTermination =
          Optional.fromNullable(request.getDisableTerminate()).or(Boolean.FALSE);
      this.monitoring = request.getMonitoring() == null ? Boolean.FALSE : request.getMonitoring();
      this.clientToken = Strings.emptyToNull(request.getClientToken());

      this.ownerFullName = this.context.getUserFullName();
      if ((this.request.getInstanceType() == null) || "".equals(this.request.getInstanceType())) {
        this.request.setInstanceType(VmTypes.defaultTypeName());
      }

      this.reservationIndex = UniqueIds.nextIndex(VmInstance.class, (long) request.getMaxCount());
      this.reservationId = IdentityIdFormats.generate(getAuthenticatedArn(), "r");
      this.request.setMonitoring(this.monitoring);
      // GRZE:FIXME: moved all this encode/decode junk into util.UserDatas
      if (this.request.getUserData() != null) {
        try {
          this.userData = Base64.decode(this.request.getUserData());
          this.request.setUserData(new String(Base64.encode(this.userData)));
        } catch (Exception e) {
        }
      } else {
        try {
          this.request.setUserData(new String(Base64.encode(new byte[0])));
        } catch (Exception ex) {
          LOG.error(ex, ex);
        }
      }
      this.credential = null;
    }

    /** Constructor for restore case. */
    private Allocation(
        final String reservationId,
        final String instanceId,
        final String instanceUuid,
        final int launchIndex,
        final boolean usePrivateAddressing,
        final VmType vmType,
        final BootableSet bootSet,
        final Partition partition,
        final SshKeyPair sshKeyPair,
        final byte[] userData,
        final UserFullName ownerFullName) {
      this.minCount = 1;
      this.maxCount = 1;
      this.vmType = vmType;
      this.bootSet = bootSet;
      this.partition = partition;
      this.sshKeyPair = sshKeyPair;
      this.userData = userData;
      this.ownerFullName = ownerFullName;
      this.reservationId = reservationId;
      this.reservationIndex = -1l;
      this.instanceIds = Maps.newHashMap();
      this.instanceIds.put(launchIndex, instanceId);
      this.instanceUuids = Maps.newHashMap();
      this.instanceUuids.put(launchIndex, instanceUuid);
      this.allocationTokens.add(new ResourceToken(this, launchIndex));
      this.context = null;
      this.monitoring = false;
      this.usePrivateAddressing = usePrivateAddressing;
      this.clientToken = null;
      this.request = inferRequest();
    }

    private Allocation(
        final String reservationId,
        final String instanceId,
        final String instanceUuid,
        final byte[] userData,
        final Date expiration,
        final Partition partition,
        final SshKeyPair sshKeyPair,
        final BootableSet bootSet,
        final VmType vmType,
        final Set<NetworkGroup> networkGroups,
        final boolean isUsePrivateAddressing,
        final boolean monitoring,
        final String clientToken,
        final String iamInstanceProfileArn,
        final String iamInstanceProfileId,
        final String iamRoleArn) {
      this.context = Contexts.lookup();
      this.minCount = 1;
      this.maxCount = 1;
      this.usePrivateAddressing = isUsePrivateAddressing;
      this.ownerFullName = context.getUserFullName();
      this.reservationId = reservationId;
      this.reservationIndex = UniqueIds.nextIndex(VmInstance.class, (long) this.maxCount);
      this.instanceIds = Maps.newHashMap();
      this.instanceIds.put(0, instanceId);
      this.instanceUuids = Maps.newHashMap();
      this.instanceUuids.put(0, instanceUuid);
      this.userData = userData;
      this.partition = partition;
      this.sshKeyPair = (sshKeyPair != null ? sshKeyPair : KeyPairs.noKey());
      this.bootSet = bootSet;
      this.expiration = expiration;
      this.vmType = vmType;
      this.monitoring = monitoring;
      this.clientToken = clientToken;
      this.iamInstanceProfileArn = iamInstanceProfileArn;
      this.iamInstanceProfileId = iamInstanceProfileId;
      this.iamRoleArn = iamRoleArn;
      this.credential = null;

      this.networkGroups =
          new HashMap<String, NetworkGroup>() {
            {
              for (NetworkGroup g : networkGroups) {
                if (Allocation.this.primaryNetwork == null) {
                  Allocation.this.primaryNetwork = g;
                }
                put(g.getDisplayName(), g);
              }
            }
          };
      this.request = inferRequest();
    }

    private RunInstancesType inferRequest() {
      return new RunInstancesType() {
        {
          this.setMinCount(1);
          this.setMaxCount(1);
          this.setImageId(bootSet.getMachine().getDisplayName());
          this.setAvailabilityZone(partition.getName());
          this.setInstanceType(vmType.getName());
        }
      };
    }

    @Override
    public RunInstancesType getRequest() {
      return getRunInstancesRequest();
    }

    public RunInstancesType getRunInstancesRequest() {
      return this.request;
    }

    public NetworkGroup getPrimaryNetwork() {
      return this.primaryNetwork;
    }

    public void commit() throws Exception {
      if (!committed.get())
        try {
          for (final ResourceToken t : this.getAllocationTokens()) {
            VmInstances.Create.INSTANCE.apply(t);
          }
          committed.set(true);
        } catch (final Exception ex) {
          this.abort();
          throw ex;
        }
    }

    public boolean isCommitted() {
      return committed.get();
    }

    public void abort() {
      for (final ResourceToken token : this.allocationTokens) {
        LOG.warn("Aborting resource token: " + token);
        Logs.exhaust().error("Aborting resource token", new RuntimeException());
        final EntityTransaction db = Entities.get(VmInstance.class);
        try {
          token.abort();
          db.commit();
        } catch (final Exception ex) {
          LOG.warn(ex.getMessage());
          Logs.exhaust().error(ex, ex);
          db.rollback();
        }
      }
    }

    public List<NetworkGroup> getNetworkGroups() {
      return Lists.newArrayList(this.networkGroups.values());
    }

    public VmType getVmType() {
      return this.vmType;
    }

    public Partition getPartition() {
      return this.partition;
    }

    public void setBootableSet(final BootableSet bootSet) {
      this.bootSet = bootSet;
    }

    public void setVmType(final VmType vmType) {
      this.vmType = vmType;
    }

    public Subnet getSubnet() {
      return subnet;
    }

    public void setSubnet(final Subnet subnet) {
      this.subnet = subnet;
    }

    public UserFullName getOwnerFullName() {
      return this.ownerFullName;
    }

    public String getAuthenticatedArn() {
      return Accounts.getAuthenticatedArn(context.getUser());
    }

    public List<ResourceToken> getAllocationTokens() {
      return this.allocationTokens;
    }

    public void setUserData(final byte[] userData) {
      this.userData = userData;
    }

    public byte[] getUserData() {
      return this.userData;
    }

    public void setCredential(final String credential) {
      this.credential = credential;
    }

    public String getCredential() {
      return this.credential;
    }

    public void setRootDirective() {
      if (!bootSet.isBlockStorage()) {
        try {
          final MachineImageInfo emi = LookupMachine.INSTANCE.apply(this.getRequest().getImageId());
          rootDirective = emi.getRootDirective();
        } catch (Exception ex) {
        }
      }
    }

    public String getRootDirective() {
      return this.rootDirective;
    }

    public Long getReservationIndex() {
      return this.reservationIndex;
    }

    public String getReservationId() {
      return this.reservationId;
    }

    public BootableSet getBootSet() {
      return this.bootSet;
    }

    @Deprecated
    public Context getContext() {
      return this.context;
    }

    public void setPartition(final Partition partition2) {
      this.partition = partition2;
    }

    public SshKeyPair getSshKeyPair() {
      return this.sshKeyPair;
    }

    public void setSshKeyPair(final SshKeyPair sshKeyPair) {
      this.sshKeyPair = sshKeyPair;
    }

    public void setNetworkRules(final Map<String, NetworkGroup> networkRuleGroups) {
      final Entry<String, NetworkGroup> ent = networkRuleGroups.entrySet().iterator().next();
      this.primaryNetwork = ent.getValue();
      this.networkGroups = networkRuleGroups;
    }

    public VmTypeInfo getVmTypeInfo(final Partition partition, final String reservationId)
        throws MetadataException {
      return this.bootSet.populateVirtualBootRecord(this.vmType, partition, reservationId);
    }

    @Nullable
    public String getIamInstanceProfileArn() {
      return iamInstanceProfileArn;
    }

    public void setInstanceProfileArn(final String instanceProfileArn) {
      this.iamInstanceProfileArn = instanceProfileArn;
    }

    public String getIamInstanceProfileId() {
      return iamInstanceProfileId;
    }

    public void setIamInstanceProfileId(final String iamInstanceProfileId) {
      this.iamInstanceProfileId = iamInstanceProfileId;
    }

    public String getIamRoleArn() {
      return iamRoleArn;
    }

    public void setIamRoleArn(final String iamRoleArn) {
      this.iamRoleArn = iamRoleArn;
    }

    public int getMinCount() {
      return this.minCount;
    }

    public int getMaxCount() {
      return this.maxCount;
    }

    public boolean isUsePrivateAddressing() {
      return usePrivateAddressing;
    }

    public void setUsePrivateAddressing(final boolean usePrivateAddressing) {
      this.usePrivateAddressing = usePrivateAddressing;
    }

    public boolean isDisableApiTermination() {
      return disableApiTermination;
    }

    public void setDisableApiTermination(final boolean disableApiTermination) {
      this.disableApiTermination = disableApiTermination;
    }

    public final boolean isMonitoring() {
      return monitoring;
    }

    @Nullable
    public String getClientToken() {
      return clientToken;
    }

    @Nullable
    public String getUniqueClientToken(@Nonnull final Integer launchIndex) {
      return clientToken == null
          ? null
          : String.format(
              "%s:%d:%s", getOwnerFullName().getAccountNumber(), launchIndex, clientToken);
    }

    public Set<String> getInstanceIds() {
      return Sets.newHashSet(this.instanceIds.values());
    }

    public String getInstanceId(int index) {
      if (!this.instanceIds.containsKey(index)) {
        this.instanceIds.put(
            index, VmInstances.getId(Accounts.getAuthenticatedArn(context.getUser())));
      }
      return this.instanceIds.get(index);
    }

    public String getInstanceUuid(int index) {
      if (!this.instanceUuids.containsKey(index)) {
        this.instanceUuids.put(index, UUID.randomUUID().toString());
      }
      return this.instanceUuids.get(index);
    }

    public Date getExpiration() {
      return this.expiration;
    }

    public void setExpiration(final Date expiration) {
      this.expiration = expiration;
    }

    public <T> T getAttribute(final TypedKey<T> key) {
      return allocationContext.get(key);
    }

    public <T> T setAttribute(final TypedKey<T> key, final T value) {
      return allocationContext.put(key, value);
    }

    public <T> T removeAttribute(final TypedKey<T> key) {
      return allocationContext.remove(key);
    }

    public AuthContextSupplier getAuthContext() throws AuthException {
      return context != null
          ? context.getAuthContext()
          : Permissions.createAuthContextSupplier(
              Accounts.lookupPrincipalByUserId(getOwnerFullName().getUserId()),
              Collections.<String, String>emptyMap());
    }
  }