/*
* 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.schema.SchemaConstantsGenerated;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.util.DebugDumpable;
import com.evolveum.midpoint.util.DebugUtil;
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 com.evolveum.midpoint.xml.ns._public.common.common_3.ExpressionType;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
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 SingleItemSerializationSafeContainerImpl<T> implements SerializationSafeContainer<T> {
private static final long serialVersionUID = 7269803380754945968L;
private static final Trace LOGGER = TraceManager.getTrace(SingleItemSerializationSafeContainerImpl.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 SingleItemSerializationSafeContainerImpl(T value, PrismContext prismContext) {
Validate.notNull(prismContext, "prismContext must not be null");
this.prismContext = prismContext;
setValue(value);
}
@Override
public void setValue(T value) {
this.actualValue = value;
checkPrismContext();
if (value != null && prismContext.canSerialize(value)) {
try {
this.valueForStorageWhenEncoded = prismContext.xmlSerializer().serializeAnyData(value, new QName("value"));
} 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");
}
@Override
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 {
PrismValue prismValue = prismContext.parserFor(valueForStorageWhenEncoded).xml().parseItemValue();
actualValue = prismValue != null ? prismValue.getRealValue() : null;
} 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;
}
@Override
public PrismContext getPrismContext() {
return prismContext;
}
@Override
public void setPrismContext(PrismContext prismContext) {
this.prismContext = prismContext;
}
// for testing purposes
@Override
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") +
'}';
}
@Override
public String debugDump(int indent) {
StringBuilder sb = new StringBuilder();
if (actualValue != null) {
debugDumpValue(indent, sb, actualValue);
} else if (valueForStorageWhenNotEncoded != null) {
debugDumpValue(indent, sb, valueForStorageWhenNotEncoded);
} else if (valueForStorageWhenEncoded != null) {
DebugUtil.debugDumpWithLabel(sb, "encoded value", valueForStorageWhenEncoded, indent);
} else {
DebugUtil.debugDumpWithLabel(sb, "value", "null", indent);
}
return sb.toString();
}
private void debugDumpValue(int indent, StringBuilder sb, T value) {
if (value instanceof DebugDumpable) {
DebugUtil.debugDumpWithLabel(sb, "value", (DebugDumpable) value, indent);
return;
}
String stringValue = null;
if (value instanceof ExpressionType) {
// brutal hack...
String xml;
try {
xml = prismContext.xmlSerializer().serializeRealValue(value, SchemaConstantsGenerated.C_EXPRESSION);
stringValue = DebugUtil.fixIndentInMultiline(indent, DebugDumpable.INDENT_STRING, xml);
} catch (SchemaException e) {
LOGGER.warn("Couldn't serialize an expression: {}", value, e);
}
}
if (stringValue == null) {
stringValue = String.valueOf(value);
}
DebugUtil.debugDumpWithLabel(sb, "value", stringValue, indent);
}
}