com.evolveum.midpoint.wf.impl.util.SerializationSafeContainer.java Source code

Java tutorial

Introduction

Here is the source code for com.evolveum.midpoint.wf.impl.util.SerializationSafeContainer.java

Source

/*
 * Copyright (c) 2010-2013 Evolveum
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.evolveum.midpoint.wf.impl.util;

import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.exception.SystemException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;

import javax.xml.bind.JAXB;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.namespace.QName;
import java.io.Serializable;

/**
 * Helper class that allows putting (almost) arbitrary objects into Activiti processes.
 *
 * Generally, prism objects and containers and jaxb objects are stored in their XML form,
 * allowing for safe deserialization in potentially newer version of midpoint.
 *
 * Other serializable items are stored as such.
 *
 * There's a child class (JaxbValueContainer) that allows directly retrieving XML representation of the object
 * (if there's one).
 *
 * @author mederly
 */
public class SerializationSafeContainer<T> implements Serializable {

    private static final long serialVersionUID = 7269803380754945968L;

    private static final Trace LOGGER = TraceManager.getTrace(SerializationSafeContainer.class);
    public static final int MAX_WIDTH = 500;

    // this is the actual (directly usable) value of the item
    private transient T actualValue;

    // if there's no need to encode the value, it is stored in the first attribute
    // if there is (e.g. for PrismObjects) the encoded value is stored in the second attribute
    private T valueForStorageWhenNotEncoded;
    protected String valueForStorageWhenEncoded;

    // beware, for JAXB, PRISM_OBJECT and PRISM_CONTAINER encoding schemes the value must be XML, as it might be
    // exposed through JaxbValueContainer
    protected EncodingScheme encodingScheme;

    private transient PrismContext prismContext;

    public SerializationSafeContainer(T value, PrismContext prismContext) {
        Validate.notNull(prismContext, "prismContext must not be null");
        this.prismContext = prismContext;
        setValue(value);
    }

    public void setValue(T value) {
        this.actualValue = value;

        checkPrismContext();
        if (value != null && prismContext.canSerialize(value)) {
            try {
                this.valueForStorageWhenEncoded = prismContext.serializeAnyData(value, new QName("value"),
                        PrismContext.LANG_XML);
            } catch (SchemaException e) {
                throw new SystemException(
                        "Couldn't serialize value of type " + value.getClass() + ": " + e.getMessage(), e);
            }
            this.valueForStorageWhenNotEncoded = null;
            encodingScheme = EncodingScheme.PRISM;
        } else if (value == null || value instanceof Serializable) {
            this.valueForStorageWhenNotEncoded = value;
            this.valueForStorageWhenEncoded = null;
            encodingScheme = EncodingScheme.NONE;
            if (value instanceof Itemable) {
                throw new IllegalStateException(
                        "Itemable value is used as not-encoded serializable item; value = " + value);
            }
        } else {
            throw new IllegalStateException("Attempt to put non-serializable item " + value.getClass() + " into "
                    + this.getClass().getSimpleName());
        }
    }

    private void checkPrismContext() {
        Validate.notNull(prismContext, "In SerializationSafeContainer the prismContext is not set up");
    }

    public T getValue() {

        if (actualValue != null) {
            return actualValue;
        }

        if (valueForStorageWhenNotEncoded != null) {
            actualValue = valueForStorageWhenNotEncoded;
            return actualValue;
        }

        if (valueForStorageWhenEncoded != null) {
            if (prismContext == null) {
                throw new IllegalStateException("PrismContext not set for SerializationSafeContainer holding "
                        + StringUtils.abbreviate(valueForStorageWhenEncoded, MAX_WIDTH));
            }

            if (encodingScheme == EncodingScheme.PRISM) {
                try {
                    actualValue = (T) prismContext.parseAnyData(valueForStorageWhenEncoded, PrismContext.LANG_XML);
                    if (actualValue instanceof Item) {
                        Item item = (Item) actualValue;
                        if (item.isEmpty()) {
                            actualValue = null;
                        } else if (item.size() == 1) {
                            PrismValue itemValue = (PrismValue) item.getValues().get(0);
                            if (itemValue instanceof PrismContainerValue) {
                                actualValue = (T) ((PrismContainerValue) itemValue).asContainerable();
                            } else if (itemValue instanceof PrismPropertyValue) {
                                actualValue = (T) ((PrismPropertyValue) itemValue).getValue();
                            } else if (itemValue instanceof PrismReferenceValue) {
                                actualValue = (T) itemValue; // TODO: ok???
                            } else {
                                throw new SchemaException("Unknown itemValue: " + itemValue);
                            }
                        } else {
                            throw new SchemaException("More than one value after deserialization of "
                                    + StringUtils.abbreviate(valueForStorageWhenEncoded, MAX_WIDTH) + ": "
                                    + item.getValues().size() + " values");
                        }
                    }
                } catch (SchemaException e) {
                    throw new SystemException("Couldn't deserialize value from JAXB: "
                            + StringUtils.abbreviate(valueForStorageWhenEncoded, MAX_WIDTH), e);
                }
                return actualValue;
            } else {
                throw new IllegalStateException("Unexpected encoding scheme " + encodingScheme);
            }
        }

        return null;
    }

    public PrismContext getPrismContext() {
        return prismContext;
    }

    public void setPrismContext(PrismContext prismContext) {
        this.prismContext = prismContext;
    }

    // for testing purposes
    public void clearActualValue() {
        actualValue = null;
    }

    public enum EncodingScheme {
        PRISM, NONE
    };

    @Override
    public String toString() {
        return "SerializationSafeContainer{" + "actualValue " + (actualValue != null ? "SET" : "NOT SET")
                + ", valueForStorageWhenNotEncoded=" + valueForStorageWhenNotEncoded
                + ", valueForStorageWhenEncoded='" + valueForStorageWhenEncoded + '\'' + ", encodingScheme="
                + encodingScheme + ", prismContext " + (prismContext != null ? "SET" : "NOT SET") + '}';
    }
}