n3phele.service.rest.impl.CommandResource.java Source code

Java tutorial

Introduction

Here is the source code for n3phele.service.rest.impl.CommandResource.java

Source

/**
 * @author Nigel Cook
 *
 * (C) Copyright 2010-2012. Nigel Cook. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * 
 * Licensed under the terms described in LICENSE file that accompanied this code, (the "License"); you may not use this file
 * except in compliance with the License. 
 * 
 *  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 n3phele.service.rest.impl;

import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.annotation.security.RolesAllowed;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;

import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import javax.xml.bind.JAXBException;

import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import com.google.appengine.api.memcache.MemcacheService;
import com.google.appengine.api.memcache.MemcacheServiceFactory;
import com.googlecode.objectify.Key;
import com.sun.jersey.api.json.JSONJAXBContext;
import com.sun.jersey.api.json.JSONUnmarshaller;
import com.sun.jersey.core.util.Base64;

import n3phele.service.actions.tasks.ActionURI;
import n3phele.service.core.NotFoundException;
import n3phele.service.core.Resource;
import n3phele.service.model.Account;
import n3phele.service.model.ActionSpecification;
import n3phele.service.model.CachingAbstractManager;
import n3phele.service.model.ChangeManager;
import n3phele.service.model.CloudProfile;
import n3phele.service.model.Command;
import n3phele.service.model.AddCommandRequest;
import n3phele.service.model.CommandCollection;
import n3phele.service.model.CommandDefinition;
import n3phele.service.model.CommandDefinitions;
import n3phele.service.model.FileSpecification;
import n3phele.service.model.ServiceModelDao;
import n3phele.service.model.core.Collection;
import n3phele.service.model.core.GenericModelDao;
import n3phele.service.model.core.TypedParameter;
import n3phele.service.model.core.User;

@Path("/command")
public class CommandResource {

    private static Logger log = Logger.getLogger(CommandResource.class.getName());

    @Context
    UriInfo uriInfo;
    @Context
    SecurityContext securityContext;
    private final Dao dao;

    public CommandResource() {
        dao = new Dao();
    }

    @GET
    @Produces("application/json")
    @RolesAllowed("authenticated")
    public CommandCollection list(@DefaultValue("false") @QueryParam("summary") Boolean summary,
            @DefaultValue("false") @QueryParam("preferred") boolean preferred,
            @DefaultValue("0") @QueryParam("start") int start, @DefaultValue("-1") @QueryParam("end") int end,
            @QueryParam("search") String search) {

        log.warning("list entered with summary " + summary + " start " + start + " end " + end + " preferred "
                + preferred);
        int n = 0;
        if (start < 0)
            start = 0;
        if (end >= 0) {
            n = end - start + 1;
            if (n <= 0)
                n = 1;
        }
        if (search != null) {
            search = search.trim().toLowerCase(Locale.ENGLISH);
            if (search.length() == 0)
                search = null;
        }
        Collection<Command> result = null;
        if (search != null || preferred == false) {
            result = dao.command().getCollection(UserResource.toUser(securityContext), preferred);

            List<Command> filtered;
            if (search != null) {
                filtered = new ArrayList<Command>(result.getElements().size());
                for (Command item : result.getElements()) {
                    Boolean nameMatch = null;
                    Boolean descriptionMatch = null;
                    if ((nameMatch = item.getName().toLowerCase(Locale.ENGLISH).contains(search))
                            || (item.getDescription() != null && (descriptionMatch = item.getDescription()
                                    .toLowerCase(Locale.ENGLISH).contains(search)))) {
                        log.info("Adding " + item.getName() + " under " + search + " nameMatch " + nameMatch
                                + " descriptionMatch " + descriptionMatch);
                        filtered.add(item);
                    }
                }
                result.setElements(filtered);
            }

            result = fixOwnerNameInCollection(result, summary);
            return new CommandCollection(result, start, end);
        } else {
            result = getPreferredCommandFromCache(UserResource.toUser(securityContext), start, n);
            CommandCollection reply = new CommandCollection(result);
            return reply;
        }

    }

    private Collection<Command> fixOwnerNameInCollection(Collection<Command> result, boolean summary) {
        if (result != null) {
            for (int i = 0; i < result.getElements().size(); i++) {
                Command item = result.getElements().get(i);
                if (summary) {
                    Command summarized = Command.summary(item);
                    try {
                        summarized.setOwnerName(getOwnerName(summarized.getOwner()));

                    } catch (NotFoundException exception) {
                        summarized.setOwnerName(summarized.getOwner().toString());
                    }
                    result.getElements().set(i, summarized);
                } else {
                    try {
                        item.setOwnerName(getOwnerName(item.getOwner()));

                    } catch (NotFoundException exception) {
                        item.setOwnerName(item.getOwner().toString());
                    }
                }
            }
        }
        return result;

    }

    private Map<URI, String> cache = null;

    private String getOwnerName(URI owner) throws NotFoundException {
        String result = null;
        if (cache == null)
            cache = new HashMap<URI, String>();
        if (cache.containsKey(owner))
            result = cache.get(owner);
        else {
            User user = dao.user().get(owner);
            result = user.toString();
            cache.put(owner, result);
        }
        return result;
    }

    @GET
    @Produces("application/json")
    @RolesAllowed("authenticated")
    @Path("export")
    public CommandDefinitions export(@DefaultValue("false") @QueryParam("preferred") boolean preferred,
            @DefaultValue("0") @QueryParam("start") int start, @DefaultValue("-1") @QueryParam("end") int end,
            @DefaultValue("0") @QueryParam("clone") int clone, @QueryParam("search") String search) {

        log.warning("list entered with start " + start + " end " + end + " preferred " + preferred);
        List<CommandDefinition> result = new ArrayList<CommandDefinition>();
        Collection<Command> cmds = dao.command().getCollection(UserResource.toUser(securityContext), preferred);
        if (cmds.getElements() != null) {
            if (search != null) {
                search = search.trim().toLowerCase(Locale.ENGLISH);
                if (search.length() == 0)
                    search = null;
            }
            if (search != null) {
                List<Command> filtered = new ArrayList<Command>(cmds.getElements().size());
                for (Command item : cmds.getElements()) {
                    Boolean nameMatch = null;
                    Boolean descriptionMatch = null;
                    Boolean ownerMatch = null;
                    Boolean versionMatch = null;
                    if ((nameMatch = item.getName().toLowerCase(Locale.ENGLISH).contains(search))
                            || (item.getDescription() != null && (descriptionMatch = item.getDescription()
                                    .toLowerCase(Locale.ENGLISH).contains(search)))
                            || (item.getOwner() != null && (ownerMatch = item.getOwner().toString()
                                    .toLowerCase(Locale.ENGLISH).contains(search)))
                            || (item.getVersion() != null && (versionMatch = item.getVersion()
                                    .toLowerCase(Locale.ENGLISH).contains(search)))) {
                        log.info("Adding " + item.getName() + " under " + search + " nameMatch " + nameMatch
                                + " descriptionMatch " + descriptionMatch + " ownerMatch " + ownerMatch
                                + " versionMatch " + versionMatch);
                        filtered.add(item);
                    }
                }
                cmds.setElements(filtered);
            }
            for (int i = 0; i < cmds.getElements().size(); i++) {
                Command item = cmds.getElements().get(i);
                if (clone == 1) {
                    ArrayList<CloudProfile> profiles = item.getCloudProfiles();
                    CloudProfile profile = new CloudProfile(profiles.get(0));
                    profile.setId(profiles.size() + 1L);
                    profile.setDescription("HP Cloud");
                    profile.setCloud(URI.create("https://n3phele.appspot.com/resources/cloud/901002"));
                    profile.setActions(new ArrayList<ActionSpecification>());
                    for (ActionSpecification a : profiles.get(0).getActions()) {
                        if (a.getAction().equals(URI.create("http://www.n3phele.com/createvm"))) {
                            a = new ActionSpecification(a);
                            for (TypedParameter x : a.getInputParameters()) {
                                if (x.getName().equals("imageId")) {
                                    x.setValue("ami-000000e5");
                                } else if (x.getName().equals("instanceType")) {
                                    x.setValue("standard.xsmall");
                                }
                                if (x.getName().equals("userData")) {
                                    x.setValue(new String(Base64.encode(
                                            "#!/bin/bash\nchmod a+rwx /mnt\napt-get -y update; apt-get -y install openjdk-6-jre-headless\nset -x\nwget -q -O - https://n3phele-agent.s3.amazonaws.com/n3ph-install-tgz-basic | su - -c '/bin/bash -s ubuntu ~/agent /mnt/sandbox https://region-a.geo-1.objects.hpcloudsvc.com:443/v1.0/AUTH_dc700102-734c-4a97-afc8-50530e87a171/n3phele-agent/agent-with-swift.tgz' ubuntu\n")));
                                }
                            }
                        }
                        profile.getActions().add(a);
                    }

                    profiles.add(profile);
                    item.setCloudProfiles(profiles);
                } else if (clone == 2) {
                    ArrayList<CloudProfile> profiles = item.getCloudProfiles();
                    CloudProfile profile = new CloudProfile(profiles.get(0));
                    profile.setId(profiles.size() + 1L);
                    profile.setDescription("HP Cloud");
                    profile.setCloud(URI.create("https://n3phele.appspot.com/resources/cloud/901002"));
                    profile.setActions(new ArrayList<ActionSpecification>());
                    for (ActionSpecification a : profiles.get(0).getActions()) {
                        if (a.getAction().equals(URI.create("http://www.n3phele.com/createvm"))) {
                            a = new ActionSpecification(a);
                            for (TypedParameter x : a.getInputParameters()) {
                                if (x.getName().equals("imageId")) {
                                    x.setValue("ami-0000379f");
                                } else if (x.getName().equals("instanceType")) {
                                    x.setValue("standard.xlarge");
                                }
                                if (x.getName().equals("userData")) {
                                    x.setValue(new String(Base64.encode(
                                            "#!/bin/bash\nchmod a+rwx /mnt\nset -x\nwget -q -O - https://n3phele-agent.s3.amazonaws.com/n3ph-install-tgz-basic | su - -c '/bin/bash -s ubuntu ~/agent /mnt/sandbox https://region-a.geo-1.objects.hpcloudsvc.com:443/v1.0/AUTH_dc700102-734c-4a97-afc8-50530e87a171/n3phele-agent/agent-with-swift.tgz' ubuntu\n")));
                                }
                            }
                        }
                        profile.getActions().add(a);
                    }

                    profiles.add(profile);
                    item.setCloudProfiles(profiles);
                }
                result.add(new CommandDefinition(item));
            }
        }
        return new CommandDefinitions(result);
    }

    @Consumes(MediaType.MULTIPART_FORM_DATA)
    @POST
    @Produces(MediaType.TEXT_PLAIN)
    @Path("import")
    @RolesAllowed("authenticated")
    public Response importer(@Context HttpServletRequest request) {

        try {
            ServletFileUpload upload = new ServletFileUpload();
            FileItemIterator iterator = upload.getItemIterator(request);
            while (iterator.hasNext()) {
                FileItemStream item = iterator.next();
                InputStream stream = item.openStream();
                if (item.isFormField()) {
                    log.warning("Got a form field: " + item.getFieldName());
                } else {
                    log.warning("Got an uploaded file: " + item.getFieldName() + ", name = " + item.getName());
                    //                  JAXBContext jc = JAXBContext.newInstance(CommandDefinitions.class);
                    //                  Unmarshaller u = jc.createUnmarshaller();
                    //                  CommandDefinitions a =   (CommandDefinitions) u.unmarshal(stream);
                    JSONJAXBContext jc = new JSONJAXBContext(CommandDefinitions.class);
                    JSONUnmarshaller u = jc.createJSONUnmarshaller();
                    CommandDefinitions a = u.unmarshalFromJSON(stream, CommandDefinitions.class);
                    StringBuilder response = new StringBuilder(
                            "Processing " + a.getCommand().size() + " commands\n");
                    URI requestor = UserResource.toUser(securityContext).getUri();
                    for (CommandDefinition cd : a.getCommand()) {
                        response.append(cd.getName());
                        response.append(".....");
                        Command existing = null;
                        try {
                            if (cd.getUri() != null && cd.getUri().toString().length() != 0) {
                                try {
                                    existing = dao.command().get(cd.getUri());
                                } catch (NotFoundException e1) {
                                    List<Command> others = null;
                                    try {
                                        others = dao.command().getList(cd.getName());
                                        for (Command c : others) {
                                            if (c.getVersion().equals(cd.getVersion())
                                                    || !requestor.equals(c.getOwner())) {
                                                existing = c;
                                            } else {
                                                if (c.isPreferred() && cd.isPreferred()) {
                                                    c.setPreferred(false);
                                                    dao.command().update(c);
                                                }
                                            }
                                        }
                                    } catch (Exception e) {
                                        // not found
                                    }
                                }
                            } else {
                                List<Command> others = null;
                                try {
                                    others = dao.command().getList(cd.getName());
                                    for (Command c : others) {
                                        if (c.getVersion().equals(cd.getVersion())
                                                || !requestor.equals(c.getOwner())) {
                                            existing = c;
                                            break;
                                        }
                                    }
                                } catch (Exception e) {
                                    // not found
                                }

                            }

                        } catch (NotFoundException e) {

                        }

                        if (existing == null) {
                            response.append(addCommand(cd));
                        } else {
                            // update or illegal operation
                            boolean isOwner = requestor.equals(existing.getOwner());
                            boolean mismatchedUri = (cd.getUri() != null && cd.getUri().toString().length() != 0)
                                    && !cd.getUri().equals(existing.getUri());
                            log.info("requestor is " + requestor + " owner is " + existing.getOwner() + " isOwner "
                                    + isOwner);
                            log.info("URI in definition is " + cd.getUri() + " existing is " + existing.getUri()
                                    + " mismatchedUri " + mismatchedUri);

                            if (!isOwner || mismatchedUri) {
                                response.append("ignored: already exists");
                            } else {
                                response.append(updateCommand(cd, existing));
                                response.append("\nupdated uri " + existing.getUri());
                            }
                        }

                        response.append("\n");
                        //response.append("</li>");
                    }
                    //response.append("</ol>");
                    return Response.ok(response.toString()).build();
                }
            }

        } catch (JAXBException e) {
            log.log(Level.WARNING, "JAXBException", e);

        } catch (FileUploadException e) {
            log.log(Level.WARNING, "FileUploadException", e);
        } catch (IOException e) {
            log.log(Level.WARNING, "IOException", e);
        }
        return Response.notModified().build();
    }

    @POST
    @Produces("text/plain")
    @RolesAllowed("authenticated")
    public Response add(AddCommandRequest request) {

        ArrayList<FileSpecification> inputs = new ArrayList<FileSpecification>();
        if (request.getInputFiles() != null)
            inputs.addAll(request.getInputFiles());

        ArrayList<FileSpecification> outputs = new ArrayList<FileSpecification>();
        if (request.getOutputFiles() != null)
            outputs.addAll(request.getOutputFiles());

        ArrayList<TypedParameter> executionParameters = new ArrayList<TypedParameter>();
        if (request.getExecutionParameters() != null)
            executionParameters.addAll(request.getExecutionParameters());
        ArrayList<TypedParameter> outputParameters = new ArrayList<TypedParameter>();
        if (request.getOutputParameters() != null)
            outputParameters.addAll(request.getOutputParameters());

        Command result = new Command(request.getName(), request.getDescription(),
                UserResource.toUser(securityContext).getUri(), request.isPublic(), request.isPreferred(),
                request.getVersion(), request.getApplication(), inputs, executionParameters, outputs,
                outputParameters, null);
        dao.command().add(result);
        log.warning("Created " + result);
        if (request.isPreferred()) {
            Collection<Command> list = dao.command().getCollection(UserResource.toUser(securityContext), true);
            if (list != null && list.getTotal() > 1) {
                for (Command x : list.getElements()) {
                    if (!x.getId().equals(result.getId())) {
                        if (x.getName().equals(result.getName())) {
                            x.setPreferred(false);
                            dao.command().update(x);
                        }
                    }
                }
            }
        }
        return Response.created(result.getUri()).build();
    }

    @POST
    @Produces("text/plain")
    @RolesAllowed("authenticated")
    @Path("{Id}/cloudProfiles")
    public Response addCloudProfile(@PathParam("Id") Long id, CloudProfile profile) throws Exception {

        User owner = dao.user().load(profile.getOwner(), UserResource.toUser(securityContext)); // validate owner exists
        log.info("adding profile for" + owner.getName());

        Command item = dao.command().load(id, UserResource.toUser(securityContext));
        item = addProfile(id, profile);
        log.warning("Added " + profile.getId());
        return Response.created(uriInfo.getAbsolutePathBuilder().uri(item.getUri()).path("cloudProfiles")
                .path(Long.toString(profile.getId())).build()).build();
    }

    @GET

    @Produces("application/json")
    @RolesAllowed("authenticated")
    @Path("{Id}/cloudProfiles/{Pid}")
    public CloudProfile getCloudProfile(@PathParam("Id") Long id, @PathParam("Pid") long pid) {
        Command command = dao.command().load(id, UserResource.toUser(securityContext));
        List<CloudProfile> profiles = command.getCloudProfiles();
        for (CloudProfile c : profiles) {
            if (c.getId() == pid) {
                return c;
            }
        }
        throw new NotFoundException();
    }

    @GET
    @RolesAllowed("authenticated")
    // @Produces("application/vnd.com.n3phele.CatalogEntry+json")
    @Produces("application/json")
    @Path("{Id}")
    public Command get(@PathParam("Id") Long id) throws NotFoundException {

        Command item = dao.command().load(id, UserResource.toUser(securityContext));
        User owner = dao.user().get(item.getOwner());
        item.setOwnerName(owner.toString());
        URI userUri = UserResource.toUser(securityContext).getUri();
        boolean isAdmin = UserResource.toUser(securityContext).isAdmin();
        Map<URI, List<Account>> accountMap = new HashMap<URI, List<Account>>();
        if (item.getCloudProfiles() != null) {
            Collection<Account> accounts = new AccountResource(dao)
                    .getAccountList(UserResource.toUser(securityContext), false);
            if (accounts != null && accounts.getElements() != null) {
                for (Account account : accounts.getElements()) {
                    if (accountMap.containsKey(account.getCloud())) {
                        List<Account> list = accountMap.get(account.getCloud());
                        list.add(account);
                    } else {
                        List<Account> list = new ArrayList<Account>();
                        list.add(account);
                        accountMap.put(account.getCloud(), list);
                    }
                }
            }
            List<CloudProfile> profiles = item.getCloudProfiles();
            ArrayList<CloudProfile> decoratedProfiles = new ArrayList<CloudProfile>();
            for (CloudProfile profile : profiles) {
                if (isAdmin || profile.isPublic() || profile.getOwner().equals(userUri)) {
                    if (accountMap.containsKey(profile.getCloud())) {
                        for (Account account : accountMap.get(profile.getCloud())) {
                            CloudProfile decorated = new CloudProfile(profile);
                            decorated.setAccountName(account.getName());
                            decorated.setCloudName(account.getCloudName());
                            decorated.setAccountUri(account.getUri());
                            decoratedProfiles.add(decorated);
                        }
                    }
                }
            }
            item.setCloudProfiles(decoratedProfiles);
        }
        return item;
    }

    @DELETE
    @RolesAllowed("authenticated")
    @Path("{id}")
    public void delete(@PathParam("id") Long id) throws NotFoundException {
        Command item = dao.command().load(id, UserResource.toUser(securityContext));
        dao.command().delete(item);
    }

    /*
     * ------------------------------------------------------------------------------------------ *
     *                         == Private & Internal support functions ==
     * ------------------------------------------------------------------------------------------ *
     */

    private final static String commandCacheKey = "n3phele-command-cache";

    private Collection<Command> getPreferredCommandFromCache(User user, int start, int n) {
        MemcacheService memcache = MemcacheServiceFactory.getMemcacheService();
        CmdLet[] cachedCommands = (CmdLet[]) memcache.get(commandCacheKey);
        if (cachedCommands == null) {
            log.info("Command cache miss");
            cachedCommands = loadCommandCache();
        }

        List<Command> commands = new ArrayList<Command>();
        for (CmdLet cl : cachedCommands) {
            if (cl.isPublic || user.isAdmin() || user.getUri().toString().equals(cl.owner)) {
                Command c = new Command();
                c.setUri(URI.create(cl.URI));
                c.setName(cl.name);
                c.setDescription(cl.description);
                c.setOwnerName(cl.ownerName);
                c.setVersion(cl.version);
                c.setApplication(URI.create(cl.application));
                c.setOwner(URI.create(cl.owner));
                c.setPublic(cl.isPublic);
                c.setPreferred(true);
                commands.add(c);
            }
        }
        int end = start + n;
        if (end > commands.size())
            end = commands.size();
        Collection<Command> collection = dao.command().collectionFactory(commands.subList(start, end));
        collection.setTotal(commands.size());
        return collection;
    }

    private CmdLet[] loadCommandCache() {
        log.info("Init command cache");
        MemcacheService memcache = MemcacheServiceFactory.getMemcacheService();
        Collection<Command> preferredCommands = dao.command().getPreferredCollection();
        CmdLet[] cachedCommands = new CmdLet[preferredCommands.getElements().size()];
        for (int i = 0; i < preferredCommands.getElements().size(); i++) {
            Command c = preferredCommands.getElements().get(i);
            CmdLet cl = new CmdLet();
            cl.URI = safeURIString(c.getUri());
            cl.name = c.getName();
            cl.description = c.getDescription();
            cl.ownerName = getOwnerName(c.getOwner());
            cl.version = c.getVersion();
            cl.application = safeURIString(c.getApplication());
            cl.owner = safeURIString(c.getOwner());
            cl.isPublic = c.isPublic();
            cachedCommands[i] = cl;
        }
        memcache.put(commandCacheKey, cachedCommands);
        return cachedCommands;
    }

    private String safeURIString(URI u) {
        if (u == null)
            return null;
        return u.toString();
    }

    private static class CmdLet implements Serializable {
        private static final long serialVersionUID = 1L;
        String URI;
        String name;
        String description;
        String ownerName;
        String version;
        String application;
        String owner;
        boolean isPublic;
    }

    private Command addProfile(long id, CloudProfile profile) throws Exception {
        GenericModelDao<Command> itemDaoTxn = dao.command().itemDaoFactory(true);
        try {
            Command command = itemDaoTxn.get(id);
            if (command.getCloudProfiles() == null)
                command.setCloudProfiles(new ArrayList<CloudProfile>());
            Long maxProfile = 0L;
            for (CloudProfile p : command.getCloudProfiles()) {
                if (p.getId() != null && p.getId() > maxProfile)
                    maxProfile = p.getId();
            }
            profile.setId(maxProfile + 1);
            command.getCloudProfiles().add(profile);
            itemDaoTxn.put(command);
            itemDaoTxn.ofy().getTxn().commit();
            ChangeManager.factory().addChange(command);
            return command;
        } catch (Exception e) {
            try {
                itemDaoTxn.ofy().getTxn().rollback();
            } catch (Exception ignore) {

            }
            throw e;
        }
    }

    private String addCommand(CommandDefinition cd) {
        StringBuffer response = new StringBuffer();
        ArrayList<FileSpecification> inputs = new ArrayList<FileSpecification>();
        if (cd.getInputFiles() != null)
            inputs.addAll(cd.getInputFiles());

        ArrayList<FileSpecification> outputs = new ArrayList<FileSpecification>();
        if (cd.getOutputFiles() != null)
            outputs.addAll(cd.getOutputFiles());

        ArrayList<TypedParameter> executionParameters = new ArrayList<TypedParameter>();
        if (cd.getExecutionParameters() != null)
            executionParameters.addAll(cd.getExecutionParameters());
        ArrayList<TypedParameter> outputParameters = new ArrayList<TypedParameter>();
        if (cd.getOutputParameters() != null)
            outputParameters.addAll(cd.getOutputParameters());

        Command result = new Command(cd.getName(), cd.getDescription(),
                UserResource.toUser(securityContext).getUri(), cd.isPublic(), cd.isPreferred(), cd.getVersion(),
                cd.getIcon(), inputs, executionParameters, outputs, outputParameters, null);
        dao.command().add(result);
        log.warning("Created " + result);
        response.append("created " + result.getUri());
        for (CloudProfile profile : cd.getExecutionProfiles()) {
            profile.setOwner(UserResource.toUser(securityContext).toString());
            try {
                fixUserdata(profile);
                result = addProfile(result.getId(), profile);
            } catch (Exception e) {
                response.append(" ignored profile");
            }
            log.warning("Added " + profile.getId());
            response.append(" with profile " + profile.getId());
        }
        if (result.isPreferred()) {
            List<Command> list = dao.command().getList(result.getName());
            if (list != null && list.size() > 1) {
                for (Command x : list) {
                    if (!x.getId().equals(result.getId())) {
                        if (x.isPreferred()) {
                            x.setPreferred(false);
                            dao.command().update(x);
                        }
                    }
                }
            }
        }

        return response.toString();
    }

    private String updateCommand(CommandDefinition cd, Command existing) {
        StringBuffer result = new StringBuffer("updated");
        try {
            existing.setName(cd.getName());
            existing.setDescription(cd.getDescription());
            existing.setVersion(cd.getVersion());
            existing.setPublic(cd.isPublic());
            if (existing.isPreferred() != cd.isPreferred()) {
                if (cd.isPreferred()) {
                    List<Command> list = dao.command().getList(existing.getName());
                    if (list != null && list.size() > 1) {
                        for (Command x : list) {
                            if (!x.getId().equals(existing.getId())) {
                                if (x.isPreferred()) {
                                    x.setPreferred(false);
                                    dao.command().update(x);
                                }
                            }
                        }
                    }
                }
            }
            existing.setPreferred(cd.isPreferred());
            existing.setApplication(cd.getIcon());
            ArrayList<FileSpecification> copy = new ArrayList<FileSpecification>();
            if (cd.getInputFiles() != null)
                copy.addAll(cd.getInputFiles());
            existing.setInputFiles(copy);
            copy = new ArrayList<FileSpecification>();
            if (cd.getOutputFiles() != null)
                copy.addAll(cd.getOutputFiles());
            existing.setOutputFiles(copy);
            ArrayList<TypedParameter> copy2 = new ArrayList<TypedParameter>();
            if (cd.getExecutionParameters() != null)
                copy2.addAll(cd.getExecutionParameters());
            existing.setExecutionParameters(copy2);
            copy2 = new ArrayList<TypedParameter>();
            if (cd.getOutputParameters() != null)
                copy2.addAll(cd.getOutputParameters());
            existing.setOutputParameters(copy2);
            if (existing.getCloudProfiles() != null)
                existing.getCloudProfiles().clear();
            dao.command().update(existing);
            ChangeManager.factory().addChange(dao.command().myPath());
            if (cd.getExecutionProfiles() != null) {
                for (CloudProfile profile : cd.getExecutionProfiles()) {
                    profile.setOwner(UserResource.toUser(securityContext).toString());
                    try {
                        fixUserdata(profile);
                        addProfile(existing.getId(), profile);
                    } catch (Exception e) {
                        result.append(" ignored profile");
                    }
                    log.warning("Added " + profile.getId());
                    result.append(" with profile " + profile.getId());
                }
            }
        } catch (NotFoundException nf) {
            return "ignored";
        }

        return result.toString();
    }

    private void fixUserdata(CloudProfile cp) {
        if (cp != null) {
            if (cp.getActions() != null) {
                for (ActionSpecification action : cp.getActions()) {
                    if (ActionURI.createvm.equals(action.getAction())
                            || ActionURI.createVM.equals(action.getAction())) {
                        if (action.getInputParameters() != null) {
                            for (TypedParameter p : action.getInputParameters()) {
                                if ("userData".equals(p.getName())) {
                                    try {
                                        if (!isNullOrBlank(p.getValue())) {
                                            String encoded = new String(Base64.encode(p.getValue()));
                                            p.setValue(encoded);
                                        }
                                    } catch (Exception e) {

                                    }
                                    try {
                                        if (!isNullOrBlank(p.getDefaultValue())) {
                                            String encoded = new String(Base64.encode(p.getDefaultValue()));
                                            p.setDefaultValue(encoded);
                                        }
                                    } catch (Exception e) {

                                    }
                                }
                            }
                        }
                    }
                }
            }

        }

    }

    private boolean isNullOrBlank(String s) {
        return s == null || s.length() == 0;
    }

    public static class CommandManager extends CachingAbstractManager<Command> {
        public CommandManager() {
        }

        public Collection<Command> collectionFactory(List<Command> list) {
            return new Collection<Command>(itemDao.clazz.getSimpleName(), URI.create(myPath().toString()), list);
        }

        @Override
        protected URI myPath() {
            return UriBuilder.fromUri(Resource.get("baseURI", "http://localhost:8888/resources"))
                    .path(CommandResource.class).build();
        }

        @Override
        protected GenericModelDao<Command> itemDaoFactory(boolean transactional) {
            return new ServiceModelDao<Command>(Command.class, transactional);
        }

        public Command load(Long id, User requestor) throws NotFoundException {
            return super.get(id, requestor);
        }

        /**
         * Locate a item from the persistent store based on the item name.
         * @param name
         * @param requestor requesting user
         * @return the item
         * @throws NotFoundException is the object does not exist
         */
        public Command load(String name, User requestor) throws NotFoundException {
            return super.get(name, requestor);
        }

        /**
         * Locate a item from the persistent store based on the item URI.
         * @param uri
         * @param requestor requesting user
         * @return the item
         * @throws NotFoundException is the object does not exist
         */
        public Command load(URI uri, User requestor) throws NotFoundException {
            return super.get(uri, requestor);
        }

        /**
         * Locate a item from the persistent store based on the item URI.
         * @param uri
         * @return the item
         * @throws NotFoundException is the object does not exist
         */
        public Command load(URI uri) throws NotFoundException {
            return super.get(uri);
        }

        /** Update existing Command
         * @param item
         * @throws NotFoundException
         */
        public void update(Command item) throws NotFoundException {
            MemcacheServiceFactory.getMemcacheService().delete(commandCacheKey);
            super.update(item);
            MemcacheServiceFactory.getMemcacheService().delete(commandCacheKey);
        }

        /* (non-Javadoc)
         * @see n3phele.service.model.CachingAbstractManager#add(n3phele.service.model.core.Entity)
         */
        public void add(Command item) throws IllegalArgumentException {
            MemcacheServiceFactory.getMemcacheService().delete(commandCacheKey);
            super.add(item);
            MemcacheServiceFactory.getMemcacheService().delete(commandCacheKey);
        }

        /* (non-Javadoc)
         * @see n3phele.service.model.CachingAbstractManager#delete(n3phele.service.model.core.Entity)
         */
        public void delete(Command item) {
            MemcacheServiceFactory.getMemcacheService().delete(commandCacheKey);
            super.add(item);
            MemcacheServiceFactory.getMemcacheService().delete(commandCacheKey);
        }

        /**
         * Collection of all preferred resources of a particular class in the persistent store. 
         * @return the collection
         */
        public Collection<Command> getPreferredCollection() {
            Collection<Command> result = null;
            try {
                List<Command> children = itemDao.ofy().query(itemDao.clazz).filter("preferred", true).order("name")
                        .list();
                result = new Collection<Command>(itemDao.clazz.getSimpleName(), URI.create(myPath().toString()),
                        children);
            } catch (NotFoundException e) {
            }
            result.setTotal(result.getElements().size());
            return result;
        }

        /**
         * Collection of all preferred resources of a particular class in the persistent store. 
         * @return the collection
         */
        public List<Command> getAllPreferredPublic() {
            List<Command> result = null;
            try {
                result = itemDao.ofy().query(itemDao.clazz).filter("preferred", true).filter("isPublic", true)
                        .order("name").list();
            } catch (NotFoundException e) {
            }
            return result;
        }

        /**
         * Subset of a collection of all preferred resources of a particular class in the persistent store. 
         * @param start offset for retrieval
         * @param n maximum number of elements to retrieve
         * @return the collection
         */
        public Collection<Command> getPreferredCollection(int start, int n) {
            Collection<Command> result = null;
            try {
                log.info("Admin fetch preferred collection start=" + start + " n = " + n);
                List<Key<Command>> keys = itemDao.ofy().query(itemDao.clazz).order("name").filter("preferred", true)
                        .offset(start).limit(n).listKeys();
                Map<Key<Command>, Command> childrenMap = itemDao.ofy().get(keys);
                List<Command> children = new ArrayList<Command>(keys.size());
                for (Key<Command> k : keys) {
                    children.add(childrenMap.get(k));
                }

                result = new Collection<Command>(itemDao.clazz.getSimpleName(), URI.create(myPath().toString()),
                        children);
            } catch (NotFoundException e) {
            }
            log.info("Admin fetch preferred collection size");
            result.setTotal(itemDao.ofy().query(itemDao.clazz).filter("preferred", true).count());
            log.info("Admin fetch preferred collection size=" + result.getTotal());
            return result;
        }

        /** Collection of preferred resources of a particular class accessible by an owner in the persistent store. 
         * @param owner
         * @return the collection
         */
        public Collection<Command> getPreferredCollection(URI owner) {
            Collection<Command> result = null;
            try {
                List<Command> owned = itemDao.ofy().query(itemDao.clazz).filter("owner", owner.toString())
                        .filter("preferred", true).list();
                List<Command> shared = itemDao.ofy().query(itemDao.clazz).filter("preferred", true)
                        .filter("isPublic", true).list();
                List<Command> items = mergeResults(owned, shared, owner);
                result = new Collection<Command>(itemDao.clazz.getSimpleName(), URI.create(myPath().toString()),
                        items);
            } catch (NotFoundException e) {
            }
            result.setTotal(result.getElements().size());
            return result;
        }

        /**
         * Subset of a collection of all preferred resources of accessible by an owner in the persistent store. 
         * @param owner
         * @param start offset for retrieval
         * @param n maximum number of elements to retrieve
         * @return the collection
         */
        public Collection<Command> getPreferredCollection(URI owner, int start, int n) {
            Collection<Command> result = null;
            List<Command> list = null;
            int owned = 0;
            try {
                owned = itemDao.ofy().query(itemDao.clazz).filter("owner", owner.toString())
                        .filter("preferred", true).filter("isPublic", false).count();
                log.info("Owned is " + owned);
                if (owned > start) {
                    list = itemDao.ofy().query(itemDao.clazz).order("name").filter("owner", owner.toString())
                            .filter("preferred", true).filter("isPublic", false).offset(start).limit(n).list();
                    log.info("Fetched owned " + (list != null ? list.size() : 0));
                }
                if (list == null || list.size() < n) {
                    if (list != null) {
                        start = 0;
                        n = n - list.size();
                    }
                    log.info("Fetching shared start=" + start + " n=" + n);
                    List<Command> shared = itemDao.ofy().query(itemDao.clazz).order("name")
                            .filter("preferred", true).filter("isPublic", true).offset(start).limit(n).list();
                    if (list == null) {
                        list = shared;
                    } else {
                        list.addAll(shared);
                    }
                }
                result = new Collection<Command>(itemDao.clazz.getSimpleName(), URI.create(myPath().toString()),
                        list);
            } catch (NotFoundException e) {
            }
            int publicCommands = itemDao.ofy().query(itemDao.clazz).filter("preferred", true)
                    .filter("isPublic", true).count();
            log.info("Size is owned " + owned + " with " + publicCommands + " public commands");
            result.setTotal(owned + publicCommands);
            return result;
        }

        /**
         * Collection of resources of a particular class in the persistent store. 
         * @return the collection
         */
        public Collection<Command> getCollection(User owner, boolean preferred) {
            if (preferred) {
                return owner.isAdmin() ? getPreferredCollection() : getPreferredCollection(owner.getUri());
            } else {
                return owner.isAdmin() ? getCollection() : getCollection(owner.getUri());
            }
        }

        /**
         * Collection of resources of a particular class in the persistent store. 
         * @return the collection
         */
        public Collection<Command> getPreferredCollection(User owner, int start, int n) {
            return owner.isAdmin() ? getPreferredCollection(start, n)
                    : getPreferredCollection(owner.getUri(), start, n);
        }

    }
}