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.Topic; 007 import de.deepamehta.core.TopicType; 008 import de.deepamehta.core.service.Directives; 009 010 import com.sun.jersey.spi.container.ContainerRequest; 011 import com.sun.jersey.spi.container.ContainerResponse; 012 import com.sun.jersey.spi.container.ContainerResponseFilter; 013 014 import java.lang.reflect.Type; 015 import java.lang.reflect.ParameterizedType; 016 import java.util.List; 017 import java.util.logging.Logger; 018 019 020 021 class JerseyResponseFilter implements ContainerResponseFilter { 022 023 // ---------------------------------------------------------------------------------------------- Instance Variables 024 025 private EmbeddedService dms; 026 027 private Logger logger = Logger.getLogger(getClass().getName()); 028 029 // ---------------------------------------------------------------------------------------------------- Constructors 030 031 JerseyResponseFilter(EmbeddedService dms) { 032 this.dms = dms; 033 } 034 035 // -------------------------------------------------------------------------------------------------- Public Methods 036 037 @Override 038 public ContainerResponse filter(ContainerRequest request, ContainerResponse response) { 039 try { 040 dms.fireEvent(CoreEvent.SERVICE_RESPONSE_FILTER, response); 041 // 042 Object entity = response.getEntity(); 043 boolean includeChilds = getIncludeChilds(request); 044 if (entity != null) { 045 // 1) Loading child topics 046 if (entity instanceof DeepaMehtaObject) { 047 loadChildTopics((DeepaMehtaObject) entity, includeChilds); 048 } else if (isIterable(response, DeepaMehtaObject.class)) { 049 loadChildTopics((Iterable<DeepaMehtaObject>) entity, includeChilds); 050 } 051 // 2) Firing PRE_SEND events 052 if (entity instanceof TopicType) { // Note: must take precedence over topic 053 firePreSend((TopicType) entity); 054 } else if (entity instanceof AssociationType) { 055 firePreSend((AssociationType) entity); // Note: must take precedence over topic 056 } else if (entity instanceof Topic) { 057 firePreSend((Topic) entity); 058 } else if (entity instanceof Association) { 059 firePreSend((Association) entity); 060 } else if (entity instanceof Directives) { 061 // Note: some plugins rely on the PRE_SEND event in order to enrich updated objects, others don't. 062 // E.g. the Access Control plugin must enrich updated objects with permission information. 063 // ### TODO: check if this is still required. Meanwhile permissions are not an enrichment anymore. 064 // ### Update: Yes, it is still required, e.g. by the Time plugin when enriching with timestamps. 065 firePreSend((Directives) entity); 066 } else if (isIterable(response, TopicType.class)) { 067 firePreSendTopicTypes((Iterable<TopicType>) entity); 068 } else if (isIterable(response, AssociationType.class)) { 069 firePreSendAssociationTypes((Iterable<AssociationType>) entity); 070 } else if (isIterable(response, Topic.class)) { 071 firePreSendTopics((Iterable<Topic>) entity); 072 // ### FIXME: for Iterable<Association> no PRE_SEND_ASSOCIATION events are fired 073 } 074 } 075 // 076 logger.fine("### Removing tread-local directives"); 077 Directives.remove(); 078 // 079 return response; 080 } catch (Exception e) { 081 throw new RuntimeException("Jersey 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 if (includeChilds) { 091 object.loadChildTopics(); 092 } 093 } 094 095 private void loadChildTopics(Iterable<DeepaMehtaObject> objects, boolean includeChilds) { 096 if (includeChilds) { 097 for (DeepaMehtaObject object : objects) { 098 object.loadChildTopics(); 099 } 100 } 101 } 102 103 // === Firing PRE_SEND events === 104 105 private void firePreSend(Topic topic) { 106 dms.fireEvent(CoreEvent.PRE_SEND_TOPIC, topic); 107 } 108 109 private void firePreSend(Association assoc) { 110 dms.fireEvent(CoreEvent.PRE_SEND_ASSOCIATION, assoc); 111 } 112 113 private void firePreSend(TopicType topicType) { 114 dms.fireEvent(CoreEvent.PRE_SEND_TOPIC_TYPE, topicType); 115 } 116 117 private void firePreSend(AssociationType assocType) { 118 dms.fireEvent(CoreEvent.PRE_SEND_ASSOCIATION_TYPE, assocType); 119 } 120 121 private void firePreSend(Directives directives) { 122 for (Directives.Entry entry : directives) { 123 switch (entry.dir) { 124 case UPDATE_TOPIC: 125 firePreSend((Topic) entry.arg); 126 break; 127 case UPDATE_ASSOCIATION: 128 firePreSend((Association) entry.arg); 129 break; 130 case UPDATE_TOPIC_TYPE: 131 firePreSend((TopicType) entry.arg); 132 break; 133 case UPDATE_ASSOCIATION_TYPE: 134 firePreSend((AssociationType) entry.arg); 135 break; 136 } 137 } 138 } 139 140 private void firePreSendTopics(Iterable<Topic> topics) { 141 for (Topic topic : topics) { 142 firePreSend(topic); 143 } 144 } 145 146 private void firePreSendTopicTypes(Iterable<TopicType> topicTypes) { 147 for (TopicType topicType : topicTypes) { 148 firePreSend(topicType); 149 } 150 } 151 152 private void firePreSendAssociationTypes(Iterable<AssociationType> assocTypes) { 153 for (AssociationType assocType : assocTypes) { 154 firePreSend(assocType); 155 } 156 } 157 158 // === Helper === 159 160 private boolean isIterable(ContainerResponse response, Class elementType) { 161 Type genericType = response.getEntityType(); 162 if (genericType instanceof ParameterizedType) { 163 Type[] typeArgs = ((ParameterizedType) genericType).getActualTypeArguments(); 164 Class<?> type = response.getEntity().getClass(); 165 if (typeArgs.length == 1 && Iterable.class.isAssignableFrom(type) && 166 elementType.isAssignableFrom((Class) typeArgs[0])) { 167 return true; 168 } 169 } 170 return false; 171 } 172 173 private boolean getIncludeChilds(ContainerRequest request) { 174 return Boolean.parseBoolean(request.getQueryParameters().getFirst("include_childs")); 175 } 176 }