001 package de.deepamehta.core.impl; 002 003 import de.deepamehta.core.Association; 004 import de.deepamehta.core.AssociationType; 005 import de.deepamehta.core.Topic; 006 import de.deepamehta.core.TopicType; 007 import de.deepamehta.core.service.ClientState; 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 if (entity != null) { 044 ClientState clientState = clientState(request); 045 if (entity instanceof TopicType) { // Note: must take precedence over topic 046 firePreSend((TopicType) entity, clientState); 047 } else if (entity instanceof AssociationType) { 048 firePreSend((AssociationType) entity, clientState); // Note: must take precedence over topic 049 } else if (entity instanceof Topic) { 050 firePreSend((Topic) entity, clientState); 051 } else if (entity instanceof Association) { 052 firePreSend((Association) entity, clientState); 053 } else if (entity instanceof Directives) { 054 // Note: some plugins rely on the PRE_SEND event in order to enrich updated objects, others don't. 055 // E.g. the Access Control plugin must enrich updated objects with permission information. 056 firePreSend((Directives) entity, clientState); 057 } else if (isIterable(response, TopicType.class)) { 058 firePreSendTopicTypes((Iterable<TopicType>) entity, clientState); 059 } else if (isIterable(response, AssociationType.class)) { 060 firePreSendAssociationTypes((Iterable<AssociationType>) entity, clientState); 061 } else if (isIterable(response, Topic.class)) { 062 firePreSendTopics((Iterable<Topic>) entity, clientState); 063 } 064 } 065 return response; 066 } catch (Exception e) { 067 throw new RuntimeException("Jersey response filtering failed", e); 068 } 069 } 070 071 // ------------------------------------------------------------------------------------------------- Private Methods 072 073 private void firePreSend(Topic topic, ClientState clientState) { 074 dms.fireEvent(CoreEvent.PRE_SEND_TOPIC, topic, clientState); 075 } 076 077 private void firePreSend(Association assoc, ClientState clientState) { 078 dms.fireEvent(CoreEvent.PRE_SEND_ASSOCIATION, assoc, clientState); 079 } 080 081 private void firePreSend(TopicType topicType, ClientState clientState) { 082 dms.fireEvent(CoreEvent.PRE_SEND_TOPIC_TYPE, topicType, clientState); 083 } 084 085 private void firePreSend(AssociationType assocType, ClientState clientState) { 086 dms.fireEvent(CoreEvent.PRE_SEND_ASSOCIATION_TYPE, assocType, clientState); 087 } 088 089 private void firePreSend(Directives directives, ClientState clientState) { 090 for (Directives.Entry entry : directives) { 091 switch (entry.dir) { 092 case UPDATE_TOPIC: 093 firePreSend((Topic) entry.arg, clientState); 094 break; 095 case UPDATE_ASSOCIATION: 096 firePreSend((Association) entry.arg, clientState); 097 break; 098 case UPDATE_TOPIC_TYPE: 099 firePreSend((TopicType) entry.arg, clientState); 100 break; 101 case UPDATE_ASSOCIATION_TYPE: 102 firePreSend((AssociationType) entry.arg, clientState); 103 break; 104 } 105 } 106 } 107 108 private void firePreSendTopics(Iterable<Topic> topics, ClientState clientState) { 109 for (Topic topic : topics) { 110 firePreSend(topic, clientState); 111 } 112 } 113 114 private void firePreSendTopicTypes(Iterable<TopicType> topicTypes, ClientState clientState) { 115 for (TopicType topicType : topicTypes) { 116 firePreSend(topicType, clientState); 117 } 118 } 119 120 private void firePreSendAssociationTypes(Iterable<AssociationType> assocTypes, ClientState clientState) { 121 for (AssociationType assocType : assocTypes) { 122 firePreSend(assocType, clientState); 123 } 124 } 125 126 // --- 127 128 private boolean isIterable(ContainerResponse response, Class elementType) { 129 Type genericType = response.getEntityType(); 130 if (genericType instanceof ParameterizedType) { 131 Type[] typeArgs = ((ParameterizedType) genericType).getActualTypeArguments(); 132 Class<?> type = response.getEntity().getClass(); 133 if (typeArgs.length == 1 && Iterable.class.isAssignableFrom(type) && 134 elementType.isAssignableFrom((Class) typeArgs[0])) { 135 return true; 136 } 137 } 138 return false; 139 } 140 141 private ClientState clientState(ContainerRequest request) { 142 List<String> cookies = request.getRequestHeader("Cookie"); 143 if (cookies == null) { 144 return new ClientState(null); 145 } 146 // ### FIXME: does this happen? 147 if (cookies.size() > 1) { 148 throw new RuntimeException("Request contains more than one Cookie header"); 149 } 150 // 151 return new ClientState(cookies.get(0)); 152 } 153 }