org.auraframework.impl.java.controller.AuraStorageTestController.java Source code

Java tutorial

Introduction

Here is the source code for org.auraframework.impl.java.controller.AuraStorageTestController.java

Source

/*
 * Copyright (C) 2013 salesforce.com, inc.
 *
 * 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 org.auraframework.impl.java.controller;

import java.io.IOException;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

import org.auraframework.Aura;
import org.auraframework.def.ComponentDef;
import org.auraframework.instance.Component;
import org.auraframework.system.Annotations.AuraEnabled;
import org.auraframework.system.Annotations.BackgroundAction;
import org.auraframework.system.Annotations.CabooseAction;
import org.auraframework.system.Annotations.Controller;
import org.auraframework.system.Annotations.Key;
import org.auraframework.test.TestContextAdapter;
import org.auraframework.throwable.AuraRuntimeException;
import org.auraframework.throwable.quickfix.QuickFixException;
import org.auraframework.util.json.Json;
import org.auraframework.util.json.JsonSerializable;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

@Controller
public class AuraStorageTestController {
    public static ConcurrentHashMap<String, Integer> staticCounter = new ConcurrentHashMap<>();
    private static Map<String, Map<String, Semaphore>> pending = new ConcurrentHashMap<>();
    private static Map<String, List<Object>> buffer = Maps.newHashMap();
    private static Map<String, Semaphore> executorLocks = new ConcurrentHashMap<>();

    private enum Command {
        RESET, WAIT, RESUME, APPEND, READ, STAMP, SLEEP;
    }

    @AuraEnabled
    public static List<Object> execute(@Key("commands") String commands) throws Exception {
        String testName = Aura.get(TestContextAdapter.class).getTestContext().getName();
        List<Object> result = Lists.newLinkedList();
        getExecutorLock(testName);
        try {
            for (String command : commands.split(";")) {
                String args[] = command.trim().split(" ", 2);
                String cmdArg = args.length > 1 ? args[1].trim() : "";
                Command cmd = Command.valueOf(args[0].trim().toUpperCase());
                switch (cmd) {
                case RESET:
                    /*
                     * When releasing blocked threads, those threads may continue running a set of commands that may
                     * create, remove, or update more semaphores. So, release blocked threads, clean up dangling
                     * semaphores, and repeat until all semaphores are gone.
                     */
                    for (Map<String, Semaphore> sems = pending.get(testName); sems != null
                            && !sems.isEmpty(); sems = pending.get(testName)) {
                        for (Iterator<Entry<String, Semaphore>> iterator = sems.entrySet().iterator(); iterator
                                .hasNext();) {
                            Semaphore current = iterator.next().getValue();
                            if (current.hasQueuedThreads()) {
                                current.release();
                            } else {
                                iterator.remove();
                            }
                        }
                        releaseExecutorLock(testName);
                        Thread.yield();
                        getExecutorLock(testName);
                    }
                    buffer.remove(testName);
                    break;
                case WAIT:
                    Semaphore sem = getSemaphore(testName, cmdArg, true);
                    releaseExecutorLock(testName);
                    try {
                        if (!sem.tryAcquire(30, TimeUnit.SECONDS)) {
                            throw new AuraRuntimeException(
                                    "Timed out waiting to acquire " + testName + ":" + cmdArg);
                        }
                    } finally {
                        getExecutorLock(testName);
                        if (!sem.hasQueuedThreads()) {
                            removeSemaphore(testName, cmdArg);
                        }
                    }
                    break;
                case RESUME:
                    getSemaphore(testName, cmdArg, true).release();
                    break;
                case APPEND:
                    getBuffer(testName).add(cmdArg);
                    break;
                case READ:
                    List<Object> temp = buffer.remove(testName);
                    if (temp != null) {
                        result.addAll(temp);
                    }
                    break;
                case STAMP:
                    getBuffer(testName).add(new Date().getTime());
                    break;
                case SLEEP:
                    long millis = 500;
                    try {
                        millis = Long.parseLong(cmdArg);
                    } catch (Throwable t) {
                    }
                    try {
                        releaseExecutorLock(testName);
                        Thread.sleep(millis);
                    } finally {
                        getExecutorLock(testName);

                    }
                    break;
                }
            }
            return result;
        } finally {
            releaseExecutorLock(testName);
        }
    }

    @AuraEnabled
    @BackgroundAction
    public static List<Object> executeBackground(@Key("commands") String commands) throws Exception {
        return execute(commands);
    }

    @AuraEnabled
    @CabooseAction
    public static List<Object> executeCaboose(@Key("commands") String commands) throws Exception {
        return execute(commands);
    }

    @AuraEnabled
    public static void block(@Key("testName") String testName) {
        getSemaphore(testName, null, true);
    }

    @AuraEnabled
    public static void resume(@Key("testName") String testName) {
        getSemaphore(testName, null, false).release();
        removeSemaphore(testName, null);
    }

    @AuraEnabled
    public static Record fetchDataRecord(@Key("testName") String testName) throws Exception {
        staticCounter.putIfAbsent(testName, 0);
        AuraStorageTestController.Record r = new AuraStorageTestController.Record(staticCounter.get(testName),
                "StorageController");
        staticCounter.put(testName, new Integer(staticCounter.get(testName).intValue() + 1));
        Semaphore lock = getSemaphore(testName, null, false);
        if (lock != null) {
            if (!lock.tryAcquire(15, TimeUnit.SECONDS)) {
                return null;
            }
        }
        return r;
    }

    /**
     * Create extra components, return value of the action is the same but getComponents() of the action differ, should
     * cause Action refresh to trigger a callback in the client
     */
    @AuraEnabled
    public static RecordWithComponents fetchDataRecordWithComponents(@Key("testName") String testName,
            @Key("extraComponentsCreated") Boolean extraComponentsCreated) throws Exception {
        staticCounter.putIfAbsent(testName, 0);
        // Create extra components the second time this server action is called.
        if (extraComponentsCreated != null && extraComponentsCreated
                && staticCounter.get(testName).intValue() == 1) {
            // Reset the counter so action return value is the same
            staticCounter.put(testName, 0);
            Map<String, Object> attr = Maps.newHashMap();
            // W-1859020 - W-1859020 - Revert to auraStorageTest:playerFacet
            // attr.put("value", ""+System.currentTimeMillis());
            Aura.getInstanceService().getInstance("uitest:hasModel", ComponentDef.class, attr);
        }
        AuraStorageTestController.RecordWithComponents r = new AuraStorageTestController.RecordWithComponents(
                staticCounter.get(testName), "StorageController");
        staticCounter.put(testName, new Integer(staticCounter.get(testName).intValue() + 1));
        Semaphore lock = getSemaphore(testName, null, false);
        if (lock != null) {
            if (!lock.tryAcquire(15, TimeUnit.SECONDS)) {
                return null;
            }
        }
        return r;
    }

    @AuraEnabled
    public static int getInt(@Key("param") int param) throws Exception {
        return param;
    }

    @AuraEnabled
    public static void resetCounter(@Key("testName") String testName) {
        if (testName != null) {
            staticCounter.remove(testName);
            removeSemaphore(testName, null);
            return;
        } else {
            for (String s : staticCounter.keySet()) {
                staticCounter.remove(s);
                removeSemaphore(s, null);
            }
        }
    }

    @AuraEnabled
    public static void setCounter(@Key("testName") String testName, @Key("value") Integer value) {
        staticCounter.put(testName, value);
    }

    @AuraEnabled
    public static List<Integer> string(@Key("testName") String testName, @Key("param1") Integer param1) {
        staticCounter.putIfAbsent(testName, 0);
        List<Integer> ret = Lists.newArrayList();
        ret.add(staticCounter.get(testName));
        ret.add(param1);
        staticCounter.put(testName, new Integer(staticCounter.get(testName).intValue() + 1));
        return ret;

    }

    @AuraEnabled
    public static List<Integer> substring(@Key("testName") String testName, @Key("param1") Integer param1) {
        staticCounter.putIfAbsent(testName, 0);
        List<Integer> ret = Lists.newArrayList();
        ret.add(staticCounter.get(testName));
        ret.add(param1);
        staticCounter.put(testName, new Integer(staticCounter.get(testName).intValue() + 1));
        return ret;
    }

    /**
     * Object to represent return value for controller.
     */
    static class Record implements JsonSerializable {
        Integer counterValue;
        Object obj;

        Record(Integer counter, Object o) {
            this.counterValue = counter;
            this.obj = o;
        }

        public Integer getCounterValue() {
            return counterValue;
        }

        public Object getObject() {
            return obj;
        }

        @Override
        public void serialize(Json json) throws IOException {
            json.writeMapBegin();
            json.writeMapEntry("Counter", getCounterValue());
            json.writeMapEntry("Data", getObject() == null ? "" : getObject());
            json.writeMapEnd();
        }
    }

    /**
     * Object to represent return value for controller.
     */
    static class RecordWithComponents implements JsonSerializable {
        Integer counterValue;
        Object obj;
        Component cmp;

        RecordWithComponents(Integer counter, Object o) {
            this.counterValue = counter;
            this.obj = o;
            Map<String, Object> attr = Maps.newHashMap();
            // attr.put("name", ""+ counterValue);
            // attr.put("nickName", "Counter" + counterValue);
            attr.put("class", "class_t " + counterValue);
            try {
                // W-1859020 - Revert to auraStorageTest:playerFacet
                // cmp = Aura.getInstanceService().getInstance("auraStorageTest:playerFacet", ComponentDef.class, attr);
                cmp = Aura.getInstanceService().getInstance("uitest:hasModel", ComponentDef.class, attr);
            } catch (QuickFixException e) {
                // Do nothing
            }
        }

        public Integer getCounterValue() {
            return counterValue;
        }

        public Object getObject() {
            return obj;
        }

        public Component getComponent() {
            return cmp;
        }

        @Override
        public void serialize(Json json) throws IOException {
            json.writeMapBegin();
            json.writeMapEntry("Counter", getCounterValue());
            json.writeMapEntry("Data", getObject() == null ? "" : getObject());
            if (getComponent() != null) {
                json.writeMapEntry("Component", getComponent());
            }
            json.writeMapEnd();
        }
    }

    @AuraEnabled
    public static List<Component> getBaseball(@Key("testName") String testName) throws Exception {
        List<Component> ret = Lists.newArrayList();
        staticCounter.putIfAbsent(testName, 0);
        Integer currentCount = staticCounter.get(testName);
        // On even count get team and on odd count get players
        if (currentCount.intValue() % 2 == 0) {
            Map<String, Object> attr = Maps.newHashMap();
            attr.put("name", "Giants");
            attr.put("city", "San Francisco");
            Component cmp = Aura.getInstanceService().getInstance("auraStorageTest:teamFacet", ComponentDef.class,
                    attr);
            ret.add(cmp);
        } else {
            Map<String, Object> attr = Maps.newHashMap();
            attr.put("name", "Posey");
            attr.put("nickName", "Buster");
            Component cmp = Aura.getInstanceService().getInstance("auraStorageTest:playerFacet", ComponentDef.class,
                    attr);
            ret.add(cmp);
            attr.put("name", "PSandavol");
            attr.put("nickName", "Panda");
            cmp = Aura.getInstanceService().getInstance("auraStorageTest:playerFacet", ComponentDef.class, attr);
            ret.add(cmp);
        }
        staticCounter.put(testName, new Integer(currentCount.intValue() + 1));
        return ret;
    }

    private static void getExecutorLock(String key) {
        Semaphore sem;
        synchronized (executorLocks) {
            sem = executorLocks.get(key);
            if (sem == null) {
                sem = new Semaphore(1, true);
                executorLocks.put(key, sem);
            }
        }
        sem.acquireUninterruptibly();
    }

    private static void releaseExecutorLock(String key) {
        synchronized (executorLocks) {
            Semaphore sem = executorLocks.get(key);
            if (sem == null) {
                return;
            }
            sem.release();
            if (!sem.hasQueuedThreads()) {
                executorLocks.remove(key);
            }
        }
    }

    private static Semaphore getSemaphore(String key, String subKey, boolean create) {
        if (key == null) {
            key = Aura.get(TestContextAdapter.class).getTestContext().getName();
        }
        synchronized (pending) {
            Map<String, Semaphore> subSet = pending.get(key);
            if (subSet == null) {
                if (!create) {
                    return null;
                }
                subSet = new ConcurrentHashMap<>();
                pending.put(key, subSet);
            }

            if (subKey == null) {
                subKey = "";
            }
            Semaphore sem = subSet.get(subKey);
            if (sem == null) {
                if (!create) {
                    return null;
                }
                sem = new Semaphore(0, true);
                subSet.put(subKey, sem);
            }
            return sem;
        }
    }

    private static void removeSemaphore(String key, String subKey) {
        if (key == null) {
            key = Aura.get(TestContextAdapter.class).getTestContext().getName();
        }
        synchronized (pending) {
            Map<String, Semaphore> subSet = pending.get(key);
            if (subSet == null) {
                return;
            }
            if (subKey == null) {
                subKey = "";
            }
            subSet.remove(subKey);
            if (subSet.isEmpty()) {
                pending.remove(key);
            }
        }
    }

    private synchronized static List<Object> getBuffer(String key) {
        List<Object> list = buffer.get(key);
        if (list == null) {
            list = Lists.newLinkedList();
            buffer.put(key, list);
        }
        return list;
    }
}