package com.intrbiz.bergamot.ui.router.admin;
import static com.intrbiz.Util.*;
import static com.intrbiz.balsa.BalsaContext.*;
import java.io.IOException;
import java.io.StringReader;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.UUID;
import javax.xml.bind.JAXBException;
import org.apache.log4j.Logger;
import com.fasterxml.jackson.core.JsonGenerator;
import com.intrbiz.Util;
import com.intrbiz.balsa.engine.route.Router;
import com.intrbiz.balsa.engine.task.BalsaTaskState;
import com.intrbiz.balsa.metadata.WithDataAdapter;
import com.intrbiz.bergamot.config.model.BergamotCfg;
import com.intrbiz.bergamot.config.model.NamedObjectCfg;
import com.intrbiz.bergamot.config.model.TemplatedObjectCfg;
import com.intrbiz.bergamot.config.model.TemplatedObjectCfg.ObjectState;
import com.intrbiz.bergamot.config.validator.ValidatedBergamotConfiguration;
import com.intrbiz.bergamot.data.BergamotDB;
import com.intrbiz.bergamot.importer.BergamotImportReport;
import com.intrbiz.bergamot.metadata.GetBergamotSite;
import com.intrbiz.bergamot.metadata.IsaObjectId;
import com.intrbiz.bergamot.model.ConfigChange;
import com.intrbiz.bergamot.model.Contact;
import com.intrbiz.bergamot.model.Site;
import com.intrbiz.bergamot.model.util.Parameter;
import com.intrbiz.bergamot.ui.BergamotApp;
import com.intrbiz.configuration.CfgParameter;
import com.intrbiz.configuration.Configuration;
import com.intrbiz.metadata.Any;
import com.intrbiz.metadata.Catch;
import com.intrbiz.metadata.CoalesceMode;
import com.intrbiz.metadata.Get;
import com.intrbiz.metadata.IsaLong;
import com.intrbiz.metadata.Param;
import com.intrbiz.metadata.Post;
import com.intrbiz.metadata.Prefix;
import com.intrbiz.metadata.RequirePermission;
import com.intrbiz.metadata.RequireValidPrincipal;
import com.intrbiz.metadata.Template;
@Prefix("/admin/configchange")
@Template("layout/main")
@RequireValidPrincipal()
@RequirePermission("ui.admin")
public class ConfigChangeAdminRouter extends Router<BergamotApp>
{
private Logger logger = Logger.getLogger(ConfigChangeAdminRouter.class);
@Any("/")
@WithDataAdapter(BergamotDB.class)
public void index(BergamotDB db, @GetBergamotSite() Site site)
{
List<ConfigChange> pending = var("pending_changes", db.getPendingConfigChanges(site.getId()));
List<ConfigChange> applied = var("applied_changes", db.pageConfigChanges(site.getId(), true, 5, 0));
List<ConfigChange> changes = var("changes", new LinkedList<ConfigChange>());
changes.addAll(pending);
changes.addAll(applied);
encode("admin/configchange/index");
}
@Any("/history")
@WithDataAdapter(BergamotDB.class)
public void history(
BergamotDB db,
@GetBergamotSite() Site site,
@Param("offset") @IsaLong(min = 0, mandatory = true, coalesce = CoalesceMode.ALWAYS, defaultValue = 0L) long offset,
@Param("limit") @IsaLong(min = 1, mandatory = true, coalesce = CoalesceMode.ALWAYS, defaultValue = 100L) long limit
)
{
var("changes", db.pageConfigChanges(site.getId(), true, limit, offset));
var("offset", offset);
var("limit", limit);
encode("admin/configchange/history");
}
@Any("/create")
@WithDataAdapter(BergamotDB.class)
public void create(
BergamotDB db,
@GetBergamotSite() Site site,
@Param("summary") String summary,
@Param("description") String description
) throws IOException
{
Contact user = currentPrincipal();
ConfigChange change = new ConfigChange(site.getId(), user, new BergamotCfg(site.getName(), summary, description));
db.setConfigChange(change);
redirect(path("/admin/configchange/edit/id/" + change.getId()));
}
@Get("/view/id/:id")
@WithDataAdapter(BergamotDB.class)
public void view(BergamotDB db, @IsaObjectId UUID id)
{
var("change", db.getConfigChange(id));
encode("admin/configchange/view");
}
@Get("/edit/id/:id")
@WithDataAdapter(BergamotDB.class)
public void edit(BergamotDB db, @IsaObjectId UUID id)
{
ConfigChange change = var("change", db.getConfigChange(id));
sessionVar("current_change", change.getId());
encode("admin/configchange/edit");
}
@Post("/edit/id/:id")
@WithDataAdapter(BergamotDB.class)
public void save(BergamotDB db, @IsaObjectId UUID id, @Param("change_configuration") String configuration, @GetBergamotSite() Site site) throws IOException, JAXBException
{
BergamotCfg cfg = Configuration.read(BergamotCfg.class, new StringReader(configuration));
// update
ConfigChange change = db.getConfigChange(id);
cfg.setSite(site.getName());
change.setConfiguration(cfg);
change.setSummary(Util.coalesceEmpty(cfg.getSummary(), change.getSummary(), ""));
change.setDescription(cfg.getDescription());
db.setConfigChange(change);
// nullify the current change id
sessionVar("current_change", null);
// back to list
redirect(path("/admin/configchange/"));
}
@Any("/add/:type/id/:id")
@WithDataAdapter(BergamotDB.class)
public void add(BergamotDB db, @GetBergamotSite() Site site, String type, @IsaObjectId UUID id) throws IOException
{
TemplatedObjectCfg<?> cfg = (TemplatedObjectCfg<?>) db.getConfig(id).getConfiguration();
// update the change
UUID currentChangeId = sessionVar("current_change");
Contact user = currentPrincipal();
ConfigChange change = currentChangeId == null ? new ConfigChange(site.getId(), user, new BergamotCfg(site.getName(), "Edit " + type, null)) : db.getConfigChange(currentChangeId);
// remove the object id
((NamedObjectCfg<?>) cfg).setId(null);
// add the object
((BergamotCfg) change.getConfiguration()).addObject(cfg);
// update
db.setConfigChange(change);
// edit
redirect(path("/admin/configchange/edit/id/" + change.getId()));
}
@Any("/add/site-parameters")
@WithDataAdapter(BergamotDB.class)
public void add(BergamotDB db, @GetBergamotSite() Site site) throws IOException
{
// make sure we have a fresh copy of the site
site = db.getSite(site.getId());
// update the change
UUID currentChangeId = sessionVar("current_change");
Contact user = currentPrincipal();
ConfigChange change = currentChangeId == null ? new ConfigChange(site.getId(), user, new BergamotCfg(site.getName(), "Edit site parameters", null)) : db.getConfigChange(currentChangeId);
// copy the site parameters into the config change
BergamotCfg cfg = ((BergamotCfg) change.getConfiguration());
for (Entry<String, Parameter> param : site.getParameters().entrySet())
{
cfg.addParameter(new CfgParameter(param.getKey(), param.getValue().getDescription(), null, param.getValue().getValue()));
}
// update
db.setConfigChange(change);
// edit
redirect(path("/admin/configchange/edit/id/" + change.getId()));
}
@Any("/remove/:type/id/:id")
@WithDataAdapter(BergamotDB.class)
public void remove(BergamotDB db, @GetBergamotSite() Site site, String type, @IsaObjectId UUID id) throws Exception
{
TemplatedObjectCfg<?> cfg = (TemplatedObjectCfg<?>) db.getConfig(id).getConfiguration();
// update the change
UUID currentChangeId = sessionVar("current_change");
Contact user = currentPrincipal();
ConfigChange change = currentChangeId == null ? new ConfigChange(site.getId(), user, new BergamotCfg(site.getName(), "Edit " + type, null)) : db.getConfigChange(currentChangeId);
// remove the object id
((NamedObjectCfg<?>) cfg).setId(null);
// set removed
cfg.setObjectState(ObjectState.REMOVED);
// add the object
((BergamotCfg) change.getConfiguration()).addObject(cfg);
// update
db.setConfigChange(change);
// edit
redirect(path("/admin/configchange/edit/id/" + change.getId()));
}
@Catch(JAXBException.class)
@Post("/edit/id/:id")
@WithDataAdapter(BergamotDB.class)
public void saveError(BergamotDB db, @IsaObjectId UUID id, @Param("change_configuration") String configuration)
{
JAXBException error = (JAXBException) balsa().getException();
Throwable linked = error.getLinkedException();
// the change
var("change", db.getConfigChange(id));
var("error", coalesceEmpty(error.getMessage(), nullable(linked, Throwable::getMessage), "Error parsing XML"));
var("bad_configuration", configuration);
// edit configuration
encode("admin/configchange/edit");
}
@Any("/validate/id/:id")
@WithDataAdapter(BergamotDB.class)
public void validate(BergamotDB db, @GetBergamotSite() Site site, @IsaObjectId UUID id)
{
// nullify any current change
sessionVar("current_change", null);
// get the change
ConfigChange change = var("change", db.getConfigChange(id));
//
BergamotCfg cfg = (BergamotCfg) change.getConfiguration();
ValidatedBergamotConfiguration validated = cfg.validate(db.getObjectLocator(site.getId()));
//
var("report", validated.getReport());
//
encode("admin/configchange/validate");
}
@Any("/apply/id/:id")
@RequirePermission("config.change.apply")
@WithDataAdapter(BergamotDB.class)
public void apply(BergamotDB db, @GetBergamotSite() Site site, @IsaObjectId UUID id)
{
Contact user = currentPrincipal();
// nullify any current change
sessionVar("current_change", null);
// get the change
ConfigChange change = var("change", db.getConfigChange(id));
// compute the reset url we need for registrations
final String resetUrl = Balsa().url(Balsa().path("/reset"));
// apply the change
String taskId = deferredActionWithId(id.toString(), "apply-config-change", site.getId(), id, resetUrl, user);
//
var("change", change);
var("taskid", taskId);
//
encode("admin/configchange/apply");
}
@Any("/poll/apply/id/:id")
@RequirePermission("config.change.apply")
public void pollApply(@GetBergamotSite() Site site, @IsaObjectId UUID id) throws IOException
{
// get the task state
BalsaTaskState state = pollDeferredAction(id.toString());
// output JSON response
JsonGenerator json = response().ok().json().getJsonWriter();
json.writeStartObject();
// output
if (state != null)
{
if (state.isComplete())
{
// get the value
try
{
BergamotImportReport report = state.get();
// stat
json.writeFieldName("stat");
json.writeString("ok");
json.writeFieldName("complete");
json.writeBoolean(true);
json.writeFieldName("result");
json.writeString(report.toString());
}
catch (Exception e)
{
logger.error("Error polling config change state", e);
json.writeFieldName("stat");
json.writeString("error");
json.writeFieldName("message");
json.writeString(e.getMessage());
}
}
else
{
json.writeFieldName("stat");
json.writeString("ok");
json.writeFieldName("complete");
json.writeBoolean(false);
}
}
else
{
json.writeFieldName("stat");
json.writeString("error");
json.writeFieldName("message");
json.writeString("No such change");
}
json.writeEndObject();
}
@Any("/remove/id/:id")
@WithDataAdapter(BergamotDB.class)
public void remove(BergamotDB db, @IsaObjectId UUID id) throws IOException
{
ConfigChange change = db.getConfigChange(id);
if (change != null && (! change.isApplied()))
{
// can't remove an applied change
db.removeConfigChange(id);
}
redirect(path("/admin/configchange/"));
}
}