/* ==================================================================
* PluginController.java - Apr 21, 2014 10:32:12 AM
*
* Copyright 2007-2014 SolarNetwork.net Dev Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
* ==================================================================
*/
package net.solarnetwork.node.setup.web;
import static net.solarnetwork.web.domain.Response.response;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import javax.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.web.bind.annotation.ExceptionHandler;
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 net.solarnetwork.node.setup.Plugin;
import net.solarnetwork.node.setup.PluginProvisionStatus;
import net.solarnetwork.node.setup.PluginService;
import net.solarnetwork.node.setup.SimplePluginQuery;
import net.solarnetwork.node.setup.web.support.ServiceAwareController;
import net.solarnetwork.util.OptionalService;
import net.solarnetwork.web.domain.Response;
/**
* Controller to manage the installed bundles via an OBR.
*
* @author matt
* @version 1.0
*/
@ServiceAwareController
@RequestMapping("/a/plugins")
public class PluginController {
public static class PluginDetails {
private final List<Plugin> availablePlugins;
private final List<Plugin> installedPlugins;
public PluginDetails() {
super();
this.availablePlugins = Collections.emptyList();
this.installedPlugins = Collections.emptyList();
}
public PluginDetails(List<Plugin> availablePlugins, List<Plugin> installedPlugins) {
super();
this.availablePlugins = availablePlugins;
this.installedPlugins = installedPlugins;
}
public List<Plugin> getAvailablePlugins() {
return availablePlugins;
}
public List<Plugin> getInstalledPlugins() {
return installedPlugins;
}
}
@Resource(name = "pluginService")
private OptionalService<PluginService> pluginService;
@Autowired(required = true)
private MessageSource messageSource;
private long statusPollTimeoutMs = 1000L * 15L;
private final Logger log = LoggerFactory.getLogger(getClass());
public static final String ERROR_UNKNOWN_PROVISION_ID = "unknown.provisionID";
@ExceptionHandler(IllegalArgumentException.class)
@ResponseBody
public Response<Object> unknownProvisionID(IllegalArgumentException e, Locale locale) {
return new Response<Object>(Boolean.FALSE, ERROR_UNKNOWN_PROVISION_ID,
messageSource.getMessage("plugins.error.unknown-provisionID", null, locale), null);
}
@RequestMapping(value = "", method = RequestMethod.GET)
public String home() {
return "plugins/list";
}
@RequestMapping(value = "/provisionStatus", method = RequestMethod.GET)
@ResponseBody
public Response<PluginProvisionStatus> status(@RequestParam(value = "id") final String provisionID,
@RequestParam(value = "p", required = false) final Integer knownProgress,
final Locale locale) {
PluginService service = pluginService.service();
if ( service == null ) {
throw new UnsupportedOperationException("PluginService not available");
}
log.debug("Looking up provision status {}", provisionID);
// we assume a long-poll request here, so wait until the status changes
PluginProvisionStatus status;
int progress = (knownProgress != null ? knownProgress.intValue() : 0);
final long maxTime = System.currentTimeMillis() + statusPollTimeoutMs;
while ( true ) {
status = service.statusForProvisioningOperation(provisionID, locale);
if ( status == null ) {
// the provision ID is not available
throw new IllegalArgumentException(provisionID);
}
int newProgress = Math.round(status.getOverallProgress() * 100f);
if ( newProgress > progress || System.currentTimeMillis() > maxTime ) {
return response(status);
}
try {
Thread.sleep(1000);
} catch ( InterruptedException e ) {
// ignore
}
}
}
@RequestMapping(value = "/list", method = RequestMethod.GET)
@ResponseBody
public Response<PluginDetails> list(
@RequestParam(value = "filter", required = false) final String filter,
@RequestParam(value = "latestOnly", required = false) final Boolean latestOnly,
final Locale locale) {
PluginService service = pluginService.service();
if ( service == null ) {
return response(new PluginDetails());
}
SimplePluginQuery query = new SimplePluginQuery();
query.setSimpleQuery(filter);
query.setLatestVersionOnly(latestOnly == null ? true : latestOnly.booleanValue());
List<Plugin> available = service.availablePlugins(query, locale);
List<Plugin> installed = service.installedPlugins(locale);
return response(new PluginDetails(available, installed));
}
@RequestMapping(value = "/refresh", method = RequestMethod.GET)
@ResponseBody
public Response<Boolean> refresh() {
PluginService service = pluginService.service();
if ( service == null ) {
return response(Boolean.FALSE);
}
service.refreshAvailablePlugins();
return response(Boolean.TRUE);
}
@RequestMapping(value = "/install", method = RequestMethod.GET)
@ResponseBody
public Response<PluginProvisionStatus> previewInstall(@RequestParam(value = "uid") final String uid,
final Locale locale) {
PluginService service = pluginService.service();
if ( service == null ) {
throw new UnsupportedOperationException("PluginService not available");
}
Collection<String> uids = Collections.singleton(uid);
return response(service.previewInstallPlugins(uids, locale));
}
@RequestMapping(value = "/install", method = RequestMethod.POST)
@ResponseBody
public Response<PluginProvisionStatus> install(@RequestParam(value = "uid") final String uid,
final Locale locale) {
PluginService service = pluginService.service();
if ( service == null ) {
throw new UnsupportedOperationException("PluginService not available");
}
Collection<String> uids = Collections.singleton(uid);
return response(service.installPlugins(uids, locale));
}
@RequestMapping(value = "/remove", method = RequestMethod.POST)
@ResponseBody
public Response<PluginProvisionStatus> remove(@RequestParam(value = "uid") final String uid,
final Locale locale) {
PluginService service = pluginService.service();
if ( service == null ) {
throw new UnsupportedOperationException("PluginService not available");
}
Collection<String> uids = Collections.singleton(uid);
return response(service.removePlugins(uids, locale));
}
public void setPluginService(OptionalService<PluginService> pluginService) {
this.pluginService = pluginService;
}
public void setStatusPollTimeoutMs(long statusPollTimeoutMs) {
this.statusPollTimeoutMs = statusPollTimeoutMs;
}
public void setMessageSource(MessageSource messageSource) {
this.messageSource = messageSource;
}
}