/*
* 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.ngrinder.home.controller;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.apache.commons.lang.StringUtils;
import org.ngrinder.common.constant.ControllerConstants;
import org.ngrinder.common.controller.BaseController;
import org.ngrinder.common.util.ThreadUtils;
import org.ngrinder.home.model.PanelEntry;
import org.ngrinder.home.service.HomeService;
import org.ngrinder.infra.logger.CoreLogger;
import org.ngrinder.infra.schedule.ScheduledTaskService;
import org.ngrinder.model.Role;
import org.ngrinder.model.User;
import org.ngrinder.region.service.RegionService;
import org.ngrinder.script.handler.ScriptHandlerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.LocaleEditor;
import org.springframework.http.HttpEntity;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.support.RequestContextUtils;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import static org.ngrinder.common.util.ExceptionUtils.processException;
import static org.ngrinder.common.util.Preconditions.checkNotNull;
/**
* Home index page controller.
*
* @author JunHo Yoon
* @since 3.0
*/
@Controller
public class HomeController extends BaseController implements ControllerConstants {
private static final Logger LOG = LoggerFactory.getLogger(HomeController.class);
@Autowired
private HomeService homeService;
@Autowired
private RegionService regionService;
@Autowired
private ScriptHandlerFactory scriptHandlerFactory;
private static final String TIMEZONE_ID_PREFIXES = "^(Africa|America|Asia|Atlantic|"
+ "Australia|Europe|Indian|Pacific)/.*";
private List<TimeZone> timeZones = null;
@Autowired
private ScheduledTaskService scheduledTaskService;
private static Gson rawObjectJsonSerializer = new GsonBuilder().setPrettyPrinting().create();
/**
* Initialize {@link HomeController}.
*/
@PostConstruct
public void init() {
timeZones = new ArrayList<TimeZone>();
final String[] timeZoneIds = TimeZone.getAvailableIDs();
for (final String id : timeZoneIds) {
if (id.matches(TIMEZONE_ID_PREFIXES) && !TimeZone.getTimeZone(id).getDisplayName().contains("GMT")) {
timeZones.add(TimeZone.getTimeZone(id));
}
}
Collections.sort(timeZones, new Comparator<TimeZone>() {
public int compare(final TimeZone a, final TimeZone b) {
return a.getID().compareTo(b.getID());
}
});
scheduledTaskService.runAsync(new Runnable() {
@Override
public void run() {
getLeftPanelEntries();
getRightPanelEntries();
}
});
}
/**
* Return nGrinder index page.
*
* @param user user
* @param exception exception if it's redirected from exception handler
* @param region region. where this access comes from. it's optional
* @param model model
* @param response response
* @param request request
* @return "index" if already logged in. Otherwise "login".
*/
@RequestMapping(value = {"/home", "/"})
public String home(User user, @RequestParam(value = "exception", defaultValue = "") String exception,
@RequestParam(value = "region", defaultValue = "") String region, ModelMap model,
HttpServletResponse response, HttpServletRequest request) {
try {
Role role;
try {
recordReferrer(region);
// set local language
setLanguage(getCurrentUser().getUserLanguage(), response, request);
setLoginPageDate(model);
role = user.getRole();
} catch (AuthenticationCredentialsNotFoundException e) {
return "login";
}
setPanelEntries(model);
model.addAttribute("handlers", scriptHandlerFactory.getVisibleHandlers());
if (StringUtils.isNotBlank(exception)) {
model.addAttribute("exception", exception);
}
if (role == Role.ADMIN || role == Role.SUPER_USER || role == Role.USER) {
return "index";
} else {
LOG.info("Invalid user role:{}", role.getFullName());
return "login";
}
} catch (Exception e) {
// Make the home reliable...
model.addAttribute("exception", e.getMessage());
return "index";
}
}
private void setPanelEntries(ModelMap model) {
model.addAttribute("left_panel_entries", getLeftPanelEntries());
model.addAttribute("right_panel_entries", getRightPanelEntries());
model.addAttribute(
"ask_question_url",
getConfig().getControllerProperties().getProperty(PROP_CONTROLLER_FRONT_PAGE_ASK_QUESTION_URL,
getMessages(PROP_CONTROLLER_FRONT_PAGE_ASK_QUESTION_URL)));
model.addAttribute(
"see_more_question_url",
getConfig().getControllerProperties().getProperty(PROP_CONTROLLER_FRONT_PAGE_QNA_MORE_URL,
getMessages(PROP_CONTROLLER_FRONT_PAGE_QNA_MORE_URL)));
model.addAttribute("see_more_resources_url", getConfig().getControllerProperties().getProperty
(PROP_CONTROLLER_FRONT_PAGE_RESOURCES_MORE_URL));
}
private List<PanelEntry> getRightPanelEntries() {
if (getConfig().getControllerProperties().getPropertyBoolean(PROP_CONTROLLER_FRONT_PAGE_ENABLED)) {
// Get nGrinder Resource RSS
String rightPanelRssURL = getConfig().getControllerProperties().getProperty(PROP_CONTROLLER_FRONT_PAGE_RESOURCES_RSS);
return homeService.getRightPanelEntries(rightPanelRssURL);
}
return Collections.emptyList();
}
private List<PanelEntry> getLeftPanelEntries() {
if (getConfig().getControllerProperties().getPropertyBoolean(PROP_CONTROLLER_FRONT_PAGE_ENABLED)) {
// Make the i18n applied QnA panel. Depending on the user language, show the different QnA panel.
String leftPanelRssURLKey = getMessages(PROP_CONTROLLER_FRONT_PAGE_QNA_RSS);
// Make admin configure the QnA panel.
String leftPanelRssURL = getConfig().getControllerProperties().getProperty(PROP_CONTROLLER_FRONT_PAGE_QNA_RSS,
leftPanelRssURLKey);
return homeService.getLeftPanelEntries(leftPanelRssURL);
}
return Collections.emptyList();
}
private void recordReferrer(String region) {
if (StringUtils.isNotEmpty(region)) {
CoreLogger.LOGGER.info("Accessed to {}", region);
}
}
/**
* Return the health check message. If there is shutdown lock, it returns
* 503. Otherwise it returns region lists.
*
* @param response response
* @return region list
*/
@RequestMapping("/check/healthcheck")
public HttpEntity<String> healthCheck(HttpServletResponse response) {
if (getConfig().hasShutdownLock()) {
try {
response.sendError(503, "nGrinder is about to down");
} catch (IOException e) {
LOG.error("While running healthCheck() in HomeController, the error occurs.");
LOG.error("Details : ", e);
}
}
Map<String, Object> map = new HashMap<String, Object>();
map.put("current", regionService.getCurrent());
map.put("regions", regionService.getAll());
return toJsonHttpEntity(map, rawObjectJsonSerializer);
}
/**
* Return health check message with 1 sec delay. If there is shutdown lock,
* it returns 503. Otherwise, it returns region lists.
*
* @param sleep in milliseconds.
* @param response response
* @return region list
*/
@ResponseBody
@RequestMapping("/check/healthcheck_slow")
public HttpEntity<String> healthCheckSlowly(@RequestParam(value = "delay", defaultValue = "1000") int sleep,
HttpServletResponse response) {
ThreadUtils.sleep(sleep);
return healthCheck(response);
}
private void setLanguage(String lan, HttpServletResponse response, HttpServletRequest request) {
LocaleResolver localeResolver = checkNotNull(RequestContextUtils.getLocaleResolver(request),
"No LocaleResolver found!");
LocaleEditor localeEditor = new LocaleEditor();
String language = StringUtils.defaultIfBlank(lan,
getConfig().getControllerProperties().getProperty(PROP_CONTROLLER_DEFAULT_LANG));
localeEditor.setAsText(language);
localeResolver.setLocale(request, response, (Locale) localeEditor.getValue());
}
/**
* Return the login page.
*
* @param model model
* @return "login" if not logged in. Otherwise, "/"
*/
@RequestMapping(value = "/login")
public String login(ModelMap model) {
setLoginPageDate(model);
model.addAttribute("signUpEnabled", getConfig().isSignUpEnabled());
final String defaultLang = getConfig().getControllerProperties().getProperty(ControllerConstants.PROP_CONTROLLER_DEFAULT_LANG);
model.addAttribute("defaultLang", defaultLang);
try {
getCurrentUser();
} catch (Exception e) {
CoreLogger.LOGGER.info("Login Failure " + e.getMessage());
return "login";
}
model.clear();
return "redirect:/";
}
private void setLoginPageDate(ModelMap model) {
TimeZone defaultTime = TimeZone.getDefault();
model.addAttribute("timezones", timeZones);
model.addAttribute("defaultTime", defaultTime.getID());
}
/**
* Error redirection to 404.
*
* @return "redirect:/doError"
*/
@RequestMapping(value = "/error_404")
public String error404(ModelMap model) {
model.clear();
return "redirect:/doError?type=404";
}
/**
* Error redirection as a second phase.
*
* @param user user
* @param model model
* @param response response
* @param request request
* @return "index"
*/
@RequestMapping(value = "/doError")
public String second(User user, ModelMap model, HttpServletResponse response, HttpServletRequest request) {
String parameter = request.getParameter("type");
if ("404".equals(parameter)) {
model.addAttribute("exception", processException("Requested URL does not exist"));
}
return home(user, null, null, model, response, request);
}
}