package org.oasis.datacore.rest.server.cxf;

import java.util.ArrayList;
import java.util.Map;

import org.apache.cxf.message.Exchange;
import org.oasis.datacore.common.context.DCRequestContextProvider;
import org.oasis.datacore.common.context.SimpleRequestContextProvider;
import org.springframework.stereotype.Component;


/**
 * Impl'd on CXF's Exchange
 * if no Exchange & for unit tests defaults to SimpleRequestContextProvider
 * within DCRequestContextProviderFactory, that must be used to access it.
 * Used also on client side.
 * 
 * @author mdutoo
 *
 */
@Component("datacore.cxfJaxrsApiProvider")
public class CxfRequestContextProvider extends CxfJaxrsApiProvider implements DCRequestContextProvider {
   
   protected DelegateRequestContextProvider delegate = new DelegateRequestContextProvider(this);

   @Override
   public Map<String, Object> getRequestContext() {
      Map<String, Object> existingLocalRequestContext = SimpleRequestContextProvider.getSimpleRequestContext();
      Exchange exchange = this.getExchange();
      if (exchange != null) { // REST !
         ArrayList<Map<String,Object>> fallbacks = new ArrayList<Map<String,Object>>(2);
         if (exchange.getInMessage() != null) {
            fallbacks.add(exchange.getInMessage()); // case of server in request
         } else if (exchange.getOutMessage() != null) {
            fallbacks.add(exchange.getOutMessage()); // case of client out request
         }
         
         // stack on top of existing context if any :
         if (existingLocalRequestContext != null // ex. in REST server
               && !existingLocalRequestContext.isEmpty()) {
            fallbacks.add(existingLocalRequestContext); // ex. in REST client, so that
            // ContextClientOutInterceptor can set view (& project, debug) to use
            // by REST server as was set by REST client app user
         }
         
         // NB. at least one of those will be not null, the way CXF works
         // and only one (unless done in (ClientIn or ServerOut) Response interceptor, which it is not for)
         return new MapWithReadonlyFallbacks<String,Object>(exchange, fallbacks);
      } // else not REST
      
      if (existingLocalRequestContext == null && SimpleRequestContextProvider.shouldEnforce()) {
         throw new RuntimeException("There is no context");
      }
      return SimpleRequestContextProvider.getSimpleRequestContext();
      // NB. don't explode if null, to allow to check whether there is any
      /*
      // helper for unit tests :
      if (requestContext == null) {
         return new HashMap<String, Object>(3); // to avoid NullPointerException outside REST ex. below LoadPersistedModelsAtInit
      }
      return requestContext;
      if (requestContext != null || !isUnitTest()) {
         return requestContext;
      }
      throw new RuntimeException("SimpleRequestContextProvider should have been set up in unit test");*/
   }
   
   @Override
   public Object get(String key) {
      return this.delegate.get(key);
   }
   
   @Override
   public void set(String key, Object value) {
      this.delegate.set(key, value);
   }
   
}

@@ -982,24 +982,31 @@
 tContext();%0A
+      %0A
       Exchan
@@ -1168,16 +1168,26 @@
 t%3E%3E(2);%0A
+         %0A
         
@@ -1195,34 +1195,35 @@
 if (exchange.get
-In
+Out
 Message() != nul
@@ -1243,132 +1243,99 @@
     
-fallbacks.add(exchange.getInMessage()); // case of server in request%0A         %7D else
+// stack order from top : (exchange then) outMessage, existingLocalRequestContext
  if 
-(
 ex
-change.getOutMessage() != null) %7B
+ists
 %0A   
@@ -1413,16 +1413,67 @@
  request
+ being built ((or server out response being built))
 %0A       
@@ -1478,74 +1478,186 @@
     
-%7D%0A
    
-      %0A         // stack on top of existing context if any :%0A
+// such as in ContextClientOutInterceptor setting view, project, debug... in REST client%0A            // so that REST server can use them as was set by REST client app user%0A   
     
@@ -1738,16 +1738,19 @@
         
+  
+ 
 && !exis
@@ -1782,24 +1782,27 @@
 sEmpty()) %7B%0A
+   
             
@@ -1852,182 +1852,478 @@
  // 
-ex. in REST client, so that%0A            // ContextClientOutInterceptor can set view (& project, debug) to use%0A            // by REST server as was set by REST client app user
+case of server in request ((or client in response being built))%0A            %7D%0A            return new MapWithReadonlyFallbacks%3CString,Object%3E(exchange, fallbacks);%0A            %0A         %7D else if (exchange.getInMessage() != null) %7B%0A            // stack order from top : existingLocalRequestContext if exists, (exchange then) inMessage%0A            if (existingLocalRequestContext != null // ex. in REST server%0A                  && !existingLocalRequestContext.isEmpty()) %7B
 %0A   
@@ -2332,281 +2332,645 @@
     
-%7D%0A
       
-   %0A         // NB. at least one of those will be not null, the way CXF works%0A         // and only one (unless done in (ClientIn or ServerOut) Response interceptor, which it is not for)%0A         return new MapWithReadonlyFallbacks%3CString,Object%3E(exchange, fallbacks
+fallbacks.add(exchange);%0A               fallbacks.add(exchange.getInMessage()); // case of server in request ((or client in response being built))%0A               return new MapWithReadonlyFallbacks%3CString,Object%3E(existingLocalRequestContext, fallbacks);%0A            %7D else %7B%0A               fallbacks.add(exchange.getInMessage());%0A               return new MapWithReadonlyFallbacks%3CString,Object%3E(exchange, fallbacks);%0A            %7D%0A         %7D%0A         %0A         // at least one of those will be not null, the way CXF works%0A         throw new RuntimeException(%22CXF exchange but both in & out messages are null%22 + exchange.toString()
 );%0A 

Fix #133 ResourceNotFound in REST model update, because bad context stack order