0001: /*
0002:
0003: This software is OSI Certified Open Source Software.
0004: OSI Certified is a certification mark of the Open Source Initiative.
0005:
0006: The license (Mozilla version 1.0) can be read at the MMBase site.
0007: See http://www.MMBase.org/license
0008:
0009: */
0010:
0011: package org.mmbase.bridge.implementation;
0012:
0013: import java.util.*;
0014: import java.io.*;
0015: import org.mmbase.bridge.*;
0016: import org.mmbase.bridge.util.BridgeCollections;
0017: import org.mmbase.bridge.util.Queries;
0018: import org.mmbase.cache.*;
0019: import org.mmbase.module.core.*;
0020: import org.mmbase.module.corebuilders.*;
0021:
0022: import org.mmbase.security.*;
0023: import org.mmbase.storage.search.*;
0024: import org.mmbase.util.*;
0025: import org.mmbase.util.functions.*;
0026: import org.mmbase.util.logging.*;
0027:
0028: /**
0029: * Basic implementation of Cloud. It wraps a.o. the core's ClusterBuilder and Typedef functionalities.
0030: *
0031: * @author Rob Vermeulen
0032: * @author Pierre van Rooden
0033: * @author Michiel Meeuwissen
0034: * @version $Id: BasicCloud.java,v 1.181 2008/02/27 11:47:54 michiel Exp $
0035: */
0036: public class BasicCloud implements Cloud, Cloneable, Comparable<Cloud>,
0037: SizeMeasurable, Serializable {
0038:
0039: private static final long serialVersionUID = 1;
0040:
0041: private static final Logger log = Logging
0042: .getLoggerInstance(BasicCloud.class);
0043:
0044: // lastRequestId
0045: // used to generate a temporary ID number
0046: // The Id starts at - 2 and is decremented each time a new id is asked
0047: // until Integer.MIN_VALUE is reached (after which counting starts again at -2).
0048: private static int lastRequestId = Integer.MIN_VALUE;
0049:
0050: // link to cloud context
0051: private BasicCloudContext cloudContext = null;
0052:
0053: // name of the cloud
0054: protected String name = null;
0055:
0056: // account of the current user (unique)
0057: // This is a unique number, unrelated to the user context
0058: // It is meant to uniquely identify this session to MMBase
0059: // It is NOT used for authorisation!
0060: protected String account = null;
0061:
0062: // description
0063: // note: in future, this is dependend on language settings!
0064: protected String description = null;
0065:
0066: // all transactions started by this cloud object (with createTransaction)
0067: protected Map<String, Transaction> transactions = new HashMap<String, Transaction>();
0068:
0069: // node managers cache
0070: protected Map<String, BasicNodeManager> nodeManagerCache = new HashMap<String, BasicNodeManager>();
0071:
0072: protected UserContext userContext = null;
0073:
0074: private HashMap<Object, Object> properties = new HashMap<Object, Object>();
0075:
0076: private Locale locale;
0077:
0078: public int getByteSize() {
0079: return getByteSize(new SizeOf());
0080: }
0081:
0082: public int getByteSize(SizeOf sizeof) {
0083: return sizeof.sizeof(transactions)
0084: + sizeof.sizeof(nodeManagerCache);
0085: }
0086:
0087: private static long counter = 0;
0088: protected final long count = ++counter;
0089:
0090: /**
0091: * basic constructor for descendant clouds (i.e. Transaction)
0092: * @param cloudName name of cloud
0093: * @param cloud parent cloud
0094: */
0095: BasicCloud(String cloudName, BasicCloud cloud) {
0096: cloudContext = cloud.cloudContext;
0097: locale = cloud.locale;
0098: name = cloudName;
0099: description = cloud.description;
0100: userContext = cloud.userContext;
0101: account = cloud.account;
0102: }
0103:
0104: /**
0105: * @param name name of cloud
0106: * @param authenticationType authentication type
0107: * @param loginInfo Map with login credentials
0108: * @param cloudContext cloudContext of cloud
0109: * @throws NotFoundException If MMBase not yet started, or shutting down.
0110: * @throws BridgeException No security could be obtained.
0111: * @throws SecurityException Could not perform login
0112: */
0113: BasicCloud(String name, String authenticationType, Map loginInfo,
0114: CloudContext cloudContext) {
0115: // get the cloudcontext and mmbase root...
0116: this .cloudContext = (BasicCloudContext) cloudContext;
0117: init();
0118: userContext = BasicCloudContext.mmb.getMMBaseCop()
0119: .getAuthentication().login(authenticationType,
0120: loginInfo, null);
0121: if (userContext == null) {
0122: log.debug("Login failed");
0123: throw new java.lang.SecurityException(
0124: "Login invalid (login-module: "
0125: + authenticationType + ")");
0126: }
0127: // end authentication...
0128:
0129: if (userContext.getAuthenticationType() == null) {
0130: log
0131: .warn("Security implementation did not set 'authentication type' in the user object.");
0132: }
0133:
0134: // normally, we want the cloud to read it's context from an xml file.
0135: // the current system does not support multiple clouds yet,
0136: // so as a temporary hack we set default values
0137: this .name = name;
0138: description = name;
0139: }
0140:
0141: /**
0142: * @param name name of cloud
0143: * @param authenticationType authentication type
0144: * @param loginInfo Map with login credentials
0145: * @param cloudContext cloudContext of cloud
0146: * @throws NotFoundException If MMBase not yet started, or shutting down.
0147: * @throws BridgeException No security could be obtained.
0148: * @throws SecurityException Could not perform login
0149: */
0150: BasicCloud(String name, UserContext user,
0151: BasicCloudContext cloudContext) {
0152: // get the cloudcontext and mmbase root...
0153: this .cloudContext = cloudContext;
0154: init();
0155: userContext = user;
0156: if (userContext == null) {
0157: throw new java.lang.SecurityException(
0158: "Login invalid: did not supply user object");
0159: }
0160:
0161: if (userContext.getAuthenticationType() == null) {
0162: log
0163: .warn("Security implementation did not set 'authentication type' in the user object.");
0164: }
0165:
0166: this .name = name;
0167: description = name;
0168: }
0169:
0170: private final void init() {
0171: MMBase mmb = BasicCloudContext.mmb;
0172:
0173: if (!mmb.getState()) {
0174: throw new NotFoundException(
0175: "MMBase not yet, or not successfully initialized (check mmbase log)");
0176: }
0177:
0178: if (mmb.isShutdown()) {
0179: throw new NotFoundException("MMBase is shutting down.");
0180: }
0181:
0182: log.debug("Doing authentication");
0183:
0184: if (mmb.getMMBaseCop() == null) {
0185: throw new BridgeException(
0186: "Couldn't find the MMBaseCop. Perhaps your MMBase did not start up correctly; check application server and mmbase logs ");
0187: }
0188: log.debug("Setting up cloud object");
0189: locale = mmb.getLocale();
0190:
0191: // generate an unique id for this instance...
0192: account = "U" + uniqueId();
0193: }
0194:
0195: // Makes a node or Relation object based on an MMObjectNode
0196: BasicNode makeNode(MMObjectNode node, String nodeNumber) {
0197: int nodenr = node.getNumber();
0198: MMObjectBuilder parent = node.getBuilder();
0199: if (nodenr == -1) {
0200: int nodeid = Integer.parseInt(nodeNumber);
0201: if (parent instanceof TypeDef) {
0202: return new BasicNodeManager(node, this , nodeid);
0203: } else if (parent instanceof RelDef
0204: || parent instanceof TypeRel) {
0205: return new BasicRelationManager(node, this , nodeid);
0206: } else if (parent instanceof InsRel) {
0207: return new BasicRelation(node, this , nodeid);
0208: } else {
0209: return new BasicNode(node, this , nodeid);
0210: }
0211: } else {
0212: this .verify(Operation.READ, nodenr);
0213: if (parent instanceof TypeDef) {
0214: return new BasicNodeManager(node, this );
0215: } else if (parent instanceof RelDef
0216: || parent instanceof TypeRel) {
0217: return new BasicRelationManager(node, this );
0218: } else if (parent instanceof InsRel) {
0219: return new BasicRelation(node, this );
0220: } else {
0221: return new BasicNode(node, this );
0222: }
0223: }
0224: }
0225:
0226: public Node getNode(String nodeNumber) throws NotFoundException {
0227: MMObjectNode node;
0228: try {
0229: node = BasicCloudContext.tmpObjectManager.getNode(account,
0230: nodeNumber);
0231: } catch (RuntimeException e) {
0232: throw new NotFoundException(
0233: "Something went wrong while getting node with number '"
0234: + nodeNumber + "': " + e.getMessage()
0235: + " by cloud with account " + account, e);
0236: }
0237: if (node == null) {
0238: throw new NotFoundException("Node with number '"
0239: + nodeNumber + "' does not exist.");
0240: } else {
0241: return makeNode(node, nodeNumber);
0242: }
0243: }
0244:
0245: public final Node getNode(int nodeNumber) throws NotFoundException {
0246: return getNode("" + nodeNumber);
0247: }
0248:
0249: public final Node getNodeByAlias(String aliasname)
0250: throws NotFoundException {
0251: return getNode(aliasname);
0252: }
0253:
0254: public final Relation getRelation(int nodeNumber)
0255: throws NotFoundException {
0256: return getRelation("" + nodeNumber);
0257: }
0258:
0259: public final Relation getRelation(String nodeNumber)
0260: throws NotFoundException {
0261: return (Relation) getNode(nodeNumber);
0262: }
0263:
0264: public boolean hasNode(int nodeNumber) {
0265: return hasNode("" + nodeNumber, false);
0266: }
0267:
0268: public boolean hasNode(String nodeNumber) {
0269: return nodeNumber != null && hasNode(nodeNumber, false);
0270: }
0271:
0272: // check if anode exists.
0273: // if isrelation is true, the method returns false if the node is not a relation
0274: private boolean hasNode(String nodeNumber, boolean isrelation) {
0275: MMObjectNode node;
0276: try {
0277: node = BasicCloudContext.tmpObjectManager.getNode(account,
0278: nodeNumber);
0279: } catch (Throwable e) {
0280: return false; // error - node inaccessible or does not exist
0281: }
0282: if (node == null) {
0283: return false; // node does not exist
0284: } else {
0285: if (isrelation && !(node.getBuilder() instanceof InsRel)) {
0286: return false;
0287: }
0288: return true;
0289: }
0290: }
0291:
0292: public boolean hasRelation(int nodeNumber) {
0293: return hasNode("" + nodeNumber, true);
0294: }
0295:
0296: public boolean hasRelation(String nodeNumber) {
0297: return hasNode(nodeNumber, true);
0298: }
0299:
0300: public NodeManagerList getNodeManagers() {
0301: List<String> nodeManagers = new ArrayList<String>();
0302: for (Object element : BasicCloudContext.mmb.getBuilders()) {
0303: MMObjectBuilder bul = (MMObjectBuilder) element;
0304: if (!bul.isVirtual()
0305: && check(Operation.READ, bul.getNumber())) {
0306: nodeManagers.add(bul.getTableName());
0307: }
0308: }
0309: return new BasicNodeManagerList(nodeManagers, this );
0310: }
0311:
0312: /**
0313: * @since MMBase-1.8
0314: */
0315: BasicNodeManager getBasicNodeManager(MMObjectBuilder bul)
0316: throws NotFoundException {
0317: String nodeManagerName = bul.getTableName();
0318: // cache quicker, and you don't get 2000 nodetypes when you do a search....
0319: BasicNodeManager nodeManager = nodeManagerCache
0320: .get(nodeManagerName);
0321: if (nodeManager == null) {
0322: // not found in cache
0323: nodeManager = new BasicNodeManager(bul, this );
0324: nodeManagerCache.put(nodeManagerName, nodeManager);
0325: } else if (nodeManager.getMMObjectBuilder() != bul) {
0326: // cache differs
0327: nodeManagerCache.remove(nodeManagerName);
0328: nodeManager = new BasicNodeManager(bul, this );
0329: nodeManagerCache.put(nodeManagerName, nodeManager);
0330: }
0331: return nodeManager;
0332: }
0333:
0334: BasicNodeManager getBasicNodeManager(String nodeManagerName)
0335: throws NotFoundException {
0336: MMObjectBuilder bul = BasicCloudContext.mmb
0337: .getMMObject(nodeManagerName);
0338: // always look if builder exists, since otherwise
0339: if (bul == null) {
0340: throw new NotFoundException("Node manager with name '"
0341: + nodeManagerName + "' does not exist.");
0342: }
0343: return getBasicNodeManager(bul);
0344:
0345: }
0346:
0347: public final NodeManager getNodeManager(String nodeManagerName)
0348: throws NotFoundException {
0349: return getBasicNodeManager(nodeManagerName);
0350: }
0351:
0352: public boolean hasNodeManager(String nodeManagerName) {
0353: return BasicCloudContext.mmb.getMMObject(nodeManagerName) != null;
0354: }
0355:
0356: /**
0357: * Retrieves a node manager
0358: * @param nodeManagerId ID of the NodeManager to retrieve
0359: * @return the requested <code>NodeManager</code> if the manager exists, <code>null</code> otherwise
0360: * @throws NotFoundException node manager not found
0361: */
0362: public NodeManager getNodeManager(int nodeManagerId)
0363: throws NotFoundException {
0364: TypeDef typedef = BasicCloudContext.mmb.getTypeDef();
0365: return getNodeManager(typedef.getValue(nodeManagerId));
0366: }
0367:
0368: /**
0369: * Retrieves a RelationManager.
0370: * Note that you can retrieve a manager with source and destination reversed.
0371: * @param sourceManagerId number of the NodeManager of the source node
0372: * @param destinationManagerId number of the NodeManager of the destination node
0373: * @param roleId number of the role
0374: * @return the requested RelationManager
0375: */
0376: RelationManager getRelationManager(int sourceManagerId,
0377: int destinationManagerId, int roleId) {
0378: Set<MMObjectNode> set = BasicCloudContext.mmb.getTypeRel()
0379: .getAllowedRelations(sourceManagerId,
0380: destinationManagerId, roleId);
0381: if (set.size() > 0) {
0382: Iterator<MMObjectNode> i = set.iterator();
0383: MMObjectNode typeRel = i.next();
0384: if (set.size() > 1
0385: && (sourceManagerId != -1 || destinationManagerId != -1)) {
0386: int quality = (typeRel.getIntValue("snumber") == sourceManagerId ? 1
0387: : 0)
0388: + (typeRel.getIntValue("dnumber") == destinationManagerId ? 1
0389: : 0);
0390:
0391: while (i.hasNext()) {
0392: MMObjectNode candidate = i.next();
0393: int candidateQuality = (candidate
0394: .getIntValue("snumber") == sourceManagerId ? 1
0395: : 0)
0396: + (candidate.getIntValue("dnumber") == destinationManagerId ? 1
0397: : 0);
0398: if (candidateQuality > quality) {
0399: typeRel = candidate;
0400: quality = candidateQuality;
0401: }
0402: }
0403: }
0404:
0405: return new BasicRelationManager(typeRel, this );
0406: } else {
0407: log.error("Relation " + sourceManagerId + "/"
0408: + destinationManagerId + "/" + roleId
0409: + " does not exist", new Exception());
0410: return null; // calling method throws exception
0411: }
0412: }
0413:
0414: public RelationManager getRelationManager(int number)
0415: throws NotFoundException {
0416: MMObjectNode n = BasicCloudContext.mmb.getTypeDef().getNode(
0417: number);
0418: if (n == null) {
0419: throw new NotFoundException("Relation manager with number "
0420: + number + " does not exist.");
0421: }
0422: if ((n.getBuilder() instanceof RelDef)
0423: || (n.getBuilder() instanceof TypeRel)) {
0424: return new BasicRelationManager(n, this );
0425: } else {
0426: throw new NotFoundException("Node with number " + number
0427: + " is not a relation manager.");
0428: }
0429: }
0430:
0431: public RelationManagerList getRelationManagers() {
0432: List<MMObjectNode> v = BasicCloudContext.mmb.getTypeRel()
0433: .getNodes();
0434: return new BasicRelationManagerList(v, this );
0435: }
0436:
0437: public RelationManager getRelationManager(String sourceManagerName,
0438: String destinationManagerName, String roleName)
0439: throws NotFoundException {
0440: int r = BasicCloudContext.mmb.getRelDef().getNumberByName(
0441: roleName);
0442: if (r == -1) {
0443: throw new NotFoundException("Role '" + roleName
0444: + "' does not exist.");
0445: }
0446: // other settings of the cloud...
0447: TypeDef typedef = BasicCloudContext.mmb.getTypeDef();
0448:
0449: int n1 = typedef.getIntValue(sourceManagerName);
0450: if (n1 == -1) {
0451: throw new NotFoundException("Source type '"
0452: + sourceManagerName + "' does not exist.");
0453: }
0454: int n2 = typedef.getIntValue(destinationManagerName);
0455: if (n2 == -1) {
0456: throw new NotFoundException("Destination type '"
0457: + destinationManagerName + "' does not exist.");
0458: }
0459: RelationManager rm = getRelationManager(n1, n2, r);
0460: if (rm == null) {
0461: throw new NotFoundException("Relation manager from '"
0462: + sourceManagerName + "' to '"
0463: + destinationManagerName + "' as '" + roleName
0464: + "' does not exist.");
0465: } else {
0466: return rm;
0467: }
0468: }
0469:
0470: public RelationManager getRelationManager(NodeManager source,
0471: NodeManager destination, String roleName)
0472: throws NotFoundException {
0473: int r = BasicCloudContext.mmb.getRelDef().getNumberByName(
0474: roleName);
0475: if (r == -1) {
0476: throw new NotFoundException("Role '" + roleName
0477: + "' does not exist.");
0478: }
0479: RelationManager rm = getRelationManager(source.getNumber(),
0480: destination.getNumber(), r);
0481: if (rm == null) {
0482: throw new NotFoundException("Relation manager from '"
0483: + source + "' to '" + destination + "' as '"
0484: + roleName + "' does not exist.");
0485: } else {
0486: return rm;
0487: }
0488:
0489: }
0490:
0491: public boolean hasRelationManager(String sourceManagerName,
0492: String destinationManagerName, String roleName) {
0493: int r = BasicCloudContext.mmb.getRelDef().getNumberByName(
0494: roleName);
0495: if (r == -1)
0496: return false;
0497: TypeDef typedef = BasicCloudContext.mmb.getTypeDef();
0498: int n1 = typedef.getIntValue(sourceManagerName);
0499: if (n1 == -1)
0500: return false;
0501: int n2 = typedef.getIntValue(destinationManagerName);
0502: if (n2 == -1)
0503: return false;
0504:
0505: return BasicCloudContext.mmb.getTypeRel().contains(n1, n2, r);
0506: // return getRelationManager(n1, n2, r) != null;
0507: }
0508:
0509: public boolean hasRole(String roleName) {
0510: return BasicCloudContext.mmb.getRelDef().getNumberByName(
0511: roleName) != -1;
0512: }
0513:
0514: public boolean hasRelationManager(NodeManager source,
0515: NodeManager destination, String roleName) {
0516: int r = BasicCloudContext.mmb.getRelDef().getNumberByName(
0517: roleName);
0518: if (r == -1)
0519: return false;
0520: return BasicCloudContext.mmb.getTypeRel().contains(
0521: source.getNumber(), destination.getNumber(), r);
0522: // return getRelationManager(source.getNumber(), destination.getNumber(), r) != null;
0523: }
0524:
0525: public RelationManager getRelationManager(String roleName)
0526: throws NotFoundException {
0527: int r = BasicCloudContext.mmb.getRelDef().getNumberByName(
0528: roleName);
0529: if (r == -1) {
0530: throw new NotFoundException("Role '" + roleName
0531: + "' does not exist.");
0532: }
0533: return getRelationManager(r);
0534: }
0535:
0536: public RelationManagerList getRelationManagers(
0537: String sourceManagerName, String destinationManagerName,
0538: String roleName) throws NotFoundException {
0539: NodeManager n1 = null;
0540: if (sourceManagerName != null) {
0541: n1 = getNodeManager(sourceManagerName);
0542: }
0543: NodeManager n2 = null;
0544: if (destinationManagerName != null) {
0545: n2 = getNodeManager(destinationManagerName);
0546: }
0547: return getRelationManagers(n1, n2, roleName);
0548: }
0549:
0550: public RelationManagerList getRelationManagers(
0551: NodeManager sourceManager, NodeManager destinationManager,
0552: String roleName) throws NotFoundException {
0553: if (sourceManager != null) {
0554: return sourceManager.getAllowedRelations(
0555: destinationManager, roleName, null);
0556: } else if (destinationManager != null) {
0557: return destinationManager.getAllowedRelations(
0558: sourceManager, roleName, null);
0559: } else if (roleName != null) {
0560: int r = BasicCloudContext.mmb.getRelDef().getNumberByName(
0561: roleName);
0562: if (r == -1) {
0563: throw new NotFoundException("Role '" + roleName
0564: + "' does not exist.");
0565: }
0566: Vector<MMObjectNode> v = BasicCloudContext.mmb.getTypeRel()
0567: .searchVector("rnumber==" + r);
0568: return new BasicRelationManagerList(v, this );
0569: } else {
0570: return getRelationManagers();
0571: }
0572: }
0573:
0574: public boolean hasRelationManager(String roleName) {
0575: return BasicCloudContext.mmb.getRelDef().getNumberByName(
0576: roleName) != -1;
0577: }
0578:
0579: /**
0580: * Create unique temporary node number.
0581: * The Id starts at - 2 and is decremented each time a new id is asked
0582: * until Integer.MINVALUE is reached (after which counting starts again at -2).
0583: * @todo This may be a temporary solution. It may be desirable to immediately reserve a
0584: * number at the database layer, so resolving (by the transaction) will not be needed.
0585: * However, this needs some changes in the TemporaryNodeManager and the classes that make use of this.
0586: *
0587: * @return the temporary id as an integer
0588: */
0589: static synchronized int uniqueId() {
0590: if (lastRequestId > Integer.MIN_VALUE) {
0591: lastRequestId--;
0592: } else {
0593: lastRequestId = -2;
0594: }
0595: return lastRequestId;
0596: }
0597:
0598: /**
0599: * Test if a node id is a temporay id.
0600: * @param id the id (node numebr) to test
0601: * @return true if the id is a temporary id
0602: * @since MMBase-1.5
0603: */
0604: static boolean isTemporaryId(int id) {
0605: return id < -1;
0606: }
0607:
0608: public Transaction createTransaction() {
0609: return createTransaction(null, false);
0610: }
0611:
0612: public Transaction createTransaction(String name)
0613: throws AlreadyExistsException {
0614: return createTransaction(name, false);
0615: }
0616:
0617: public Transaction createTransaction(String name, boolean overwrite)
0618: throws AlreadyExistsException {
0619: if (name == null) {
0620: name = "Tran" + uniqueId();
0621: } else {
0622: Transaction oldtransaction = transactions.get(name);
0623: if (oldtransaction != null) {
0624: if (overwrite) {
0625: oldtransaction.cancel();
0626: } else {
0627: throw new AlreadyExistsException(
0628: "Transaction with name " + name
0629: + "already exists.");
0630: }
0631: }
0632: }
0633: Transaction transaction = new BasicTransaction(name, this );
0634: transactions.put(name, transaction);
0635: return transaction;
0636: }
0637:
0638: public Transaction getTransaction(String name) {
0639: Transaction tran = transactions.get(name);
0640: if (tran == null) {
0641: tran = createTransaction(name, false);
0642: } else {
0643: }
0644: return tran;
0645: }
0646:
0647: public CloudContext getCloudContext() {
0648: return cloudContext;
0649: }
0650:
0651: public String getName() {
0652: return name;
0653: }
0654:
0655: public String getDescription() {
0656: return description;
0657: }
0658:
0659: public UserContext getUser() {
0660: return userContext;
0661: }
0662:
0663: /**
0664: * Retrieves the current user accountname (unique)
0665: * @return the account name
0666: */
0667: String getAccount() {
0668: return account;
0669: }
0670:
0671: /**
0672: * Checks access rights.
0673: * @param operation the operation to check (READ, WRITE, CREATE, OWN)
0674: * @param nodeID the node on which to check the operation
0675: * @return <code>true</code> if access is granted, <code>false</code> otherwise
0676: */
0677: boolean check(Operation operation, int nodeID) {
0678: return BasicCloudContext.mmb.getMMBaseCop().getAuthorization()
0679: .check(userContext, nodeID, operation);
0680: }
0681:
0682: /**
0683: * Asserts access rights. throws an exception if an operation is not allowed.
0684: * @param operation the operation to check (READ, WRITE, CREATE, OWN)
0685: * @param nodeID the node on which to check the operation
0686: */
0687: void verify(Operation operation, int nodeID) {
0688: BasicCloudContext.mmb.getMMBaseCop().getAuthorization().verify(
0689: userContext, nodeID, operation);
0690: }
0691:
0692: /**
0693: * Checks access rights.
0694: * @param operation the operation to check (CREATE, CHANGE_RELATION)
0695: * @param nodeID the node on which to check the operation
0696: * @param srcNodeID the source node for this relation
0697: * @param dstNodeID the destination node for this relation
0698: * @return <code>true</code> if access is granted, <code>false</code> otherwise
0699: */
0700: boolean check(Operation operation, int nodeID, int srcNodeID,
0701: int dstNodeID) {
0702: return BasicCloudContext.mmb.getMMBaseCop().getAuthorization()
0703: .check(userContext, nodeID, srcNodeID, dstNodeID,
0704: operation);
0705: }
0706:
0707: /**
0708: * Asserts access rights. throws an exception if an operation is not allowed.
0709: * @param operation the operation to check (CREATE, CHANGE_RELATION)
0710: * @param nodeID the node on which to check the operation
0711: * @param srcNodeID the source node for this relation
0712: * @param dstNodeID the destination node for this relation
0713: */
0714: void verify(Operation operation, int nodeID, int srcNodeID,
0715: int dstNodeID) {
0716: BasicCloudContext.mmb.getMMBaseCop().getAuthorization().verify(
0717: userContext, nodeID, srcNodeID, dstNodeID, operation);
0718: }
0719:
0720: // javadoc inherited
0721: public NodeList getList(Query query) {
0722: log.debug("get List");
0723: NodeList result;
0724: if (query.isAggregating()) { // should this perhaps be a seperate method? --> Then also 'isAggregating' not needed any more
0725: result = getResultNodeList(query);
0726: } else {
0727: result = getSecureList(query);
0728: }
0729: if (query instanceof NodeQuery) {
0730: NodeQuery nq = (NodeQuery) query;
0731: String pref = nq.getNodeStep().getAlias();
0732: if (pref == null)
0733: pref = nq.getNodeStep().getTableName();
0734: result.setProperty(NodeList.NODESTEP_PROPERTY, pref);
0735: }
0736: return result;
0737: }
0738:
0739: /*
0740: // javadoc inherited
0741: public NodeList getList(NodeQuery query) {
0742: return getSecureNodes(query);
0743: }
0744: */
0745:
0746: /**
0747: * Aggregating query result.
0748: * @param query query to execute
0749: * @return list of nodes
0750: * @since MMBase-1.7
0751: */
0752: protected NodeList getResultNodeList(Query query) {
0753: log.debug("Resultnode list");
0754: try {
0755:
0756: boolean checked = setSecurityConstraint(query);
0757:
0758: if (!checked) {
0759: log
0760: .warn("Query "
0761: + query
0762: + " could not be completely modified by security: Aggregated result might be wrong");
0763: }
0764: AggregatedResultCache cache = AggregatedResultCache
0765: .getCache();
0766:
0767: List<MMObjectNode> resultList = cache.get(query);
0768: if (resultList == null) {
0769: ResultBuilder resultBuilder = new ResultBuilder(
0770: BasicCloudContext.mmb, query);
0771: resultList = BasicCloudContext.mmb
0772: .getSearchQueryHandler().getNodes(query,
0773: resultBuilder);
0774: cache.put(query, resultList);
0775: }
0776: query.markUsed();
0777: NodeManager tempNodeManager = new VirtualNodeManager(query,
0778: this );
0779: NodeList resultNodeList = new BasicNodeList(resultList,
0780: tempNodeManager);
0781: resultNodeList.setProperty(NodeList.QUERY_PROPERTY, query);
0782: return resultNodeList;
0783: } catch (SearchQueryException sqe) {
0784: throw new BridgeException(sqe);
0785: }
0786: }
0787:
0788: /**
0789: * Result with all Cluster - MMObjectNodes, with cache. Security is not considered here (the
0790: * query is executed thoughtlessly). The security check is done in getSecureNodes, which calls
0791: * this one.
0792: * @param query query to execute
0793: * @return list of cluster nodes
0794: * @since MMBase-1.7
0795: */
0796: protected List<MMObjectNode> getClusterNodes(Query query) {
0797:
0798: // start multilevel cache
0799: MultilevelCache multilevelCache = MultilevelCache.getCache();
0800:
0801: ClusterBuilder clusterBuilder = BasicCloudContext.mmb
0802: .getClusterBuilder();
0803: // check multilevel cache if needed
0804: List<MMObjectNode> resultList = null;
0805: if (query.getCachePolicy().checkPolicy(query)) {
0806: resultList = multilevelCache.get(query);
0807: }
0808: // if unavailable, obtain from database
0809: if (resultList == null) {
0810: log.debug("result list is null, getting from database");
0811: try {
0812: resultList = clusterBuilder.getClusterNodes(query);
0813: } catch (SearchQueryException sqe) {
0814: throw new BridgeException(query.toString() + ":"
0815: + sqe.getMessage(), sqe);
0816: }
0817: if (query.getCachePolicy().checkPolicy(query)) {
0818: multilevelCache.put(query, resultList);
0819: }
0820: }
0821:
0822: query.markUsed();
0823:
0824: return resultList;
0825: }
0826:
0827: /**
0828: * @param query add security constaint to this query
0829: * @return is query secure
0830: * @since MMBase-1.7
0831: */
0832: boolean setSecurityConstraint(Query query) {
0833: Authorization auth = BasicCloudContext.mmb.getMMBaseCop()
0834: .getAuthorization();
0835: if (query instanceof BasicQuery) { // query should alway be 'BasicQuery' but if not, for some on-fore-seen reason..
0836: BasicQuery bquery = (BasicQuery) query;
0837: if (bquery.isSecure()) { // already set, and secure
0838: return true;
0839: } else {
0840: if (bquery.queryCheck == null) { // not set already, do it now.
0841: Authorization.QueryCheck check = auth.check(
0842: userContext, query, Operation.READ);
0843: if (log.isDebugEnabled()) {
0844: log.debug("FOUND security check " + check
0845: + " FOR " + query);
0846: }
0847: bquery.setSecurityConstraint(check);
0848: }
0849: return bquery.isSecure();
0850: }
0851: } else {
0852: // should not happen
0853: if (query != null) {
0854: log
0855: .warn("Don't know how to set a security constraint on a "
0856: + query.getClass().getName());
0857: } else {
0858: log
0859: .warn("Don't know how to set a security constraint on NULL");
0860: }
0861: }
0862: return false;
0863: }
0864:
0865: public StringList getPossibleContexts() {
0866: return new BasicStringList(BasicCloudContext.mmb.getMMBaseCop()
0867: .getAuthorization().getPossibleContexts(getUser()));
0868: }
0869:
0870: void checkNodes(BasicNodeList resultNodeList, Query query) {
0871: Authorization auth = BasicCloudContext.mmb.getMMBaseCop()
0872: .getAuthorization();
0873: resultNodeList.autoConvert = false; // make sure no conversion to Node happen, until we are ready.
0874:
0875: if (log.isTraceEnabled()) {
0876: log.trace(resultNodeList);
0877: }
0878:
0879: log.debug("Starting read-check");
0880: // resultNodeList is now a BasicNodeList; read restriction should only be applied now
0881: // assumed is though, that it contain _only_ MMObjectNodes..
0882:
0883: // get authorization for this call only
0884:
0885: List<Step> steps = query.getSteps();
0886: Step nodeStep = null;
0887: if (query instanceof NodeQuery) {
0888: nodeStep = ((NodeQuery) query).getNodeStep();
0889: }
0890: log.debug("Creating iterator");
0891: ListIterator<Node> li = resultNodeList.listIterator();
0892: while (li.hasNext()) {
0893: Node o = li.next();
0894: log.debug("next");
0895: if (log.isDebugEnabled()) {
0896: log.debug(o.getClass().getName());
0897: }
0898: Node node = o;
0899: boolean mayRead = true;
0900: for (int j = 0; mayRead && (j < steps.size()); ++j) {
0901: Step step = steps.get(j);
0902: int nodenr;
0903: if (step.equals(nodeStep)) {
0904: nodenr = node.getIntValue("number");
0905: } else {
0906: String pref = step.getAlias();
0907: if (pref == null) {
0908: pref = step.getTableName();
0909: }
0910: String fn = pref + ".number";
0911: if (node.getNodeManager().hasField(fn)) {
0912: nodenr = node.getIntValue(pref + ".number");
0913: } else {
0914: log.warn("Could not check step " + step
0915: + ". Because, the field '" + fn
0916: + "' is not found in the node " + node
0917: + " which is in the result of "
0918: + query.toSql());
0919: nodenr = -1;
0920: }
0921: }
0922: if (nodenr != -1) {
0923: mayRead = auth.check(userContext, nodenr,
0924: Operation.READ);
0925: }
0926: }
0927:
0928: if (!mayRead) {
0929: li.remove();
0930: }
0931: }
0932: resultNodeList.autoConvert = true;
0933:
0934: }
0935:
0936: /**
0937: * Result with Cluster Nodes (checked security)
0938: * @param query query to execute
0939: * @return lisr of cluster nodes
0940: * @since MMBase-1.7
0941: */
0942: protected NodeList getSecureList(Query query) {
0943:
0944: boolean checked = setSecurityConstraint(query);
0945:
0946: List<MMObjectNode> resultList = getClusterNodes(query);
0947:
0948: if (log.isDebugEnabled()) {
0949: log.debug("Creating NodeList of size " + resultList.size());
0950: }
0951:
0952: // create resultNodeList
0953: NodeManager tempNodeManager = new VirtualNodeManager(query,
0954: this );
0955:
0956: BasicNodeList resultNodeList = new BasicNodeList(resultList,
0957: tempNodeManager);
0958: resultNodeList.setProperty(NodeList.QUERY_PROPERTY, query);
0959:
0960: if (!checked) {
0961: checkNodes(resultNodeList, query);
0962: }
0963:
0964: return resultNodeList;
0965: }
0966:
0967: //javadoc inherited
0968: public NodeList getList(String startNodes, String nodePath,
0969: String fields, String constraints, String orderby,
0970: String directions, String searchDir, boolean distinct) {
0971:
0972: if ((nodePath == null) || nodePath.equals(""))
0973: throw new BridgeException(
0974: "Node path cannot be empty - list at least one nodemanager.");
0975: Query query = Queries.createQuery(this , startNodes, nodePath,
0976: fields, constraints, orderby, directions, searchDir,
0977: distinct);
0978: return getList(query);
0979: }
0980:
0981: public void setLocale(Locale l) {
0982: if (l == null) {
0983: locale = new Locale(BasicCloudContext.mmb.getLanguage(), "");
0984: } else {
0985: locale = l;
0986: }
0987: }
0988:
0989: public Locale getLocale() {
0990: return locale;
0991: }
0992:
0993: public boolean mayRead(int nodeNumber) {
0994: return mayRead(nodeNumber + "");
0995: }
0996:
0997: public boolean mayRead(String nodeNumber) {
0998: MMObjectNode node;
0999: try {
1000: node = BasicCloudContext.tmpObjectManager.getNode(account,
1001: nodeNumber);
1002: } catch (RuntimeException e) {
1003: throw new NotFoundException(
1004: "Something went wrong while getting node with number '"
1005: + nodeNumber + "': " + e.getMessage(), e);
1006: }
1007: if (node == null) {
1008: throw new NotFoundException("Node with number '"
1009: + nodeNumber + "' does not exist.");
1010: } else {
1011: int nodenr = node.getNumber();
1012: if (nodenr == -1) {
1013: return true; // temporary node
1014: } else {
1015: return check(Operation.READ, node.getNumber()); // check read access
1016: }
1017: }
1018: }
1019:
1020: public boolean may(org.mmbase.security.Action action,
1021: org.mmbase.util.functions.Parameters parameters) {
1022: return BasicCloudContext.mmb.getMMBaseCop().getAuthorization()
1023: .check(userContext, action, parameters);
1024: }
1025:
1026: // javadoc inherited
1027: public Query createQuery() {
1028: return new BasicQuery(this );
1029: }
1030:
1031: public NodeQuery createNodeQuery() {
1032: return new BasicNodeQuery(this );
1033: }
1034:
1035: public Query createAggregatedQuery() {
1036: return new BasicQuery(this , true);
1037: }
1038:
1039: /**
1040: * Based on multi-level query. Returns however 'normal' nodes based on the last step. This is a
1041: * protected function, which is used in the implemetnedion of getRelatedNodes, getRelations of
1042: * NodeManager
1043: *
1044: * Before it executes the query, the fields of the query are checked. All and only fields of the
1045: * 'last' NodeManager and the relation should be queried. If fields are present already, but
1046: * not like this, an exception is thrown. If not fields are present, the rights fields are added
1047: * first (if the query is still unused, otherwise trhows Exception).
1048: * @param query query to execute
1049: * @return list of normal nodes
1050: *
1051: * @throws BridgeException If wrong fields in query or could not be added.
1052: *
1053: * @since MMBase-1.7
1054: */
1055: protected NodeList getLastStepList(Query query) {
1056: return null;
1057: }
1058:
1059: /**
1060: * Compares this cloud to the passed object.
1061: * Returns 0 if they are equal, -1 if the object passed is a Cloud and larger than this cloud,
1062: * and +1 if the object passed is a Cloud and smaller than this cloud.
1063: * @todo There is no specific order in which clouds are ordered at this moment.
1064: * Currently, all clouds within one CloudContext are treated as being equal.
1065: * @param o the object to compare it with
1066: * @return compare number
1067: */
1068: public int compareTo(Cloud o) {
1069: int h1 = o.getCloudContext().hashCode();
1070: int h2 = cloudContext.hashCode();
1071:
1072: // mm: why not simply return h2 - h1?
1073:
1074: if (h1 > h2) {
1075: return -1;
1076: } else if (h1 < h2) {
1077: return 1;
1078: } else {
1079: return 0;
1080: }
1081: }
1082:
1083: /**
1084: * Compares this cloud to the passed object, and returns true if they are equal.
1085: * @todo Add checks for multiple clouds in the same cloudcontext
1086: * Currently, all clouds within one CloudContext are treated as being equal.
1087: * @param o the object to compare it with
1088: * @return is equal
1089: */
1090: @Override
1091: public boolean equals(Object o) {
1092: // XXX: Currently, all clouds (i.e. transactions/user clouds) within a CloudContext
1093: // are treated as the 'same' cloud. This may change in future implementations
1094: if (o instanceof Cloud) {
1095: Cloud oc = (Cloud) o;
1096: return cloudContext.equals(oc.getCloudContext())
1097: && userContext.equals(oc.getUser());
1098: } else {
1099: return false;
1100: }
1101: }
1102:
1103: public Object getProperty(Object key) {
1104: return properties.get(key);
1105: }
1106:
1107: public void setProperty(Object key, Object value) {
1108: properties.put(key, value);
1109: }
1110:
1111: public Map<Object, Object> getProperties() {
1112: return Collections.unmodifiableMap(properties);
1113: }
1114:
1115: public Collection<Function<?>> getFunctions(String setName) {
1116: FunctionSet set = FunctionSets.getFunctionSet(setName);
1117: if (set == null) {
1118: throw new NotFoundException("Functionset with name "
1119: + setName + "does not exist.");
1120: }
1121: // get functions
1122: return set.getFunctions();
1123: }
1124:
1125: public Function getFunction(String setName, String functionName) {
1126: FunctionSet set = FunctionSets.getFunctionSet(setName);
1127: if (set == null) {
1128: throw new NotFoundException("Functionset with name '"
1129: + setName + "' does not exist.");
1130: }
1131: Function fun = set.getFunction(functionName);
1132: if (fun == null) {
1133: throw new NotFoundException("Function with name '"
1134: + functionName
1135: + "' does not exist in function set with name '"
1136: + setName + "'.");
1137: }
1138: return fun;
1139: }
1140:
1141: public NodeList createNodeList() {
1142: return new BasicNodeList(BridgeCollections.EMPTY_NODELIST, this );
1143: }
1144:
1145: public RelationList createRelationList() {
1146: return new BasicRelationList(
1147: BridgeCollections.EMPTY_RELATIONLIST, this );
1148: }
1149:
1150: public NodeManagerList createNodeManagerList() {
1151: return new BasicNodeManagerList(
1152: BridgeCollections.EMPTY_NODEMANAGERLIST, this );
1153: }
1154:
1155: public RelationManagerList createRelationManagerList() {
1156: return new BasicRelationManagerList(
1157: BridgeCollections.EMPTY_RELATIONMANAGERLIST, this );
1158: }
1159:
1160: /**
1161: * Checks wether the current transaction contains the given node.
1162: */
1163: boolean contains(MMObjectNode node) {
1164: return false;
1165: }
1166:
1167: /**
1168: * Ignored by basic cloud. See {@link BasicTransaction#add(String)}.
1169: */
1170: void add(String currentObjectContext) {
1171: }
1172:
1173: /**
1174: * Ignored by basic cloud. See {@link BasicTransaction#add(String)}.
1175: */
1176: int add(BasicNode node) {
1177: return node.getNumber();
1178: }
1179:
1180: /**
1181: * Throws exception if node alias already exists
1182: * @since MMBase-1.8.4
1183: */
1184: protected void checkAlias(String aliasName) {
1185: Node otherNode = hasNode(aliasName) ? getNode(aliasName) : null;
1186: if (otherNode != null) {
1187: throw new BridgeException("Alias " + aliasName
1188: + " could not be created. It is an alias for "
1189: + otherNode.getNodeManager().getName() + " node "
1190: + otherNode.getNumber() + " already");
1191: }
1192: }
1193:
1194: void createAlias(BasicNode node, String aliasName) {
1195: checkAlias(aliasName);
1196: String owner = getUser().getOwnerField();
1197: if (!node.getNode().getBuilder().createAlias(node.getNumber(),
1198: aliasName, owner)) {
1199: Node otherNode = getNode(aliasName);
1200: if (otherNode != null) {
1201: throw new BridgeException("Alias " + aliasName
1202: + " could not be created. It is an alias for "
1203: + otherNode.getNodeManager().getName()
1204: + " node " + otherNode.getNumber() + " already");
1205: } else {
1206: throw new BridgeException("Alias " + aliasName
1207: + " could not be created.");
1208: }
1209: }
1210: }
1211:
1212: /**
1213: * Ignored by basic cloud. See {@link BasicTransaction#remove(String)}.
1214: */
1215: void remove(String currentObjectContext) {
1216: }
1217:
1218: void remove(MMObjectNode node) {
1219: node.remove(getUser());
1220: }
1221:
1222: protected void _readObject(ObjectInputStream in)
1223: throws IOException, ClassNotFoundException {
1224: name = in.readUTF();
1225: userContext = (UserContext) in.readObject();
1226: cloudContext = LocalContext.getCloudContext();
1227: description = name;
1228: properties = (HashMap) in.readObject();
1229: locale = (Locale) in.readObject();
1230: log.info("Reading " + this );
1231: org.mmbase.util.ThreadPools.jobsExecutor
1232: .execute(new BasicCloudStarter());
1233: transactions = new HashMap<String, Transaction>();
1234: nodeManagerCache = new HashMap<String, BasicNodeManager>();
1235: }
1236:
1237: private void readObject(ObjectInputStream in) throws IOException,
1238: ClassNotFoundException {
1239: _readObject(in);
1240: }
1241:
1242: protected void _writeObject(ObjectOutputStream out)
1243: throws IOException {
1244: out.writeUTF(name);
1245: out.writeObject(userContext);
1246: HashMap<Object, Object> props = new HashMap<Object, Object>();
1247: Iterator i = properties.entrySet().iterator();
1248: while (i.hasNext()) {
1249: Map.Entry entry = (Map.Entry) i.next();
1250: Object key = entry.getKey();
1251: Object value = entry.getValue();
1252: if ((key instanceof Serializable)
1253: && (value instanceof Serializable)) {
1254: props.put(key, value);
1255: }
1256: }
1257: out.writeObject(props);
1258: out.writeObject(locale);
1259: log.service("Serialized cloud " + BasicCloud.this + " of "
1260: + BasicCloud.this .getUser());
1261: }
1262:
1263: private void writeObject(ObjectOutputStream out) throws IOException {
1264: _writeObject(out);
1265: }
1266:
1267: class BasicCloudStarter implements Runnable {
1268: public void run() {
1269: synchronized (BasicCloud.this ) {
1270: LocalContext.getCloudContext().assertUp();
1271: BasicCloud.this .init();
1272: if (BasicCloud.this .userContext == null) {
1273: throw new java.lang.SecurityException(
1274: "Login invalid: did not supply user object");
1275: }
1276: if (BasicCloud.this .userContext.getAuthenticationType() == null) {
1277: log
1278: .warn("Security implementation did not set 'authentication type' in the user object.");
1279: }
1280: log.service("Deserialized " + BasicCloud.this );
1281: }
1282: }
1283: }
1284:
1285: @Override
1286: public String toString() {
1287: String n = getClass().getName();
1288: int dot = n.lastIndexOf(".");
1289: if (dot > 0) {
1290: n = n.substring(dot + 1);
1291: }
1292: UserContext uc = getUser();
1293: return n + " '" + getName() + "' of "
1294: + (uc != null ? uc.getIdentifier() : "NO USER YET")
1295: + " @" + Integer.toHexString(hashCode());
1296: }
1297: }
|