uk.ac.susx.tag.method51.webapp.handler.CodingInstanceHandler.java Source code

Java tutorial

Introduction

Here is the source code for uk.ac.susx.tag.method51.webapp.handler.CodingInstanceHandler.java

Source

package uk.ac.susx.tag.method51.webapp.handler;

/*
 * #%L
 * CodingInstanceHandler.java - method51 - University of Sussex - 2,013
 * %%
 * Copyright (C) 2013 - 2014 University of Sussex
 * %%
 * 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.
 * #L%
 */

import org.apache.http.*;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.eclipse.jetty.server.Request;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import uk.ac.susx.tag.method51.core.coding.Coder;
import uk.ac.susx.tag.method51.core.organise.Job;
import uk.ac.susx.tag.method51.core.params.codec.DecodeException;
import uk.ac.susx.tag.method51.core.params.codec.EncodeException;
import uk.ac.susx.tag.method51.twitter.params.MySQLConnectionParams;
import uk.ac.susx.tag.method51.webapp.jetty.RequestException;
import uk.ac.susx.tag.method51.webapp.params.standardvalidators.NonEmptyStringValidator;
import uk.ac.susx.tag.method51.webapp.params.DoSomethingAfterValidatingMyParams;
import uk.ac.susx.tag.method51.webapp.params.Params;
import uk.ac.susx.tag.method51.webapp.params.standardvalidators.AllowedValidator;
import uk.ac.susx.tag.method51.webapp.params.standardvalidators.BooleanValidator;
import uk.ac.susx.tag.method51.webapp.server.CodingServer;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;

import static org.apache.commons.io.FilenameUtils.concat;
import static uk.ac.susx.tag.method51.core.params.Params.instance;

/**
 * Created with IntelliJ IDEA.
 * User: sw206
 * Date: 19/06/2013
 * Time: 16:19
 * To change this template use File | Settings | File Templates.
 */
public class CodingInstanceHandler extends AbstractJobHandler<CodingInstanceHandler.Options> {

    private static final Logger LOG = LoggerFactory.getLogger(CodingInstanceHandler.class);

    private Set<Integer> portRange;

    private final String modelsFolder;

    public CodingInstanceHandler(File optsFile) throws IOException, DecodeException, EncodeException {
        super(Options.class, optsFile, "coding-instances");

        modelsFolder = options.modelsFolder.get();

        if (options.instancePortRange.get().size() == 2) {
            portRange = new HashSet<>();
            int begin = options.instancePortRange.get().get(0);
            int end = options.instancePortRange.get().get(1);
            for (int i = begin; i < end; ++i) {
                portRange.add(i);
            }
        } else {
            portRange = new HashSet<>(options.instancePortRange.get());
        }

    }

    @Override
    public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {

        target = target.replaceFirst(options.serverLoc.get(), "");

        if ("/delete".equals(target)) {
            deleteJob(target, baseRequest, request, response);

        } else if ("/rename".equals(target)) {
            renameJob(target, baseRequest, request, response);

        } else if (!handleDefaultJobRequests(target, baseRequest, request, response)) {
            if ("/start_instance".equals(target)) {
                startInstance(target, baseRequest, request, response);
            } else if (target.startsWith("/coding")) {
                coding(target, baseRequest, request, response);
            } else if (target.equals("/list")) {
                list(target, baseRequest, request, response);
            } else {
                notFound(target, response);
            }
        }
        baseRequest.setHandled(true);
    }

    /**
     * Re-issue the request to an instance.
     *
     * @param target
     * @param baseRequest
     * @param request
     * @param response
     * @throws IOException
     */
    private void coding(final String target, Request baseRequest, HttpServletRequest request,
            HttpServletResponse response) throws IOException {

        new DoSomethingWithTheSpecifiedJobID(request, response) {

            public void something(String jid) throws IOException {

                Integer port = project.m51(jid).getPort();

                //int port = ((Number)j.getMeta("port")).intValue();

                HttpClient httpclient = new DefaultHttpClient();

                String method = request.getMethod();

                String target = "/coding" + request.getParameter("_target");

                URIBuilder builder = new URIBuilder();

                Set<String> ignore = new HashSet<>();
                ignore.add("id");
                ignore.add("_target");

                builder.setHost("localhost").setScheme("http").setPort(port).setPath(target);

                try {

                    HttpUriRequest uriRequest;

                    if ("GET".equals(method)) {
                        for (Map.Entry<String, String[]> e : request.getParameterMap().entrySet()) {
                            String key = e.getKey();
                            if (!ignore.contains(key)) {
                                builder.setParameter(e.getKey(), e.getValue()[0]);
                            }
                        }
                        URI uri = builder.build();
                        uriRequest = new HttpGet(uri);
                    } else {
                        URI uri = builder.build();
                        List<NameValuePair> params = new ArrayList<>();
                        for (Map.Entry<String, String[]> e : request.getParameterMap().entrySet()) {
                            String key = e.getKey();
                            if (!ignore.contains(key)) {
                                params.add(new BasicNameValuePair(key, e.getValue()[0]));
                            }
                        }
                        uriRequest = new HttpPost(uri);
                        ((HttpPost) uriRequest).setEntity(new UrlEncodedFormEntity(params));
                    }

                    LOG.info("re-issuing request: {}", uriRequest.toString());

                    HttpResponse r = httpclient.execute(uriRequest);

                    StatusLine statusLine = r.getStatusLine();
                    HttpEntity entity = r.getEntity();
                    if (statusLine.getStatusCode() >= 400) {
                        try {
                            ContentType contentType = ContentType.get(entity);
                            String responseBody = EntityUtils.toString(entity, contentType.getCharset());
                            throw new RequestException(statusLine.getReasonPhrase() + responseBody,
                                    statusLine.getStatusCode());
                        } catch (IOException | ParseException | NullPointerException e) {
                            EntityUtils.consume(entity);
                            throw new RequestException(statusLine.getReasonPhrase(), statusLine.getStatusCode());
                        }
                    }

                    response.setStatus(statusLine.getStatusCode());

                    ContentType contentType = ContentType.get(entity);
                    if (contentType == null) {
                        error("null content type!", response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                    } else {
                        String responseBody = EntityUtils.toString(entity, contentType.getCharset());

                        response.setContentType(contentType.toString());
                        response.setCharacterEncoding(contentType.getCharset().toString());
                        response.getWriter().print(responseBody);
                    }

                } catch (URISyntaxException e) {
                    error(e.getMessage(), response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                }
            }
        };
    }

    private void list(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
            throws IOException {

        File f = new File(modelsFolder);
        List<String> names = new ArrayList<>(Arrays.asList(f.list(new FilenameFilter() {
            public boolean accept(File dir, String name) {
                return name.endsWith(Coder.SAMPLE_SUFFIX);
            }
        })));

        for (int i = 0; i < names.size(); ++i) {
            names.set(i, names.get(i).replace(Coder.SAMPLE_SUFFIX, ""));
        }

        Collections.sort(names);
        okHereIsYourJson("models", names, response);
    }

    /**
     * Start new instance.
     *
     * @param target
     * @param baseRequest
     * @param request
     * @param response
     * @throws IOException
     */
    private void startInstance(String target, Request baseRequest, HttpServletRequest request,
            HttpServletResponse response) throws IOException {

        Params params = new Params();

        params.addValidator("name", new NonEmptyStringValidator(true));
        params.addValidator("wp", new BooleanValidator());

        new DoSomethingAfterValidatingMyParams(params, request, response) {

            @Override
            public void something() throws IOException, EncodeException, DecodeException {

                String name = request.getParameter("name");

                boolean nameConflict = false;

                for (String jobId : project.listJobIds()) {
                    Job job = project.getJob(jobId);
                    if (job.name.get().equals(name)) {
                        nameConflict = true;
                    }
                }

                if (nameConflict) {

                    error("Coding instances with name \"" + name + "\" already exists!");
                } else {

                    //                    ServerSocket serverSocket = null;
                    //                    boolean closed = true;
                    //                    Iterator<Integer> itr = portRange.iterator();
                    //
                    //                    int port = -1;
                    //
                    //                    Set<Integer> inUsePorts = getPortsInUse();
                    //
                    //                    while (closed && itr.hasNext()) {
                    //
                    //                        port = itr.next();
                    //
                    //                        if(inUsePorts.contains(port)) {
                    //                            continue;
                    //                        }
                    //
                    //                        try {
                    //                            serverSocket = new ServerSocket(port);
                    //                            serverSocket.close();
                    //                            closed = false;
                    //                        } catch (IOException e) {
                    //                            closed = true;
                    //                        } finally {
                    //                            if(serverSocket != null && !serverSocket.isClosed()) {
                    //                                serverSocket.close();
                    //                            }
                    //                        }
                    //                    }

                    //if(closed) {
                    if (false) {
                        error("No ports available in range");
                    } else {

                        Job job = instance(Job.class);

                        String id = project.addJob(name, job);

                        //CodingServer.Options codingServer = instance(CodingServer.Options.class);

                        // not sure if this is used

                        Coder.Options coder = instance(Coder.Options.class);

                        job.setMeta("output_prefix", id);

                        coder.modelDir.set(new File(options.modelsFolder.get()));

                        // this is used to name model files and so forth. is crazy shit.
                        coder.setName(id);

                        //File choFile = project.jobFile(id, "coding-handler.json");

                        //coder.toFile(choFile);

                        //codingServer.setHandlerConfigFile(choFile);
                        //codingServer.setPort(port);

                        //job.setMeta("port", port);

                        if (request.getParameter("wp") != null && Boolean.valueOf(request.getParameter("wp"))) {
                            job.setMeta("wp", true);
                        } else {
                            job.setMeta("wp", false);
                        }

                        //job.addComponent(codingServer);

                        job.addComponent(coder);

                        project.setJob(id, job);

                        project.startJob(id, false);

                        okHereIsYourJson("id", id, response);
                    }
                }
            }
        };
    }

    protected void renameJob(final String target, final Request baseRequest, HttpServletRequest request,
            HttpServletResponse response) throws IOException {

        Params params = new Params();
        params.addValidator("to", new NonEmptyStringValidator(true));
        params.addValidator("id", new AllowedValidator(true));
        params.addValidator("from", new AllowedValidator(true));

        new DoSomethingAfterValidatingMyParams(params, request, response) {
            @Override
            public void something() throws IOException {
                new DoSomethingWithTheSpecifiedJobID(request, response) {
                    @Override
                    public void something(String jid) throws IOException {

                        if (project.jobIsRunning(jid)) {
                            error("Can't rename a job while it is running");
                        } else {
                            String to = request.getParameter("to");

                            String newId = project.rename(jid, to);

                            String fromPath = concat(modelsFolder, jid);
                            String toPath = concat(modelsFolder, newId);

                            CodingHandler.Options cho = uk.ac.susx.tag.method51.core.params.Params.fromFile(
                                    CodingHandler.Options.class, project.jobFile(newId, "coding-handler.json"));

                            cho.name.set(newId);

                            cho.toFile(project.jobFile(newId, "coding-handler.json"));

                            Job j = project.getJob(newId);

                            j.setMeta("output_prefix", newId);

                            //CodingServer.Options serverOpts = (CodingServer.Options) j.components.get().get(0).opts.get();

                            //serverOpts.setHandlerConfigFile(project.jobFile(newId, "coding-handler.json"));

                            project.setJob(newId, j);

                            Coder.rename(fromPath, toPath);

                            okHereIsYourJson("id", newId, response);
                        }
                    }
                };
            }
        };
    }

    protected void deleteJob(final String target, final Request baseRequest, HttpServletRequest request,
            HttpServletResponse response) throws IOException {
        new DoSomethingWithTheSpecifiedJobID(request, response) {

            @Override
            public void something(String jid) throws IOException {

                Coder.delete(new File(modelsFolder, jid).getPath());

                projectHandler.delete.handle(target, baseRequest, request, response);
            }
        };
    }

    private Set<Integer> getPortsInUse() throws IOException, DecodeException {

        Set<Integer> ports = new HashSet<>();

        for (String jid : project.listAllJobIds()) {
            Job j = project.getJob(jid);
            ports.add(((Number) j.getMeta("port")).intValue());
        }

        return ports;
    }

    //
    public static class Options extends AbstractJobHandler.Options {
        public final Param<String> modelsFolder = new Param<>();
        {
            modelsFolder.doc("Directory for all models");
            modelsFolder.defaultValue("data/models");
        }

        public final Param<List<Integer>> instancePortRange = new Param<>();
        {
            List<Integer> range = new LinkedList<>();
            range.add(49125);
            range.add(65535);
            instancePortRange.defaultValue(range);
            instancePortRange.doc("ports that are to be used for coding instances");
        }
    }
}