001 package de.deepamehta.core.impl; 002 003 import de.deepamehta.core.Association; 004 import de.deepamehta.core.AssociationType; 005 import de.deepamehta.core.DeepaMehtaObject; 006 import de.deepamehta.core.ChildTopics; 007 import de.deepamehta.core.RelatedTopic; 008 import de.deepamehta.core.Topic; 009 import de.deepamehta.core.TopicType; 010 import de.deepamehta.core.service.Directives; 011 012 import com.sun.jersey.spi.container.ContainerRequest; 013 import com.sun.jersey.spi.container.ContainerResponse; 014 import com.sun.jersey.spi.container.ContainerResponseFilter; 015 016 import java.lang.reflect.Type; 017 import java.lang.reflect.ParameterizedType; 018 import java.util.List; 019 import java.util.logging.Logger; 020 021 022 023 class JerseyResponseFilter implements ContainerResponseFilter { 024 025 // ---------------------------------------------------------------------------------------------- Instance Variables 026 027 private EmbeddedService dms; 028 029 private Logger logger = Logger.getLogger(getClass().getName()); 030 031 // ---------------------------------------------------------------------------------------------------- Constructors 032 033 JerseyResponseFilter(EmbeddedService dms) { 034 this.dms = dms; 035 } 036 037 // -------------------------------------------------------------------------------------------------- Public Methods 038 039 @Override 040 public ContainerResponse filter(ContainerRequest request, ContainerResponse response) { 041 try { 042 dms.fireEvent(CoreEvent.SERVICE_RESPONSE_FILTER, response); 043 // 044 Object entity = response.getEntity(); 045 boolean includeChilds = getIncludeChilds(request); 046 boolean includeAssocChilds = getIncludeAssocChilds(request); 047 if (entity != null) { 048 // 049 // 1) Loading child topics 050 if (entity instanceof DeepaMehtaObject) { 051 loadChildTopics((DeepaMehtaObject) entity, includeChilds, includeAssocChilds); 052 } else if (isIterable(response, DeepaMehtaObject.class)) { 053 loadChildTopics((Iterable<DeepaMehtaObject>) entity, includeChilds, includeAssocChilds); 054 } 055 // 056 // 2) Firing PRE_SEND events 057 if (entity instanceof TopicType) { // Note: must take precedence over topic 058 firePreSend((TopicType) entity); 059 } else if (entity instanceof AssociationType) { 060 firePreSend((AssociationType) entity); // Note: must take precedence over topic 061 } else if (entity instanceof Topic) { 062 firePreSend((Topic) entity); 063 } else if (entity instanceof Association) { 064 firePreSend((Association) entity); 065 } else if (entity instanceof Directives) { 066 // Note: some plugins rely on the PRE_SEND event in order to enrich updated objects, others don't. 067 // E.g. the Access Control plugin must enrich updated objects with permission information. 068 // ### TODO: check if this is still required. Meanwhile permissions are not an enrichment anymore. 069 // ### Update: Yes, it is still required, e.g. by the Time plugin when enriching with timestamps. 070 firePreSend((Directives) entity); 071 } else if (isIterable(response, TopicType.class)) { 072 firePreSendTopicTypes((Iterable<TopicType>) entity); 073 } else if (isIterable(response, AssociationType.class)) { 074 firePreSendAssociationTypes((Iterable<AssociationType>) entity); 075 } else if (isIterable(response, Topic.class)) { 076 firePreSendTopics((Iterable<Topic>) entity); 077 // ### FIXME: for Iterable<Association> no PRE_SEND_ASSOCIATION events are fired 078 } 079 } 080 // 081 logger.fine("### Removing tread-local directives"); 082 Directives.remove(); 083 // 084 return response; 085 } catch (Exception e) { 086 throw new RuntimeException("Jersey response filtering failed", e); 087 } 088 } 089 090 // ------------------------------------------------------------------------------------------------- Private Methods 091 092 // === Loading child topics === 093 094 private void loadChildTopics(DeepaMehtaObject object, boolean includeChilds, 095 boolean includeAssocChilds) { 096 if (includeChilds) { 097 object.loadChildTopics(); 098 if (includeAssocChilds) { 099 loadRelatingAssociationChildTopics(object); 100 } 101 } 102 } 103 104 private void loadChildTopics(Iterable<DeepaMehtaObject> objects, boolean includeChilds, 105 boolean includeAssocChilds) { 106 if (includeChilds) { 107 for (DeepaMehtaObject object : objects) { 108 object.loadChildTopics(); 109 } 110 if (includeAssocChilds) { 111 for (DeepaMehtaObject object : objects) { 112 loadRelatingAssociationChildTopics(object); 113 } 114 } 115 } 116 } 117 118 // --- 119 120 private void loadRelatingAssociationChildTopics(DeepaMehtaObject object) { 121 ChildTopics childTopics = object.getChildTopics(); 122 for (String childTypeUri : childTopics) { 123 Object value = childTopics.get(childTypeUri); 124 if (value instanceof RelatedTopic) { 125 RelatedTopic childTopic = (RelatedTopic) value; 126 childTopic.getRelatingAssociation().loadChildTopics(); 127 loadRelatingAssociationChildTopics(childTopic); // recursion 128 } else if (value instanceof List) { 129 for (RelatedTopic childTopic : (List<RelatedTopic>) value) { 130 childTopic.getRelatingAssociation().loadChildTopics(); 131 loadRelatingAssociationChildTopics(childTopic); // recursion 132 } 133 } else { 134 throw new RuntimeException("Unexpected \"" + childTypeUri + "\" value in ChildTopics: " + value); 135 } 136 } 137 } 138 139 // === Firing PRE_SEND events === 140 141 private void firePreSend(Topic topic) { 142 dms.fireEvent(CoreEvent.PRE_SEND_TOPIC, topic); 143 } 144 145 private void firePreSend(Association assoc) { 146 dms.fireEvent(CoreEvent.PRE_SEND_ASSOCIATION, assoc); 147 } 148 149 private void firePreSend(TopicType topicType) { 150 dms.fireEvent(CoreEvent.PRE_SEND_TOPIC_TYPE, topicType); 151 } 152 153 private void firePreSend(AssociationType assocType) { 154 dms.fireEvent(CoreEvent.PRE_SEND_ASSOCIATION_TYPE, assocType); 155 } 156 157 private void firePreSend(Directives directives) { 158 for (Directives.Entry entry : directives) { 159 switch (entry.dir) { 160 case UPDATE_TOPIC: 161 firePreSend((Topic) entry.arg); 162 break; 163 case UPDATE_ASSOCIATION: 164 firePreSend((Association) entry.arg); 165 break; 166 case UPDATE_TOPIC_TYPE: 167 firePreSend((TopicType) entry.arg); 168 break; 169 case UPDATE_ASSOCIATION_TYPE: 170 firePreSend((AssociationType) entry.arg); 171 break; 172 } 173 } 174 } 175 176 private void firePreSendTopics(Iterable<Topic> topics) { 177 for (Topic topic : topics) { 178 firePreSend(topic); 179 } 180 } 181 182 private void firePreSendTopicTypes(Iterable<TopicType> topicTypes) { 183 for (TopicType topicType : topicTypes) { 184 firePreSend(topicType); 185 } 186 } 187 188 private void firePreSendAssociationTypes(Iterable<AssociationType> assocTypes) { 189 for (AssociationType assocType : assocTypes) { 190 firePreSend(assocType); 191 } 192 } 193 194 // === Helper === 195 196 private boolean isIterable(ContainerResponse response, Class elementType) { 197 Type genericType = response.getEntityType(); 198 if (genericType instanceof ParameterizedType) { 199 Type[] typeArgs = ((ParameterizedType) genericType).getActualTypeArguments(); 200 Class<?> type = response.getEntity().getClass(); 201 if (typeArgs.length == 1 && Iterable.class.isAssignableFrom(type) && 202 elementType.isAssignableFrom((Class) typeArgs[0])) { 203 return true; 204 } 205 } 206 return false; 207 } 208 209 // --- 210 211 private boolean getIncludeChilds(ContainerRequest request) { 212 return getBooleanQueryParameter(request, "include_childs"); 213 } 214 215 private boolean getIncludeAssocChilds(ContainerRequest request) { 216 return getBooleanQueryParameter(request, "include_assoc_childs"); 217 } 218 219 // --- 220 221 private boolean getBooleanQueryParameter(ContainerRequest request, String param) { 222 return Boolean.parseBoolean(request.getQueryParameters().getFirst(param)); 223 } 224 }