com.interop.webapp.WebApp.java Source code

Java tutorial

Introduction

Here is the source code for com.interop.webapp.WebApp.java

Source

/*
 * Copyright 2012-2014 the original author or authors.
 *
 * 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 com.interop.webapp;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static java.nio.file.StandardCopyOption.*;

import javax.annotation.PostConstruct;
import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.rabbitmq.client.*;
import com.rabbitmq.client.AMQP.BasicProperties;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@EnableAutoConfiguration
@ComponentScan
@Controller
public class WebApp extends WebMvcConfigurerAdapter {
    @Autowired
    private WebAppConfig config;

    static String imagesWebPath = "resources";
    static String processedFilesWebPath = "processedfiles";
    static String imagesWebPathMask = "/" + imagesWebPath + "/**";

    static final Logger log = LoggerFactory.getLogger(WebApp.class);

    private Connection connection;
    private Channel channel;
    private String replyQueueName;
    private QueueingConsumer consumer;

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.favorPathExtension(false);
    }

    @PostConstruct
    public void setUpQueue() throws Exception {

        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost(config.getQueueHostName());
        connection = factory.newConnection();
        channel = connection.createChannel();

        replyQueueName = channel.queueDeclare().getQueue();
        consumer = new QueueingConsumer(channel);
        channel.basicConsume(replyQueueName, true, consumer);
        // Create the folder that will hold the processed files
        String foldername = config.getImageFilesRoot() + '/' + processedFilesWebPath;
        File folder = new File(URI.create(foldername));
        if (folder.exists()) {
            return;
        }
        log.info("creating directory: " + foldername);
        try {
            folder.mkdir();
        } catch (SecurityException se) {
            log.error(String.format("creating directory: %s (%s)", foldername, se.getMessage()));
        } catch (Exception e) {
            log.error(String.format("creating directory: %s (%s)", foldername, e.getMessage()));
        }
    }

    /**
     * @return String - logged in user
     */
    private String getLoggedInUser() {
        return SecurityContextHolder.getContext().getAuthentication().getName();
    }

    @RequestMapping("/")
    public String home(Map<String, Object> model) {
        String user = getLoggedInUser();
        UserImageFileRepository store = new UserImageFileRepository(user, config.getImageFilesRoot());
        List<ImageDetailBean> collection = store.getWebPaths();
        model.put("user", user);
        model.put("imagesWebPath", imagesWebPath);
        model.put("imagerefs", collection);
        model.put("count", collection.size());
        return "home";
    }

    @RequestMapping(value = "/image", method = RequestMethod.GET)
    public String image(Map<String, Object> model, @RequestParam("name") String imageName) {
        String user = getLoggedInUser();
        UserImageFileRepository store = new UserImageFileRepository(user, config.getImageFilesRoot());
        model.put("hostname", config.getHostName());
        model.put("user", user);
        model.put("imagesWebPath", imagesWebPath);
        model.put("imagename", imageName);
        model.put("imageref", store.getWebPath(imageName));
        model.put("effects", new String[] { "Grayscale", "Invert", "Blur" });
        model.put("message", "");
        return "image";
    }

    @RequestMapping(value = "/image", method = RequestMethod.POST)
    public String imageSave(Map<String, Object> model,
            @RequestParam(value = "imagename", defaultValue = "") String imageName,
            @RequestParam(value = "imagenew", defaultValue = "") String imageNew) {
        String user = getLoggedInUser();
        String filesRoot = config.getImageFilesRoot();
        if (!imageNew.equals("")) {
            // We have a new image from processing, need to replace the original.
            UserImageFileRepository store = new UserImageFileRepository(user, filesRoot);
            String destFilename = store.getPath(imageName);
            String srcFilename = filesRoot + '/' + processedFilesWebPath + '/'
                    + imageNew.substring(imageNew.lastIndexOf('/') + 1);
            try {
                Files.move(Paths.get(URI.create(srcFilename)), Paths.get(URI.create(destFilename)),
                        REPLACE_EXISTING);
            } catch (Exception e) {
                log.error(String.format("Failed to copy [%s] to [%s] (%s)", srcFilename, destFilename,
                        e.getMessage()));
            }
        }
        return "redirect:/";
    }

    @RequestMapping(value = "/upload", method = RequestMethod.GET)
    public String upload(Map<String, Object> model) {
        return "upload";
    }

    @RequestMapping(value = "/upload", method = RequestMethod.POST)
    public String upload(Map<String, Object> model,
            @RequestParam(value = "imagename", defaultValue = "") String imagename,
            @RequestParam("uploadfile") MultipartFile uploadedfile) {
        String user = getLoggedInUser();
        UserImageFileRepository store = new UserImageFileRepository(user, config.getImageFilesRoot());
        Boolean success = store.newUpload(imagename, uploadedfile);
        model.put("success", success);
        model.put("imagename", imagename);
        return "upload";
    }

    @RequestMapping("/testharness")
    public String testHarness(Map<String, Object> model) {
        String user = getLoggedInUser();
        model.put("user", user);
        model.put("hostname", config.getHostName());
        model.put("processingtimeout", config.getProcessingTimeout());
        return "testharness";
    }

    @RequestMapping("/logout")
    public String logout(Map<String, Object> model) {
        SecurityContextHolder.getContext().getAuthentication().setAuthenticated(false);
        return "redirect:/";
    }

    /*
     * REST methods for AJAX calls from client
     */

    /*
     * Because we are sending in a filename, we needed to add the 
     * regular expression to the end of the RequestMapping because Spring 
     * defaults to a regular expression of [^.]* (everything but a period)
     * However, because we a sending names of image/binary files the Spring 
     * component MappingJackson2HttpMessageConverter determines that the JSON 
     * content that we are sending back does not match the content requested 
     * and sends back a HTTP error code 406 back to the client.  To mitigate
     * this we need to define the configureContentNegotiation() method above.
     */
    @RequestMapping(value = "/effectrequest/{name}/{imagename:[a-zA-Z0-9%\\.]*}", headers = "Accept=*/*", method = RequestMethod.GET, produces = "application/json")
    public @ResponseBody EffectRequest effectRequest(@PathVariable("name") String name,
            @PathVariable("imagename") String imageName) {
        String user = getLoggedInUser();
        String hostName = this.config.getHostName();
        UserImageFileRepository store = new UserImageFileRepository(user, config.getImageFilesRoot());

        String status = "submitted";
        String correlationId = java.util.UUID.randomUUID().toString();

        Map<String, Object> mapObject = new HashMap<String, Object>();
        String imageFullPath = store.getPath(imageName);
        String ext = imageFullPath.substring(imageFullPath.lastIndexOf('.') + 1);
        String processedFileName = String.format("%s/%s/%s.%s", config.getImageFilesRoot(), processedFilesWebPath,
                correlationId, ext);

        long created = System.currentTimeMillis();
        mapObject.put("correlationId", correlationId);
        mapObject.put("user", user);
        mapObject.put("inputPath", imageFullPath);
        mapObject.put("ouputPath", processedFileName);
        mapObject.put("effectName", name);
        mapObject.put("requestHost", hostName);
        mapObject.put("requestCreated", created);

        JsonWrapper objJson = new JsonWrapper();
        String message = objJson.toJson(mapObject);

        BasicProperties props = new BasicProperties.Builder().correlationId(correlationId).replyTo(replyQueueName)
                .build();
        try {
            channel.basicPublish("", config.getQueueName(), props, message.getBytes());
            log.info(String.format("effectrequest\tsuccess\t%s\t%s\t%s", hostName, user, correlationId));
        } catch (IOException e) {
            log.error(String.format("effectrequest\tfail\t%s\t%s\t%s\t%s", hostName, user, correlationId,
                    e.getMessage()));
            status = "failed";
        }
        return new EffectRequest(status, correlationId, created);
    }

    @RequestMapping(value = "/effectfetch/{requestid}/{requestcreated}", headers = "Accept=*/*", method = RequestMethod.GET, produces = "application/json")
    public @ResponseBody EffectFetch effectFetch(@PathVariable("requestid") String correlationId,
            @PathVariable("requestcreated") long requestCreated) {
        String user = getLoggedInUser();
        String hostName = this.config.getHostName();
        String status = "notready";
        String url = "";
        long millisecondsElapsed = 0;

        String response = null;
        QueueingConsumer.Delivery delivery = null;
        try {
            delivery = consumer.nextDelivery(333);
            if (delivery != null && delivery.getProperties().getCorrelationId().equals(correlationId)) {
                response = new String(delivery.getBody());
                JsonWrapper objJson = new JsonWrapper();
                Map<String, Object> mapObject = objJson.fromJson(response);
                status = (String) mapObject.get("status");
                String newFileName = (String) mapObject.get("ouputPath");
                newFileName = newFileName.substring(newFileName.lastIndexOf('/') + 1);
                url = "/" + imagesWebPath + "/" + processedFilesWebPath + "/" + newFileName;

                long curr = System.currentTimeMillis();
                millisecondsElapsed = curr - requestCreated; //Time difference in milliseconds

                log.info(String.format("effectfetch\t%s\t%s\t%s\t%s\t%d", status, hostName, user, correlationId,
                        millisecondsElapsed));
            } else {
                millisecondsElapsed = System.currentTimeMillis() - requestCreated;
                if (millisecondsElapsed > config.getProcessingTimeout()) {
                    status = "failed";
                    log.error(String.format("effectrequest\tfail\t%s\t%s\t%s\t%s", hostName, user, correlationId,
                            "Max time exceeded"));
                }
            }
        } catch (ShutdownSignalException e) {
            status = "failed";
            log.error(String.format("effectrequest\tqueuefail\t%s\t%s\t%s\t%s", hostName, user, correlationId,
                    e.getMessage()));
        } catch (ConsumerCancelledException e) {
            status = "failed";
            log.error(String.format("effectrequest\tqueuefail\t%s\t%s\t%s\t%s", hostName, user, correlationId,
                    e.getMessage()));
        } catch (InterruptedException e) {
            status = "failed";
            log.error(String.format("effectrequest\tqueuefail\t%s\t%s\t%s\t%s", hostName, user, correlationId,
                    e.getMessage()));
        }
        return new EffectFetch(status, correlationId, url, millisecondsElapsed);
    }

    public static void main(String[] args) throws Exception {
        new SpringApplicationBuilder(WebApp.class, "classpath:/META-INF/application-context.xml").run(args);
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/login").setViewName("login");
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler(imagesWebPathMask).addResourceLocations(config.getImageFilesRoot() + '/');
    }

    @Bean
    public ApplicationSecurity applicationSecurity() {
        return new ApplicationSecurity();
    }

    @Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
    protected static class ApplicationSecurity extends WebSecurityConfigurerAdapter {

        @Autowired
        private SecurityProperties security;

        @Autowired
        private DataSource dataSource;

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests().antMatchers("/css/**", imagesWebPathMask).permitAll().anyRequest()
                    .fullyAuthenticated().and().formLogin().loginPage("/login").failureUrl("/login?error")
                    .permitAll();
        }

        @Override
        public void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.jdbcAuthentication().dataSource(this.dataSource);
        }

    }

}