All Downloads are FREE. Search and download functionalities are using the official Maven repository.

example.group.EmployeeService Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2016 VMware, Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License.  You may obtain a copy of
 * the License at http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed
 * under the License is distributed on an "AS IS" BASIS, without warranties or
 * conditions of any kind, EITHER EXPRESS OR IMPLIED.  See the License for the
 * specific language governing permissions and limitations under the License.
 */

package example.group;

import com.vmware.xenon.common.Operation;
import com.vmware.xenon.common.ServiceDocument;
import com.vmware.xenon.common.ServiceDocumentDescription.PropertyUsageOption;
import com.vmware.xenon.common.StatefulService;
import com.vmware.xenon.common.Utils;

/**
 * Provides an implementation of highly scalable, highly available employee record service. Since most of the mechanisms
 * are provided natively by Xenon, this is a very simple class.
 *
 */

public class EmployeeService extends StatefulService {
    // FACTORY_LINK is a special variable that Xenon looks for to know where to host your REST API
    public static final String FACTORY_LINK = "/quickstart/employees";

    public static class Employee extends ServiceDocument {
        // must call Utils.mergeWithState to leverage this
        @UsageOption(option = PropertyUsageOption.AUTO_MERGE_IF_NOT_NULL)
        // must call Utils.validateState to leverage this
        @UsageOption(option = PropertyUsageOption.REQUIRED)
        public String name;

        @UsageOption(option = PropertyUsageOption.AUTO_MERGE_IF_NOT_NULL)
        @UsageOption(option = PropertyUsageOption.LINK)
        public String managerLink;
    }

    public EmployeeService() {
        super(Employee.class);

        // Employee records will indexed, searchable and versioned. Also service can be paused under
        // memory pressure, and resumed on demand. If you disable, you can now longer discover via query task.
        toggleOption(ServiceOption.PERSISTENCE, true);

        // In multi-node, each employee record will be replicated to all nodes (see docs for limited 3x replication)
        toggleOption(ServiceOption.REPLICATION, true);

        // statistics will be collected on API calls to the service
        toggleOption(ServiceOption.INSTRUMENTATION, true);

        // in multi-node, for each employee, one node will be elected leader and all GET/PUT/POST/PATCH/DELETE
        // calls will be routed to that node. That leader node will first commit writes and then replicate to other
        // nodes in the cluster. Thus an individual thread will see "read your writes" consistency,
        // and cluster-wide, there will be a consistent view of a service across nodes.
        toggleOption(ServiceOption.OWNER_SELECTION, true);
    }

    /**
     * handleCreate() is called when a service is first created. This happens on POST to the factory.
     * When xenon is shut down and restarted, this method will NOT be called. If you want a method that will be called
     * whenever a service is started (both on creation and when the service is restarted), then override
     * handleStart(). Note that we only need to overide handleCreate() because we want to do some verification on
     * the proposed document body before we persist it. If you don't want to validate any inputs, you can
     * exclude this method.
     * @param startPost - use this to fetch the state and body as necessary and call complete() when done
     */
    @Override
    public void handleCreate(Operation startPost) {
        Employee s = startPost.getBody(Employee.class);
        Utils.validateState(getStateDescription(), s);    // Checks for REQUIRED fields, can throw exception.

        // When you call complete(), not only will the object be created, but the REST POST operation will
        // return to the sender.
        startPost.complete();

        // You can still do processing after the complete - very common in an async world.
        // You can also not call complete() this function but rather call complete() at some later time. The REST
        // caller will be blocked until complete() is called.
    }

    /**
     * With PUT, the old version is discarded and replaced with the new complete object.
     * PUT *cannot* be used to create services - it can only be used to update an already running service.
     * @see 
     * programming model. Note that we only override the default here so that we can call validateState(),
     * otherwise, no state will be validated.
     * @param put - use this to fetch the state and body as necessary and call complete() when done
     */
    @Override
    public void handlePut(Operation put) {
        Employee newState = getBody(put);
        Utils.validateState(getStateDescription(), newState);    // Checks for REQUIRED fields, can throw exception.
        setState(put, newState);
        put.complete();
    }

    /**
     * The HTTP PATCH is used to update a subset of fields on the service/document. The PATCH request specifies
     * just the fields that need to be updated. The rest of the fields are left unchanged. PATCH is commonly
     * used with Xenon services talking to each other - in particular, it is used to update the state of a task
     * (most tasks in Xenon are state-machineb based). However it is efficient and convenient for all updates.
     * @param patch - use this to fetch the state and body as necessary and call complete() when done
     */
    @Override
    public void handlePatch(Operation patch) {
        Employee state = getState(patch);
        Employee patchBody = getBody(patch);

        /*
          Looks for differences between the 'body' object (the new version) and the 'state' object (old version).
          the object already stored in Xenon (the state). If a field differs and is non-null in the body,
          and if the AUTO_MERGE_IF_NOT_NULL property is set on that field, then the field is updated with the new
          value found in the body. The merged version is returned.
         */
        Utils.mergeWithState(getStateDescription(), state, patchBody);

        /*
         * For a PATCH, the default body returned to caller will just have the properties that actually changed plus all the
         * system properties. This may not be intuitive. By calling setBody with the new state, we'll return all
         * the properties that have been set (in this patch or previously).
         * setBody(null) may be the most optimized solution which will return an empty document to the user,
         * although it can be confusing when first exploring Xenon.
         */
        patch.setBody(state);
        patch.complete();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy