001 package de.deepamehta.core.impl; 002 003 import de.deepamehta.core.Association; 004 import de.deepamehta.core.AssociationDefinition; 005 import de.deepamehta.core.DeepaMehtaObject; 006 import de.deepamehta.core.RelatedTopic; 007 import de.deepamehta.core.Topic; 008 import de.deepamehta.core.Type; 009 import de.deepamehta.core.model.CompositeValueModel; 010 import de.deepamehta.core.model.DeepaMehtaObjectModel; 011 import de.deepamehta.core.model.RelatedTopicModel; 012 import de.deepamehta.core.model.SimpleValue; 013 import de.deepamehta.core.model.TopicModel; 014 import de.deepamehta.core.service.ClientState; 015 import de.deepamehta.core.service.Directives; 016 import de.deepamehta.core.service.ResultList; 017 import de.deepamehta.core.storage.spi.DeepaMehtaTransaction; 018 019 import org.codehaus.jettison.json.JSONObject; 020 021 import java.util.List; 022 import java.util.logging.Logger; 023 024 025 026 /** 027 * A DeepaMehta object model that is attached to the DB. 028 * 029 * Method name conventions and semantics: 030 * - getXX() Reads from memory (model). 031 * - setXX(arg) Writes to memory (model) and DB. Elementary operation. 032 * - updateXX(arg) Compares arg with current value (model) and calls setXX() method(s) if required. 033 * Can be called with arg=null which indicates no update is requested. 034 * Typically returns nothing. 035 * - fetchXX() Fetches value from DB. ### FIXDOC 036 * - storeXX() Stores current value (model) to DB. ### FIXDOC 037 */ 038 abstract class AttachedDeepaMehtaObject implements DeepaMehtaObject { 039 040 // ---------------------------------------------------------------------------------------------- Instance Variables 041 042 private DeepaMehtaObjectModel model; // underlying model 043 044 private AttachedCompositeValue childTopics; // attached object cache 045 046 protected final EmbeddedService dms; 047 048 private Logger logger = Logger.getLogger(getClass().getName()); 049 050 // ---------------------------------------------------------------------------------------------------- Constructors 051 052 AttachedDeepaMehtaObject(DeepaMehtaObjectModel model, EmbeddedService dms) { 053 this.model = model; 054 this.dms = dms; 055 this.childTopics = new AttachedCompositeValue(model.getCompositeValueModel(), this, dms); 056 } 057 058 // -------------------------------------------------------------------------------------------------- Public Methods 059 060 061 062 // *************************************** 063 // *** DeepaMehtaObject Implementation *** 064 // *************************************** 065 066 067 068 // === Model === 069 070 // --- ID --- 071 072 @Override 073 public long getId() { 074 return model.getId(); 075 } 076 077 // --- URI --- 078 079 @Override 080 public String getUri() { 081 return model.getUri(); 082 } 083 084 @Override 085 public void setUri(String uri) { 086 // update memory 087 model.setUri(uri); 088 // update DB 089 storeUri(); // abstract 090 } 091 092 // --- Type URI --- 093 094 @Override 095 public String getTypeUri() { 096 return model.getTypeUri(); 097 } 098 099 @Override 100 public void setTypeUri(String typeUri) { 101 // update memory 102 model.setTypeUri(typeUri); 103 // update DB 104 storeTypeUri(); // abstract 105 } 106 107 // --- Simple Value --- 108 109 @Override 110 public SimpleValue getSimpleValue() { 111 return model.getSimpleValue(); 112 } 113 114 // --- 115 116 @Override 117 public void setSimpleValue(String value) { 118 setSimpleValue(new SimpleValue(value)); 119 } 120 121 @Override 122 public void setSimpleValue(int value) { 123 setSimpleValue(new SimpleValue(value)); 124 } 125 126 @Override 127 public void setSimpleValue(long value) { 128 setSimpleValue(new SimpleValue(value)); 129 } 130 131 @Override 132 public void setSimpleValue(boolean value) { 133 setSimpleValue(new SimpleValue(value)); 134 } 135 136 @Override 137 public void setSimpleValue(SimpleValue value) { 138 dms.valueStorage.setSimpleValue(getModel(), value); 139 } 140 141 // --- Composite Value --- 142 143 @Override 144 public AttachedCompositeValue getCompositeValue() { 145 return childTopics; 146 } 147 148 @Override 149 public void setCompositeValue(CompositeValueModel comp, ClientState clientState, Directives directives) { 150 DeepaMehtaTransaction tx = dms.beginTx(); // ### TODO: only resource methods should create a transaction 151 try { 152 getCompositeValue().update(comp, clientState, directives); 153 tx.success(); 154 } catch (Exception e) { 155 logger.warning("ROLLBACK!"); 156 throw new RuntimeException("Setting composite value failed (" + comp + ")", e); 157 } finally { 158 tx.finish(); 159 } 160 } 161 162 // --- 163 164 @Override 165 public void loadChildTopics() { 166 getCompositeValue().loadChildTopics(); 167 } 168 169 @Override 170 public void loadChildTopics(String childTypeUri) { 171 getCompositeValue().loadChildTopics(childTypeUri); 172 } 173 174 // --- 175 176 @Override 177 public DeepaMehtaObjectModel getModel() { 178 return model; 179 } 180 181 182 183 // === Updating === 184 185 @Override 186 public void update(DeepaMehtaObjectModel newModel, ClientState clientState, Directives directives) { 187 updateUri(newModel.getUri()); 188 updateTypeUri(newModel.getTypeUri()); 189 updateValue(newModel, clientState, directives); 190 } 191 192 // --- 193 194 @Override 195 public void updateChildTopic(TopicModel newChildTopic, AssociationDefinition assocDef, 196 ClientState clientState, Directives directives) { 197 getCompositeValue().updateChildTopics(newChildTopic, null, assocDef, clientState, directives); 198 } 199 200 @Override 201 public void updateChildTopics(List<TopicModel> newChildTopics, AssociationDefinition assocDef, 202 ClientState clientState, Directives directives) { 203 getCompositeValue().updateChildTopics(null, newChildTopics, assocDef, clientState, directives); 204 } 205 206 207 208 // === Deletion === 209 210 /** 211 * Deletes all sub-topics of this DeepaMehta object (associated via "dm4.core.composition", recursively) and 212 * deletes all the remaining direct associations of this DeepaMehta object. 213 * <p> 214 * Note: deletion of the object itself is up to the subclasses. 215 */ 216 @Override 217 public void delete(Directives directives) { 218 // Note: directives must be not null. 219 // The subclass's delete() methods add DELETE_TOPIC and DELETE_ASSOCIATION directives to it respectively. 220 if (directives == null) { 221 throw new IllegalArgumentException("directives is null"); 222 } 223 // 1) recursively delete sub-topics 224 ResultList<RelatedTopic> childTopics = getRelatedTopics("dm4.core.composition", 225 "dm4.core.parent", "dm4.core.child", null, false, false, 0); 226 for (Topic childTopic : childTopics) { 227 childTopic.delete(directives); 228 } 229 // 2) delete direct associations 230 for (Association assoc : getAssociations()) { // getAssociations() is abstract 231 assoc.delete(directives); 232 } 233 } 234 235 236 237 // === Traversal === 238 239 // --- Topic Retrieval --- 240 241 @Override 242 public RelatedTopic getRelatedTopic(String assocTypeUri, String myRoleTypeUri, String othersRoleTypeUri, 243 String othersTopicTypeUri, boolean fetchComposite, 244 boolean fetchRelatingComposite) { 245 RelatedTopicModel topic = fetchRelatedTopic(assocTypeUri, myRoleTypeUri, othersRoleTypeUri, othersTopicTypeUri); 246 // fetchRelatedTopic() is abstract 247 return topic != null ? dms.instantiateRelatedTopic(topic, fetchComposite, fetchRelatingComposite) : null; 248 } 249 250 @Override 251 public ResultList<RelatedTopic> getRelatedTopics(String assocTypeUri, int maxResultSize) { 252 return getRelatedTopics(assocTypeUri, null, null, null, false, false, maxResultSize); 253 } 254 255 @Override 256 public ResultList<RelatedTopic> getRelatedTopics(String assocTypeUri, String myRoleTypeUri, 257 String othersRoleTypeUri, String othersTopicTypeUri, 258 boolean fetchComposite, boolean fetchRelatingComposite, int maxResultSize) { 259 ResultList<RelatedTopicModel> topics = fetchRelatedTopics(assocTypeUri, myRoleTypeUri, othersRoleTypeUri, 260 othersTopicTypeUri, maxResultSize); // fetchRelatedTopics() is abstract 261 return dms.instantiateRelatedTopics(topics, fetchComposite, fetchRelatingComposite); 262 } 263 264 // Note: this method is implemented in the subclasses (this is an abstract class): 265 // getRelatedTopics(List assocTypeUris, ...) 266 267 // --- Association Retrieval --- 268 269 // Note: these methods are implemented in the subclasses (this is an abstract class): 270 // getAssociation(...); 271 // getAssociations(); 272 273 274 275 // ********************************** 276 // *** JSONEnabled Implementation *** 277 // ********************************** 278 279 280 281 @Override 282 public JSONObject toJSON() { 283 return model.toJSON(); 284 } 285 286 287 288 // **************** 289 // *** Java API *** 290 // **************** 291 292 293 294 @Override 295 public boolean equals(Object o) { 296 return ((AttachedDeepaMehtaObject) o).model.equals(model); 297 } 298 299 @Override 300 public int hashCode() { 301 return model.hashCode(); 302 } 303 304 @Override 305 public String toString() { 306 return model.toString(); 307 } 308 309 310 311 // ----------------------------------------------------------------------------------------- Package Private Methods 312 313 abstract String className(); 314 315 // ### TODO: Directive getUpdateDirective() 316 abstract void addUpdateDirective(Directives directives); 317 318 abstract void storeUri(); 319 320 abstract void storeTypeUri(); 321 322 // --- 323 324 abstract RelatedTopicModel fetchRelatedTopic(String assocTypeUri, String myRoleTypeUri, 325 String othersRoleTypeUri, String othersTopicTypeUri); 326 327 abstract ResultList<RelatedTopicModel> fetchRelatedTopics(String assocTypeUri, String myRoleTypeUri, 328 String othersRoleTypeUri, String othersTopicTypeUri, int maxResultSize); 329 330 // --- 331 332 // ### TODO: add to public interface 333 Type getType() { 334 return dms.valueStorage.getType(getModel()); 335 } 336 337 // ------------------------------------------------------------------------------------------------- Private Methods 338 339 340 341 // === Update === 342 343 private void updateUri(String newUri) { 344 // abort if no update is requested 345 if (newUri == null) { 346 return; 347 } 348 // 349 String uri = getUri(); 350 if (!uri.equals(newUri)) { 351 logger.info("### Changing URI of " + className() + " " + getId() + 352 " from \"" + uri + "\" -> \"" + newUri + "\""); 353 setUri(newUri); 354 } 355 } 356 357 private void updateTypeUri(String newTypeUri) { 358 // abort if no update is requested 359 if (newTypeUri == null) { 360 return; 361 } 362 // 363 String typeUri = getTypeUri(); 364 if (!typeUri.equals(newTypeUri)) { 365 logger.info("### Changing type URI of " + className() + " " + getId() + 366 " from \"" + typeUri + "\" -> \"" + newTypeUri + "\""); 367 setTypeUri(newTypeUri); 368 } 369 } 370 371 private void updateValue(DeepaMehtaObjectModel newModel, ClientState clientState, Directives directives) { 372 if (getType().getDataTypeUri().equals("dm4.core.composite")) { 373 getCompositeValue().update(newModel.getCompositeValueModel(), clientState, directives); 374 } else { 375 updateSimpleValue(newModel.getSimpleValue()); 376 } 377 } 378 379 private void updateSimpleValue(SimpleValue newValue) { 380 // abort if no update is requested 381 if (newValue == null) { 382 return; 383 } 384 // 385 SimpleValue value = getSimpleValue(); 386 if (!value.equals(newValue)) { 387 logger.info("### Changing simple value of " + className() + " " + getId() + 388 " from \"" + value + "\" -> \"" + newValue + "\""); 389 setSimpleValue(newValue); 390 } 391 } 392 }