/*
* Lokomo OneCMDB - An Open Source Software for Configuration
* Management of Datacenter Resources
*
* Copyright (C) 2006 Lokomo Systems AB
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* Lokomo Systems AB can be contacted via e-mail: info@lokomo.com or via
* paper mail: Lokomo Systems AB, Sv�rdv�gen 27, SE-182 33
* Danderyd, Sweden.
*
*/
package org.onecmdb.web;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.Map.Entry;
import org.acegisecurity.AuthenticationException;
import org.onecmdb.core.ICcb;
import org.onecmdb.core.ICi;
import org.onecmdb.core.ICiModifiable;
import org.onecmdb.core.ICmdbTransaction;
import org.onecmdb.core.IModelService;
import org.onecmdb.core.IRfcResult;
import org.onecmdb.core.ISession;
import org.onecmdb.core.ITicket;
import org.onecmdb.core.IType;
import org.onecmdb.core.IValue;
import org.onecmdb.core.internal.model.ItemId;
import org.onecmdb.core.utils.HashCodeUtil;
import org.springframework.validation.BindException;
/**
* Actions are create via parameters passed into the system.
*
* In subclasses
* @author nogun
*
*/
public class SiteAction implements Cloneable {
private ItemId _actionId;
private String _name;
private SiteCommand _command;
private String _displayName;
private Map<String, Boolean> _predicates = new HashMap<String, Boolean>();
/**
* A map used to ask if this actions supports a specific feature
* @return
*/
public final Map<String, Boolean> getPredicates() {
return this._predicates ;
}
public final void setDisplayName(String name) {
this._displayName = name;
}
public final String getDisplayName() {
return this._displayName != null ? this._displayName : getName();
}
// {{{ keeps a hierarchy of the actions
private Map<String, SiteAction> subActionMap = new TreeMap<String,SiteAction>();
// }}}
private Map<String, Object> params = new HashMap<String, Object>(0) {
@Override
public Object put(String key, Object value) {
return super.put(key, value);
}
};
/**
* <p>A map, which guarantees all values put are of type
* <code>IValue</code>.</p>
*
* <p>Also makes it possible to revert values, see {@link #revert}.</p>
*/
public class IValueMap extends LinkedHashMap<String, Object /*IValue */> {
private static final long serialVersionUID = -8681593439719854723L;
private Map<String, Object> _revert = new HashMap<String, Object>();
@Override
public Object /* IValue */ put(String key, Object value) {
if ( ! (value instanceof IValue) ) {
IModelService modelsvc = (IModelService) getCommand().getSession().getService(IModelService.class);
IType xsstring = modelsvc.getType("xs:string");
String stringValue = (value instanceof String)
? (String) value
: (value != null) ? value.toString() : null;
value = xsstring.parseString(stringValue);
}
Object prev = get(key);
if (prev != null) {
_revert .put(key, prev);
}
return super.put(key, value);
}
@Override
public void putAll(Map<? extends String, ? extends Object> m) {
super.putAll(m);
}
@Override
public Object remove(Object key) {
_revert.remove(key);
return super.remove(key);
}
@Override
public void clear() {
_revert.clear();
super.clear();
}
/**
* Revert to the previous value for the selected key, as long as the
* entry hasn't been removed, merely overwritten via {@link #put}
* @param key
*/
public void revert(String key) {
Object prev = _revert.get(key);
if (prev != null) {
put(key, prev);
}
}
};
private IValueMap _formParams = new IValueMap();
/**
* Reset all parameter known by the action with a new ones
* @param params The new map of parameters to be known by this action.
*/
public final void setParams(Map<String, Object> params) {
this.params.clear();
this.params.putAll(params);
}
public void setFormParams(Map<String, Object /*IValue*/> formParams) {
this._formParams.clear();
this._formParams.putAll(formParams);
}
/**
* Put a new parameter to this action.
* @param _name
* @param value
*/
public final void addParam(String name, String value) {
this.params.put(name, value);
}
/**
*
* @return
*/
public final Map<String, Object> getParams() {
return this.params ;
}
public SiteAction(String text) {
this._actionId = new ItemId();
this._name = text;
}
public final ItemId getId() {
return _actionId;
}
public final Map<String,SiteAction> getSubActionMap() {
return subActionMap;
}
public final void addSubAction(SiteAction action) {
subActionMap.put(action.getName(), action);
}
public final String getName() {
return _name;
}
/**
* Every action has a <em>path</em>
* @return The path this action is
*/
// public final IPath<SiteAction> getActionPath() {
// Path<SiteAction> path = new Path<SiteAction>();
// path.addElement(this);
// return path;
// }
@Override
public final int hashCode() {
int h = HashCodeUtil.SEED;
h = HashCodeUtil.hash(h, getName());
h = HashCodeUtil.hash(h, getParams());
return h;
}
/**
* Compares this action with another. Two actions are considered equal when
* its _name and its parameters, that is {@link #getParams()} matches.
*/
public final boolean equals(Object o) {
if (o == null) return false;
if ( !(o instanceof SiteAction) ) return false;
SiteAction other = (SiteAction) o;
if ( !getName().equals(other.getName()) ) return false;
return getParams().equals(other.getParams());
}
/**
* Called when an onNavigationalChange occurs. Override
* in subclasses.
* @param errors TODO
*/
protected void handleNavigationalChange(BindException errors) {
}
/**
* Return this actions <em>form parameters</em>, which should be seen as
* a <em>temporary</em>. These parameters are rebound on every submission
* until the form is <em>applied</em>.
* @return A map of values. The object stored is always a IValue.
*/
public IValueMap /* IValue */ getFormParams() {
return _formParams;
}
/**
* <p>Produces a clone of this object, by <em>coping</em> only the relevant
* parts:</p>
* <ul>
* <li>The _name</li>
* <li>The currently set form parameters</li>
* </ul>
*
* @see java.lang.Object#clone()
*/
@Override
public final Object clone() {
SiteAction clone = null;;
try {
clone = (SiteAction) super.clone();
clone.params = new HashMap<String, Object>(getParams().size());
for (Entry<String, Object> entry : getParams().entrySet()) {
clone.params.put(entry.getKey(), entry.getValue());
}
clone._formParams = new IValueMap();
for (Entry<String, Object /*IValue*/> entry :getFormParams().entrySet() ) {
IValue v = (IValue) entry.getValue();
clone._formParams.put(entry.getKey(), v);
}
//clone.predicates = new HashMap<String, Boolean>();
//clone.predicates.putAll(_predicates);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
/**
* Callback used when a navigational change to this action occurs. Should
* take care of initialization, etc.
*/
public final void onNavigationalChange(BindException errors) {
getFormParams().clear();
handleNavigationalChange(errors);
}
@Override
public String toString() {
return getName() + getParams();
}
/**
* Called once, to initiate this action, when a new form is created, i.e.
* a new session is opened.
* @param site
* @param errors
*/
public void onNewForm(SiteCommand site, BindException errors) {
this._command = site;
}
/**
* Fetch the site _command associated with this action
* @return
*/
protected final SiteCommand getCommand() {
return this._command;
}
/**
* Whenever a <em>change form</em> is triggered this method is called, which
* actually forwards to the abstract method
* {@link #handleFormChange(FormChange, BindException)}.
*
* @param change
* @param errors
* @see #handleFormChange(FormChange, BindException)
*/
public final void onChangeForm(FormChange change, BindException errors) {
handleFormChange(change, errors);
}
/**
* <p>Used to actually act on an {@link #onChangeForm} event, and is supposed
* to be overridden by subclasses.</p>
*
* <p>This is kind a way to implement actions within an action, to be able
* to process a longer action, for example, a wizard.</p>
*
* @param change The event triggering the form change
* @param errors
*/
protected void handleFormChange(final FormChange change,
final BindException errors) {
/*
* TODO: break apart into something like a command pattern, to reflect
* the sub action characteristics.
*/
if ("login".equals(change.getOperation())) {
try {
getSession().login();
} catch (AuthenticationException e) {
errors.rejectValue(change.getChangeExpr(), "LOGIN_FAILED", "Login Failed");
}
getCommand().reset();
} else if ("logout".equals(change.getOperation())) {
getSession().logout();
getCommand().reset();
}
else if ("deleteCi".equals(change.getOperation())) {
deleteCi(change, errors);
}
else if ("deleteMarked".equals(change.getOperation())) {
deleteMarked(change, errors);
}
}
/** Deletes a group a selected CIs, found in the CIMARK parameters */
private void deleteMarked(FormChange change, BindException errors) {
ISession session = getCommand().getSession();
IModelService model = (IModelService) session.getService(IModelService.class);
IType xsboolean = model.getType("xs:boolean");
ICcb ccb = (ICcb) session.getService(ICcb.class);
ICmdbTransaction tx = ccb.getTx(session);
for (String name : getFormParams().keySet()) {
if (name.startsWith("CIMARK")) {
IValue value = (IValue) getFormParams().get(name);
if (value != null ) {
value = xsboolean.fromValue(value);
if ((Boolean) value.getAsJavaObject()) {
String lex = name.substring("CIMARK".length());
ItemId ciid = new ItemId(lex);
ICi ci = model.find(ciid);
if (ci != null) {
ICiModifiable tplci = tx.getTemplate(ci);
tplci.delete();
}
}
}
}
}
ITicket ticket = ccb.submitTx(tx);
IRfcResult result = ccb.waitForTx(ticket);
if (result.isRejected()) {
errors.rejectValue(change.getChangeExpr(), "REJECT",
new String[] { result.getRejectCause()},
result.getRejectCause());
}
}
/**
* Processes a CI deletion
* @param change
* @param errors
*/
private void deleteCi(FormChange change, BindException errors) {
ItemId ciId = new ItemId(change.getArgs().get(0));
ISession session = getCommand().getSession();
IModelService model = (IModelService) session.getService(IModelService.class);
ICi ci = model.find(ciId);
ICcb ccb = (ICcb) session.getService(ICcb.class);
ICmdbTransaction tx = ccb.getTx(session);
{
ICiModifiable tpl = tx.getTemplate(ci);
tpl.delete();
}
ITicket ticket = ccb.submitTx(tx);
IRfcResult result = ccb.waitForTx(ticket);
if (result.isRejected()) {
errors.rejectValue(change.getChangeExpr(), "REJECT",
new String[] { result.getRejectCause()},
result.getRejectCause());
}
}
/** before the form is shown, this method is called */
public void process() {
// do nothing
}
protected ISession getSession() {
return getCommand().getSession();
}
}