001package de.deepamehta.core.impl; 002 003import de.deepamehta.core.Association; 004import de.deepamehta.core.AssociationType; 005import de.deepamehta.core.DeepaMehtaObject; 006import de.deepamehta.core.ChildTopics; 007import de.deepamehta.core.RelatedTopic; 008import de.deepamehta.core.Topic; 009import de.deepamehta.core.TopicType; 010import de.deepamehta.core.service.Directives; 011import de.deepamehta.core.service.DirectivesResponse; 012 013import com.sun.jersey.spi.container.ContainerRequest; 014import com.sun.jersey.spi.container.ContainerResponse; 015import com.sun.jersey.spi.container.ContainerResponseFilter; 016 017import java.lang.reflect.Type; 018import java.lang.reflect.ParameterizedType; 019import java.util.List; 020import java.util.logging.Logger; 021 022 023 024class JerseyResponseFilter implements ContainerResponseFilter { 025 026 // ---------------------------------------------------------------------------------------------- Instance Variables 027 028 private EventManager em; 029 030 private Logger logger = Logger.getLogger(getClass().getName()); 031 032 // ---------------------------------------------------------------------------------------------------- Constructors 033 034 JerseyResponseFilter(EventManager em) { 035 this.em = em; 036 } 037 038 // -------------------------------------------------------------------------------------------------- Public Methods 039 040 @Override 041 public ContainerResponse filter(ContainerRequest request, ContainerResponse response) { 042 try { 043 em.fireEvent(CoreEvent.SERVICE_RESPONSE_FILTER, response); 044 // 045 Object entity = response.getEntity(); 046 boolean includeChilds = getIncludeChilds(request); 047 boolean includeAssocChilds = getIncludeAssocChilds(request); 048 if (entity != null) { 049 // 050 // 1) Loading child topics 051 // ### TODO: move to Webservice module? 052 if (entity instanceof DeepaMehtaObject) { 053 loadChildTopics((DeepaMehtaObject) entity, includeChilds, includeAssocChilds); 054 } else if (isIterable(response, DeepaMehtaObject.class)) { 055 loadChildTopics((Iterable<DeepaMehtaObject>) entity, includeChilds, includeAssocChilds); 056 } 057 // 058 // 2) Firing PRE_SEND events 059 // ### TODO: move to Webservice module? 060 if (entity instanceof DeepaMehtaObject) { 061 firePreSend((DeepaMehtaObject) entity); 062 } else if (isIterable(response, DeepaMehtaObject.class)) { 063 firePreSend((Iterable<DeepaMehtaObject>) entity); 064 } else if (entity instanceof DirectivesResponse) { 065 firePreSend(((DirectivesResponse) entity).getObject()); 066 // 067 // Note: some plugins rely on the PRE_SEND event to be fired for the individual DeepaMehta 068 // objects contained in the set of directives. E.g. the Time plugin enriches updated objects 069 // with timestamps. The timestamps in turn are needed at client-side by the Caching plugin 070 // in order to issue conditional PUT requests. 071 // ### TODO: don't fire PRE_SEND events for the individual directives but only for the wrapped 072 // DeepaMehtaObject? Let the update() Core Service calls return the updated object? 073 firePreSend(((DirectivesResponse) entity).getDirectives()); 074 } 075 } 076 // 077 Directives.remove(); 078 // 079 return response; 080 } catch (Exception e) { 081 throw new RuntimeException("Response filtering failed", e); 082 } 083 } 084 085 // ------------------------------------------------------------------------------------------------- Private Methods 086 087 // === Loading child topics === 088 089 private void loadChildTopics(DeepaMehtaObject object, boolean includeChilds, 090 boolean includeAssocChilds) { 091 if (includeChilds) { 092 object.loadChildTopics(); 093 if (includeAssocChilds) { 094 loadRelatingAssociationChildTopics(object); 095 } 096 } 097 } 098 099 private void loadChildTopics(Iterable<DeepaMehtaObject> objects, boolean includeChilds, 100 boolean includeAssocChilds) { 101 for (DeepaMehtaObject object : objects) { 102 loadChildTopics(object, includeChilds, includeChilds); 103 } 104 } 105 106 // --- 107 108 private void loadRelatingAssociationChildTopics(DeepaMehtaObject object) { 109 ChildTopics childTopics = object.getChildTopics(); 110 for (String childTypeUri : childTopics) { 111 Object value = childTopics.get(childTypeUri); 112 if (value instanceof RelatedTopic) { 113 RelatedTopic childTopic = (RelatedTopic) value; 114 childTopic.getRelatingAssociation().loadChildTopics(); 115 loadRelatingAssociationChildTopics(childTopic); // recursion 116 } else if (value instanceof List) { 117 for (RelatedTopic childTopic : (List<RelatedTopic>) value) { 118 childTopic.getRelatingAssociation().loadChildTopics(); 119 loadRelatingAssociationChildTopics(childTopic); // recursion 120 } 121 } else { 122 throw new RuntimeException("Unexpected \"" + childTypeUri + "\" value in ChildTopics: " + value); 123 } 124 } 125 } 126 127 // === Firing PRE_SEND events === 128 129 private void firePreSend(DeepaMehtaObject object) { 130 if (object instanceof TopicType) { // Note: must take precedence over topic 131 em.fireEvent(CoreEvent.PRE_SEND_TOPIC_TYPE, object); 132 } else if (object instanceof AssociationType) { // Note: must take precedence over topic 133 em.fireEvent(CoreEvent.PRE_SEND_ASSOCIATION_TYPE, object); 134 } else if (object instanceof Topic) { 135 em.fireEvent(CoreEvent.PRE_SEND_TOPIC, object); 136 } else if (object instanceof Association) { 137 em.fireEvent(CoreEvent.PRE_SEND_ASSOCIATION, object); 138 } 139 } 140 141 private void firePreSend(Iterable<DeepaMehtaObject> objects) { 142 for (DeepaMehtaObject object : objects) { 143 firePreSend(object); 144 } 145 } 146 147 private void firePreSend(Directives directives) { 148 for (Directives.Entry entry : directives) { 149 switch (entry.dir) { 150 case UPDATE_TOPIC: 151 case UPDATE_ASSOCIATION: 152 case UPDATE_TOPIC_TYPE: 153 case UPDATE_ASSOCIATION_TYPE: 154 firePreSend((DeepaMehtaObject) entry.arg); 155 break; 156 } 157 } 158 } 159 160 // === Helper === 161 162 private boolean isIterable(ContainerResponse response, Class elementType) { 163 Type genericType = response.getEntityType(); 164 if (genericType instanceof ParameterizedType) { 165 Type[] typeArgs = ((ParameterizedType) genericType).getActualTypeArguments(); 166 Class<?> type = response.getEntity().getClass(); 167 if (typeArgs.length == 1 && Iterable.class.isAssignableFrom(type) && 168 elementType.isAssignableFrom((Class) typeArgs[0])) { 169 return true; 170 } 171 } 172 return false; 173 } 174 175 // --- 176 177 private boolean getIncludeChilds(ContainerRequest request) { 178 return getBooleanQueryParameter(request, "include_childs"); 179 } 180 181 private boolean getIncludeAssocChilds(ContainerRequest request) { 182 return getBooleanQueryParameter(request, "include_assoc_childs"); 183 } 184 185 // --- 186 187 private boolean getBooleanQueryParameter(ContainerRequest request, String param) { 188 return Boolean.parseBoolean(request.getQueryParameters().getFirst(param)); 189 } 190}