Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  package org.kie.remote.services.rest.jaxb;
  
  import static org.kie.remote.services.rest.jaxb.DynamicJaxbContextFilter.DEFAULT_JAXB_CONTEXT_ID;
  import static org.kie.services.client.serialization.JaxbSerializationProvider.configureMarshaller;
  
  import java.util.Arrays;
  import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
 import  javax.enterprise.context.ApplicationScoped;
 import  javax.enterprise.event.Observes;
 import  javax.inject.Inject;
 
 import  org.jbpm.services.api.DeploymentEvent;
 import  org.jbpm.services.cdi.Deploy;
 import  org.jbpm.services.cdi.Undeploy;
 import  org.slf4j.Logger;
 import  org.slf4j.LoggerFactory;
 
 import  com.sun.xml.bind.v2.runtime.IllegalAnnotationException;
 import  com.sun.xml.bind.v2.runtime.IllegalAnnotationsException;
 import  com.sun.xml.bind.v2.runtime.Location;

READ THIS BEFORE WORKING ON THIS CLASS!

There are a couple of hidden issues that this class deals this:

  1. JAXBContext instances are cached by the REST framework. This means that we can not provide multiple JAXBContext instances, but must make sure that an instance of this class is cached. When the extended methods are called (JAXBContext.createMarshaller() for example), we then look at the internal cache to retrieve the correct JAXBContext).
  2. Different deployments may have different version of the same class. This means that We can not use 1 JAXBContext instance to deal with all deployments. Tests have been specifically created to test this issue.
  3. Concurrency: this is an application scoped class that is being acted on by multiple REST request threads. Fortunately, we just handle normal REST requests (no comet, for example), so the requests themselves (with regards to the serializaiton logic) are all handled in one thread.
Regardless, please preserve existing tests when modifying this class!
 
 @ApplicationScoped
 public class DynamicJaxbContext extends JAXBContext {
 
     private static final Logger logger = LoggerFactory.getLogger(DynamicJaxbContext.class);
 
     // The contextsCache needs to be a ConcurrentHashMap because parallel operations (involing *different* deployments) can happen on it.
     static { 
         setupDefaultJaxbContext();
     }
 
     private static ThreadLocal<JAXBContextrequestJaxbContextLocal = new ThreadLocal<JAXBContext>();
     
     @Inject
     // pkg level for tests
 
     private static AtomicInteger instanceCreated = new AtomicInteger(0);
 
     public DynamicJaxbContext() {
         if( ! .compareAndSet(0, 1) ) {
             .debug("Instance {} of the {} created!".incrementAndGet(), DynamicJaxbContext.class.getSimpleName() ); 
         }
     }
   
     // Servlet Filter ------------------------------------------------------------------------------------------------------------
     
     public static void setDeploymentJaxbContext(String deploymentId) {
         JAXBContext jaxbContext = .get(deploymentId);
         ifjaxbContext == null ) { 
            .debug("No JAXBContext available for deployment '" + deploymentId + "', using default JAXBContext instance."); 
            jaxbContext = .get();
         }
         .set(jaxbContext);
     }
     
     public static void clearDeploymentJaxbContext() {
        .set(null);
     }
 
     // JAXBContext methods --------------------------------------------------------------------------------------------------------

    
This method is called by the JAXBContext methods when they need a JAXBContext to create a Marshaller, Unmarshaller or Validator instance.

Returns:
The JAXBContext created or retrieved from cache for the request.
    private JAXBContext getRequestContext() { 
        JAXBContext requestJaxbContext = .get();
        ifrequestJaxbContext == null ) { 
            .error("No JAXB context could be found for request, using default!");
            requestJaxbContext = .get();
        }
        return requestJaxbContext;
    }
    /*
     * (non-Javadoc)
     * @see javax.xml.bind.JAXBContext#createUnmarshaller()
     */
    @Override
    public Unmarshaller createUnmarshaller() throws JAXBException {
        JAXBContext context = getRequestContext();
        if (context != null) {
            return context.createUnmarshaller();
        }
        throw new KieRemoteServicesInternalError("No Unmarshaller available: JAXBContext instance could be found for this request!");
    }
    /*
     * (non-Javadoc)
     * @see javax.xml.bind.JAXBContext#createMarshaller()
     */
    @Override
    public Marshaller createMarshaller() throws JAXBException {
        JAXBContext context = getRequestContext();
        if (context != null) {
            return configureMarshaller(contextfalse);
        }
        throw new KieRemoteServicesInternalError("No Marshaller available: JAXBContext instance could be found for this request!");
    }
    /*
     * (non-Javadoc)
     * @see javax.xml.bind.JAXBContext#createValidator()
     */
    @Override
    @SuppressWarnings("deprecation")
    public Validator createValidator() throws JAXBException {
        JAXBContext context = getRequestContext();
        if (context != null) {
            return context.createValidator();
        }
        throw new KieRemoteServicesInternalError("No Validator available: JAXBContext instance could be found for this request!");
    }
    
    // Deployment jaxbContext management and creation logic -----------------------------------------------------------------------
    
    
Adds a deployment lock object.

Parameters:
event The DeploymentEvent fired on deployment
    public void setupDeploymentJaxbContext(@Observes @Deploy DeploymentProcessedEvent event) {
        String deploymentId = event.getDeploymentId();
        setupDeploymentJaxbContext(deploymentId);
    }

    
Removes the cached JAXBContext and deployment lock object It's VERY important that the cached JAXBContext instance be removed! The new deployment may contain a different version of a class with the same name. Keeping the old class definition will cause problems!

Parameters:
event The DeploymentEvent fired on an undeploy of a deployment
    public void cleanUpOnUndeploy(@Observes @Undeploy DeploymentProcessedEvent event) {
        String deploymentId = event.getDeploymentId();
        JAXBContext deploymentJaxbContext = .remove(deploymentId);
        ifdeploymentJaxbContext == null ) { 
            .error("JAXB context instance could not be found when undeploying deployment '" + deploymentId + "'!");
        }
    }

    
Creates the default JAXB Context at initialization
    private static void setupDefaultJaxbContext() {
        try {
            Class<?> [] types = ServerJaxbSerializationProvider.getAllBaseJaxbClasses();
            JAXBContext defaultJaxbContext = JAXBContext.newInstance(types);
            .put(defaultJaxbContext);
        } catch (JAXBException e) {
            throw new IllegalStateException"Unable to create new " + JAXBContext.class.getSimpleName() + " instance."e);
        }
    }
    public JAXBContext getDeploymentJaxbContext(String deploymentId) {
        JAXBContext jaxbContext = .get(deploymentId);
        ifjaxbContext == null ) { 
            .debug("No JAXBContext available for deployment '" + deploymentId + "', using default JAXBContext instance."); 
            jaxbContext = .get();
        }
        return jaxbContext;
    }

    
Creates the JAXBContext instance for the given deployment at deployment time

Parameters:
deploymentId The deployment identifier.
    private void setupDeploymentJaxbContext(String deploymentId) { 
        if.contains(deploymentId) ) { 
            .error("JAXB context instance already found when deploying deployment '" + deploymentId + "'!");
            .remove(deploymentId);
        }
        
        // retrieve deployment classes
        Collection<Class<?>> depClasses = .getDeploymentClasses(deploymentId);
        // We are sharing the default jaxb context among different requests here: 
        // while we have no guarantees that JAXBContext instances are thread-safe, 
        // the REST framework using the JAXBContext instance is responsible for that thread-safety
        // since it is caching the JAXBContext in any case.. 
        ifdepClasses.size() == 0 ) { 
            JAXBContext defaultJaxbContext = .get();
            .put(deploymentIddefaultJaxbContext);
            return;
        }
        
        // create set of all classes needed
        List<Class<?>> allClassList = Arrays.asList(ServerJaxbSerializationProvider.getAllBaseJaxbClasses());
        Set<Class<?>> allClasses = new HashSet<Class<?>>(allClassList);
        allClasses.addAll(depClasses);
        Class [] allClassesArr = allClasses.toArray(new Class[allClasses.size()]);
        // create and cache jaxb context 
        JAXBContext jaxbContext = null;
        try { 
            if ) { 
                jaxbContext = smartJaxbContextInitialization(allClassesArrdeploymentId);
            } else { 
                jaxbContext = JAXBContext.newInstance(allClassesArr);
            }
            .put(deploymentIdjaxbContext);
        } catchJAXBException jaxbe ) { 
            String errMsg = "Unable to instantiate JAXBContext for deployment '" + deploymentId + "'.";
            throw new KieRemoteServicesDeploymentExceptionerrMsgjaxbe );
            // This is a serious problem if it goes wrong here. 
        }
    }
  
    private final static String SMART_JAXB_CONTEXT_INIT_PROPERTY_NAME = "org.kie.remote.jaxb.smart.init";
    private final static boolean smartJaxbContextInitialization;
    private final static String EXPECTED_JAXB_CONTEXT_IMPL_CLASS = "com.sun.xml.bind.v2.runtime.JAXBContextImpl";
    
    // only use smart initialization if we're using the (default) JAXB RI 
    static { 
         boolean smartJaxbContextInitProperty = Boolean.parseBoolean(System.getProperty("true"));
         ifsmartJaxbContextInitProperty ) { 
            try {
                smartJaxbContextInitProperty = false;
                smartJaxbContextInitProperty = .equals(JAXBContext.newInstance(new Class[0]).getClass().getName());
            } catchJAXBException jaxbe ) {
                .error("Unable to initialize empty JAXB Context: something is VERY wrong!"jaxbe);
            }
         }
          = smartJaxbContextInitProperty;
    }
 
    private JAXBContext smartJaxbContextInitialization(Class [] jaxbContextClassesString deploymentIdthrows JAXBException { 
        
        List<ClassclassList = new ArrayList<Class>(Arrays.asList(jaxbContextClasses));
        
        JAXBContext jaxbContext = null;
        boolean retryJaxbContextCreation = true;
        whileretryJaxbContextCreation ) { 
            try {
                jaxbContext = JAXBContext.newInstance(classList.toArray(new Class[classList.size()]));
                retryJaxbContextCreation = false;
            } catch( IllegalAnnotationsException iae ) {
                // throws any exception it can not process
                removeClassFromJaxbContextClassList(classListiaedeploymentId);
            } 
        }
        
        return jaxbContext;
    }
    private void removeClassFromJaxbContextClassListList<ClassclassList, IllegalAnnotationsException iaeString deploymentId)
        throws IllegalAnnotationException {
        
        Set<ClassremovedClasses = new HashSet<Class>();
        for( IllegalAnnotationException error : iae.getErrors() ) {
            List<Location> classLocs = error.getSourcePos().get(0);
            
            ifclassLocs != null && ! classLocs.isEmpty() ) { 
               String className = classLocs.listIterator(classLocs.size()).previous().toString();
               Class removeClass = null;
               try {
                   removeClass = Class.forName(className);
                   if( ! removedClasses.add(removeClass) ) { 
                       // we've already determined that this class was bad
                       continue;
                   } 
               } catchClassNotFoundException cnfe ) {
                   // this should not be possible, after the class object instance has already been found
                   //  and added to the list of classes needed for the JAXB context
                   throw new KieRemoteServicesInternalError("Class [" + className + "] could not be found when creating JAXB context: "  + cnfe.getMessage(), cnfe);
               }
               ifclassList.remove(removeClass) ) { 
                   .warn("Removing class '{}' from serialization context for deployment '{}'"classNamedeploymentId);
                   // next error
                   continue;
               }
            } 
            // We could not figure out which class caused this error (this error is wrapped later)
            throw error;
        }
    }
    
New to GrepCode? Check out our FAQ X