/* * Copyright 2016 Red Hat, Inc. and/or its affiliates. * * 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 org.jbpm.casemgmt.impl.marshalling; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.drools.core.marshalling.impl.ClassObjectMarshallingStrategyAcceptor; import org.drools.core.marshalling.impl.SerializablePlaceholderResolverStrategy; import org.drools.persistence.api.TransactionAware; import org.drools.persistence.api.TransactionManager; import org.jbpm.casemgmt.api.model.instance.CaseRoleInstance; import org.jbpm.casemgmt.api.model.instance.CommentInstance; import org.jbpm.casemgmt.impl.model.instance.CaseFileInstanceImpl; import org.kie.api.marshalling.ObjectMarshallingStrategy; import org.kie.internal.runtime.Cacheable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class CaseFileInstanceMarshallingStrategy implements ObjectMarshallingStrategy, TransactionAware, Cacheable { private static final Logger logger = LoggerFactory.getLogger(CaseFileInstanceMarshallingStrategy.class); private static final String CASE_ID_KEY = "CaseId"; private static final String CASE_DEF_ID_KEY = "CaseDefId"; private static final String CASE_START_KEY = "CaseStart"; private static final String CASE_END_KEY = "CaseEnd"; private static final String CASE_REOPEN_KEY = "CaseReopen"; private static final String CASE_ROLE_ASSIGNMENTS_KEY = "CaseRoleAssignments"; private static final String CASE_COMMENTS_KEY = "CaseComments"; private static final String CASE_DATA_KEY = "CaseData"; private Map<String, ObjectMarshallingStrategy> marshallersByName = new LinkedHashMap<String, ObjectMarshallingStrategy>(); private SerializablePlaceholderResolverStrategy caseFileMarshaller = new SerializablePlaceholderResolverStrategy(ClassObjectMarshallingStrategyAcceptor.DEFAULT); public CaseFileInstanceMarshallingStrategy() { marshallersByName.put(caseFileMarshaller.getClass().getName(), caseFileMarshaller); logger.debug("Created CaseFileInstance marshaller with default marshaller only"); } public CaseFileInstanceMarshallingStrategy(ObjectMarshallingStrategy...strategies) { for (ObjectMarshallingStrategy strategy : strategies) { logger.debug("Adding {} marshaller into CaseFileInstance marshaller under name {}", strategy, strategy.getClass().getName()); marshallersByName.put(strategy.getClass().getName(), strategy); } marshallersByName.put(caseFileMarshaller.getClass().getName(), caseFileMarshaller); logger.debug("Created CaseFileInstance marshaller with following marshallers {}", marshallersByName); } @Override public void close() { marshallersByName.values().stream().filter(m -> m instanceof Cacheable).forEach(m -> { logger.debug("Closing {} marshaller on close of {}", m, this); ((Cacheable) m).close(); }); } @Override public void onStart(TransactionManager txm) { marshallersByName.values().stream().filter(m -> m instanceof TransactionAware).forEach(m -> { logger.debug("Calling onStart (txm) on {} marshaller", m); ((TransactionAware) m).onStart(txm); }); } @Override public void onEnd(TransactionManager txm) { marshallersByName.values().stream().filter(m -> m instanceof TransactionAware).forEach(m -> { logger.debug("Calling onEnd (txm) on {} marshaller", m); ((TransactionAware) m).onStart(txm); }); } @Override public boolean accept(Object object) { if (object instanceof CaseFileInstanceImpl) { logger.debug("{} object is of CaseFileInstanceImpl type, will be serialized by CaseFileInstanceMarshaller", object); return true; } return false; } @Override public void write(ObjectOutputStream os, Object object) throws IOException { throw new UnsupportedOperationException("org.jbpm.casemgmt.impl.marshalling.CaseFileInstanceMarshallingStrategy.write(ObjectOutputStream, Object) is not supported"); } @Override public Object read(ObjectInputStream os) throws IOException, ClassNotFoundException { throw new UnsupportedOperationException("org.jbpm.casemgmt.impl.marshalling.CaseFileInstanceMarshallingStrategy.read(ObjectInputStream) is not supported"); } @Override public byte[] marshal(Context context, ObjectOutputStream os, Object object) throws IOException { logger.debug("About to marshal {}", object); CaseFileInstanceImpl caseFile = (CaseFileInstanceImpl) object; Map<String, Object> caseFileContent = new HashMap<>(); caseFileContent.put(CASE_ID_KEY, caseFile.getCaseId()); caseFileContent.put(CASE_DEF_ID_KEY, caseFile.getDefinitionId()); caseFileContent.put(CASE_START_KEY, caseFile.getCaseStartDate()); caseFileContent.put(CASE_END_KEY, caseFile.getCaseEndDate()); caseFileContent.put(CASE_REOPEN_KEY, caseFile.getCaseReopenDate()); caseFileContent.put(CASE_ROLE_ASSIGNMENTS_KEY, new HashMap<>(caseFile.getRolesAssignments())); caseFileContent.put(CASE_COMMENTS_KEY, new ArrayList<>(caseFile.getComments())); logger.debug("CaseFileContent before case file data is {}", caseFileContent); List<SerializedContent> caseDataContent = new ArrayList<>(); caseFileContent.put(CASE_DATA_KEY, caseDataContent); // transform with various strategies data that belong to a case for (Entry<String, Object> dataEntry : caseFile.getData().entrySet()) { byte[] content = null; String marshallerName = null; logger.debug("About to find marshaller for {}", dataEntry.getValue()); for (ObjectMarshallingStrategy marshaller : marshallersByName.values()) { if (marshaller.accept(dataEntry.getValue())) { content = marshaller.marshal(context, os, dataEntry.getValue()); marshallerName = marshaller.getClass().getName(); logger.debug("Object {} marshalled by {}", dataEntry.getValue(), marshallerName); break; } } SerializedContent serializedContent = new SerializedContent(marshallerName, dataEntry.getKey(), content); caseDataContent.add(serializedContent); logger.debug("Serialized content for object {} is {}", dataEntry.getValue(), serializedContent); } byte[] caseFileBytes = caseFileMarshaller.marshal(context, os, caseFileContent); logger.debug("Content of the case file instance after marshaller is of length {}", (caseFileBytes == null ? 0 : caseFileBytes.length)); return caseFileBytes; } @SuppressWarnings("unchecked") @Override public Object unmarshal(Context context, ObjectInputStream is, byte[] object, ClassLoader classloader) throws IOException, ClassNotFoundException { logger.debug("About to read {} bytes to unmarshal CaseFileInstance", (object == null ? 0 : object.length)); Map<String, Object> caseFileContent = (Map<String, Object>) caseFileMarshaller.unmarshal(context, is, object, classloader); CaseFileInstanceImpl caseFileInstance = new CaseFileInstanceImpl(); caseFileInstance.setCaseId((String)caseFileContent.get(CASE_ID_KEY)); caseFileInstance.setDefinitionId((String)caseFileContent.get(CASE_DEF_ID_KEY)); caseFileInstance.setCaseStartDate((Date)caseFileContent.get(CASE_START_KEY)); caseFileInstance.setCaseEndDate((Date)caseFileContent.get(CASE_END_KEY)); caseFileInstance.setCaseReopenDate((Date)caseFileContent.get(CASE_REOPEN_KEY)); caseFileInstance.setRolesAssignments((Map<String, CaseRoleInstance>) caseFileContent.get(CASE_ROLE_ASSIGNMENTS_KEY)); caseFileInstance.setComments((List<CommentInstance>) caseFileContent.get(CASE_COMMENTS_KEY)); logger.debug("CaseFileInstance meta data unmarshalled properly into {}", caseFileInstance); List<SerializedContent> caseDataContent = (List<SerializedContent>) caseFileContent.get(CASE_DATA_KEY); logger.debug("About to read serialized content {}", caseDataContent); for (SerializedContent serializedContent : caseDataContent) { ObjectMarshallingStrategy marshaller = marshallersByName.get(serializedContent.getMarshaller()); logger.debug("Marshaller for {} is of type {}", serializedContent, marshaller); Object value = marshaller.unmarshal(context, is, serializedContent.getContent(), classloader); caseFileInstance.add(serializedContent.getName(), value); logger.debug("Data unmarshalled into {} and put into case file under '{}' name", value, serializedContent.getName()); } logger.debug("Unmarshal of CaseFileInstance completed - result {}", caseFileInstance); return caseFileInstance; } @Override public Context createContext() { return caseFileMarshaller.createContext(); } }