func (self *metadataInstance) opPut(operation *operations.Put, state *minos.OperationState) (bucketReturn ares.Return) { key, status := extractMetadataKey(operation.Key) if !status.IsOk() { return &bucket.ReturnGeneric{status} } status = assertNotConst(key) if !status.IsOk() { return &bucket.ReturnGeneric{status} } var valueToSet []byte var existingValue []byte var optimisticLocking bool numberOfValueElements := len(operation.Value) switch numberOfValueElements { case 0: optimisticLocking = false valueToSet = nil case 1: optimisticLocking = false valueToSet = operation.Value[0] case 2: optimisticLocking = true valueToSet = operation.Value[0] existingValue = operation.Value[1] default: status = retcode.NewStatusFmt(retcode.ErrorClient, "Expecting 0 value elements (remove), one "+ "value element (the new value to set) or two value elements (the value to set and the existing "+ "Value for optimistic locking. You've supplied %v value elements.", numberOfValueElements) return &bucket.ReturnGeneric{status} } singleValueGetterSetter := metadata.SingleValueGetterSetter{ Key: key, Value: valueToSet, } systemKey := isSystemKey(key) var validationError error if systemKey { // System validate validationError = validateSystemChange(singleValueGetterSetter) } else { // Let the bucket type validate the change bucketType := state.BucketType(state.BucketId.TypeId()) if bucketType == nil { validationError = errors.New(fmt.Sprintf("The bucket type %v is not "+ "known to the system. Cannot perform metadata change.", state.BucketId.TypeId())) } validationError = bucketType.ValidateMetadata(singleValueGetterSetter) } // Ok to perform the change? if validationError != nil { status = retcode.NewStatusFmt(retcode.ErrorClient, "The requested change of key %v was rejected, reason: %v", key, validationError) return &bucket.ReturnGeneric{status} } // Lock while performing change metadataId, err := state.BucketId.ConvertToMetadataId() if err != nil { status = retcode.NewStatusFmt(retcode.ErrorServer, "Unable to convert bucket ID to metadata ID: %v", err) return &bucket.ReturnGeneric{status} } state.Locker.BucketIdWriteLock(metadataId) defer state.Locker.BucketIdWriteUnlock(metadataId) // Check current value if optimistic locking is enabled if optimisticLocking { currentValue, err := state.MetadataGetterSetter.Get(key) if err != nil { status = retcode.NewStatusFmt(retcode.ErrorServer, "Error getting current metadata value for optimistic locking: %v", err) return &bucket.ReturnGeneric{status} } if bytes.Compare(currentValue, existingValue) != 0 { status = retcode.NewStatusFmt(retcode.ErrorOptimisticLocking, "Optimistic locking failure: The supplied 'existing' value is no longer "+ "the current value. Refusing to perform the change.") return &bucket.ReturnGeneric{status} } } // Everything is OK, now perform the change err = state.MetadataGetterSetter.Set(key, valueToSet) if err != nil { status = retcode.NewStatusFmt(retcode.ErrorServer, "Could not write metadata: %v", err) return &bucket.ReturnGeneric{status} } return &operations.PutReturn{ Status: retcode.NewStatusOk(), } }
func (self *dispatcherimpl) perform_BucketOperation(context *ares.Context, operation ares.Operation, targetBucketId bucket.Id) ( ret ares.Return) { bucketId := targetBucketId if bucketId == nil || len(bucketId) == 0 { return &operations.GenericReturn{retcode.NewStatusError(retcode.ErrorClient, errors.New("Bucket ID is missing or invalid"))} } bucketType := self.typesRegistry.BucketType(bucketId.TypeId()) if bucketType == nil { return &operations.GenericReturn{retcode.NewStatusError(retcode.ErrorClient, errors.New(fmt.Sprintf("BucketType %v not found", bucketId.TypeId())))} } var err error // Special case for metadata if bucketId.IsMetadata() { // Extract and supply the original bucket ID to the metadata bucketId = bucketId.ExtractOriginalId() } metadataReader, err := self.metadata.TypedGetterSetter(bucketId) if err != nil { return &operations.GenericReturn{ retcode.NewStatusError(retcode.ErrorBucketIdNotFound, err)} } state := new(minos.OperationState) state.Operation = operation state.BucketId = bucketId putfowarderVar := new(putfowarder) putfowarderVar.metadataReader = metadataReader putfowarderVar.putReceiversIds, err = metadataReader.PutReceivers() putfowarderVar.context = *context putfowarderVar.forwardFunction = self.Perform state.PutForwarder = putfowarderVar state.MetadataGetterSetter = metadataReader state.NotificationReceiver = self.receiveNotification state.Context = context state.Dispatcher = self state.Locker = self.lock state.Files = self.files state.BucketType = self.typesRegistry.BucketType bucketReturn := bucketType.Perform(state) putfowarderVar.maybeDoPutForward(bucketReturn) return bucketReturn }