/* * #%L * BroadleafCommerce Open Admin Platform * %% * Copyright (C) 2009 - 2013 Broadleaf Commerce * %% * 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. * #L% */ package org.broadleafcommerce.openadmin.server.service.persistence.module.provider; import org.apache.commons.lang3.StringUtils; import org.broadleafcommerce.common.exception.ExceptionHelper; import org.broadleafcommerce.common.extension.ExtensionResultHolder; import org.broadleafcommerce.common.extension.ExtensionResultStatusType; import org.broadleafcommerce.common.media.domain.Media; import org.broadleafcommerce.common.media.domain.MediaImpl; import org.broadleafcommerce.common.persistence.EntityConfiguration; import org.broadleafcommerce.common.presentation.client.SupportedFieldType; import org.broadleafcommerce.common.sandbox.SandBoxHelper; import org.broadleafcommerce.common.util.Tuple; import org.broadleafcommerce.openadmin.dto.BasicFieldMetadata; import org.broadleafcommerce.openadmin.dto.FieldMetadata; import org.broadleafcommerce.openadmin.dto.Property; import org.broadleafcommerce.openadmin.server.service.persistence.ParentEntityPersistenceException; import org.broadleafcommerce.openadmin.server.service.persistence.PersistenceException; import org.broadleafcommerce.openadmin.server.service.persistence.module.FieldManager; import org.broadleafcommerce.openadmin.server.service.persistence.module.FieldNotAvailableException; import org.broadleafcommerce.openadmin.server.service.persistence.module.provider.extension .MediaFieldPersistenceProviderExtensionManager; import org.broadleafcommerce.openadmin.server.service.persistence.module.provider.request.AddFilterPropertiesRequest; import org.broadleafcommerce.openadmin.server.service.persistence.module.provider.request.ExtractValueRequest; import org.broadleafcommerce.openadmin.server.service.persistence.module.provider.request.PopulateValueRequest; import org.broadleafcommerce.openadmin.server.service.type.FieldProviderResponse; import org.broadleafcommerce.openadmin.web.service.MediaBuilderService; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.Serializable; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.annotation.Resource; import javax.persistence.OneToMany; /** * @author Brian Polster */ @Component("blMediaFieldPersistenceProvider") @Scope("prototype") public class MediaFieldPersistenceProvider extends FieldPersistenceProviderAdapter { @Resource(name="blEntityConfiguration") protected EntityConfiguration entityConfiguration; @Resource(name="blSandBoxHelper") protected SandBoxHelper sandBoxHelper; @Resource(name = "blMediaFieldPersistenceProviderExtensionManager") protected MediaFieldPersistenceProviderExtensionManager extensionManager; @Resource(name = "blMediaBuilderService") protected MediaBuilderService mediaBuilderService; protected boolean canHandlePersistence(PopulateValueRequest populateValueRequest, Serializable instance) { return populateValueRequest.getMetadata().getFieldType() == SupportedFieldType.MEDIA; } protected boolean canHandleExtraction(ExtractValueRequest extractValueRequest, Property property) { return extractValueRequest.getMetadata().getFieldType() == SupportedFieldType.MEDIA; } @Override public FieldProviderResponse populateValue(PopulateValueRequest populateValueRequest, Serializable instance) throws PersistenceException { if (!canHandlePersistence(populateValueRequest, instance)) { return FieldProviderResponse.NOT_HANDLED; } String prop = populateValueRequest.getProperty().getName(); if (prop.contains(FieldManager.MAPFIELDSEPARATOR)) { Field field = populateValueRequest.getFieldManager().getField(instance.getClass(), prop.substring(0, prop.indexOf(FieldManager.MAPFIELDSEPARATOR))); if (field.getAnnotation(OneToMany.class) == null) { throw new UnsupportedOperationException("MediaFieldPersistenceProvider is currently only compatible with map fields when modelled using @OneToMany"); } } FieldProviderResponse response = FieldProviderResponse.HANDLED; boolean dirty = false; try { setNonDisplayableValues(populateValueRequest); Class<?> valueType = getStartingValueType(populateValueRequest); if (Media.class.isAssignableFrom(valueType)) { Media newMedia = mediaBuilderService.convertJsonToMedia(populateValueRequest .getProperty().getUnHtmlEncodedValue(), valueType); boolean persist = false; boolean noPrimary = false; Media media; Boolean cleared; try { checkExtension: { if (extensionManager != null) { ExtensionResultHolder<Tuple<Media, Boolean>> result = new ExtensionResultHolder<Tuple<Media, Boolean>>(); ExtensionResultStatusType statusType = extensionManager.getProxy().retrieveMedia(instance, populateValueRequest, result); if (ExtensionResultStatusType.NOT_HANDLED != statusType) { Tuple<Media,Boolean> tuple = result.getResult(); media = tuple.getFirst(); cleared = tuple.getSecond(); break checkExtension; } } media = (Media) populateValueRequest.getFieldManager().getFieldValue(instance, populateValueRequest.getProperty().getName()); cleared = false; } if (newMedia == null) { noPrimary = true; dirty = true; if (!cleared && media != null) { // remove entry in sku to media map populateValueRequest.getFieldManager().setFieldValue(instance, populateValueRequest.getProperty().getName(), null); populateValueRequest.getPersistenceManager().getDynamicEntityDao().remove(media); } } else if (media == null) { media = (Media) valueType.newInstance(); Object parent = extractParent(populateValueRequest, instance); populateValueRequest.getFieldManager().setFieldValue(media, populateValueRequest.getMetadata(). getToOneParentProperty(), parent); populateValueRequest.getFieldManager().setFieldValue(media, populateValueRequest.getMetadata(). getMapKeyValueProperty(), prop.substring(prop.indexOf( FieldManager.MAPFIELDSEPARATOR) + FieldManager.MAPFIELDSEPARATOR.length(), prop.length())); persist = true; } } catch (FieldNotAvailableException e) { throw new IllegalArgumentException(e); } if (media != null) { populateValueRequest.getProperty().setOriginalValue(convertMediaToJson(media)); } if (!noPrimary) { dirty = establishDirtyState(newMedia, media); if (dirty) { updateMedia(populateValueRequest, newMedia, persist, media); } } if (dirty) { response = FieldProviderResponse.HANDLED_BREAK; } } else { throw new UnsupportedOperationException("MediaFields only work with Media types."); } } catch (Exception e) { throw ExceptionHelper.refineException(PersistenceException.class, PersistenceException.class, e); } populateValueRequest.getProperty().setIsDirty(dirty); return response; } @Override public FieldProviderResponse extractValue(ExtractValueRequest extractValueRequest, Property property) throws PersistenceException { if (!canHandleExtraction(extractValueRequest, property)) { return FieldProviderResponse.NOT_HANDLED; } if (extractValueRequest.getRequestedValue() != null) { Object requestedValue = extractValueRequest.getRequestedValue(); if (!StringUtils.isEmpty(extractValueRequest.getMetadata().getToOneTargetProperty())) { try { requestedValue = extractValueRequest.getFieldManager().getFieldValue(requestedValue, extractValueRequest.getMetadata().getToOneTargetProperty()); } catch (IllegalAccessException e) { throw ExceptionHelper.refineException(e); } catch (FieldNotAvailableException e) { throw ExceptionHelper.refineException(e); } } if (requestedValue instanceof Media) { Media media = (Media) requestedValue; String jsonString = convertMediaToJson(media); if (extensionManager != null) { ExtensionResultHolder<Long> resultHolder = new ExtensionResultHolder<Long>(); ExtensionResultStatusType result = extensionManager.getProxy().transformId(media, resultHolder); if (ExtensionResultStatusType.NOT_HANDLED != result && resultHolder.getResult() != null) { Class<?> type; if (media.isUnwrappableAs(Media.class)) { type = media.unwrap(Media.class).getClass(); } else { type = media.getClass(); } Media converted = mediaBuilderService.convertJsonToMedia(jsonString, type); converted.setId(resultHolder.getResult()); jsonString = convertMediaToJson(converted); } } property.setValue(jsonString); property.setUnHtmlEncodedValue(jsonString); property.setDisplayValue(extractValueRequest.getDisplayVal()); return FieldProviderResponse.HANDLED_BREAK; } else { throw new UnsupportedOperationException("MEDIA type is currently only supported on fields of type Media"); } } return FieldProviderResponse.HANDLED; } @Override public FieldProviderResponse filterProperties(AddFilterPropertiesRequest addFilterPropertiesRequest, Map<String, FieldMetadata> properties) { // BP: Basically copied this from RuleFieldPersistenceProvider List<Property> propertyList = new ArrayList<Property>(); propertyList.addAll(Arrays.asList(addFilterPropertiesRequest.getEntity().getProperties())); Iterator<Property> itr = propertyList.iterator(); List<Property> additionalProperties = new ArrayList<Property>(); while(itr.hasNext()) { Property prop = itr.next(); if (prop.getName().endsWith("Json")) { for (Map.Entry<String, FieldMetadata> entry : properties.entrySet()) { if (prop.getName().startsWith(entry.getKey())) { BasicFieldMetadata originalFM = (BasicFieldMetadata) entry.getValue(); if (originalFM.getFieldType() == SupportedFieldType.MEDIA) { Property originalProp = addFilterPropertiesRequest.getEntity().findProperty(originalFM .getName()); if (originalProp == null) { originalProp = new Property(); originalProp.setName(originalFM.getName()); additionalProperties.add(originalProp); } originalProp.setValue(prop.getValue()); originalProp.setRawValue(prop.getRawValue()); originalProp.setUnHtmlEncodedValue(prop.getUnHtmlEncodedValue()); itr.remove(); break; } } } } } propertyList.addAll(additionalProperties); addFilterPropertiesRequest.getEntity().setProperties(propertyList.toArray(new Property[propertyList.size()])); return FieldProviderResponse.HANDLED; } @Override public int getOrder() { return FieldPersistenceProvider.MEDIA; } protected void updateMedia(PopulateValueRequest populateValueRequest, Media newMedia, boolean persist, Media media) throws IllegalAccessException, FieldNotAvailableException { if (!persist) { //pre-merge (can result in a clone for enterprise) media = populateValueRequest.getPersistenceManager().getDynamicEntityDao().merge(media); if (extensionManager != null) { extensionManager.getProxy().postUpdate(media); } } updateMediaFields(media, newMedia); if (persist) { populateValueRequest.getPersistenceManager().getDynamicEntityDao().persist(media); if (extensionManager != null) { extensionManager.getProxy().postAdd(media); } } } protected boolean checkEquality(Object one, Object two) { return one == null && two == null || !(one == null || two == null) && one.equals(two); } protected boolean establishDirtyState(Media newMedia, Media media) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { boolean dirty = (newMedia == null && media != null) || (newMedia != null && media == null); if (newMedia == null && media == null) { return false; } if (!dirty) { dirty = !checkEquality(newMedia.getAltText(), media.getAltText()); } if (!dirty) { dirty = !checkEquality(newMedia.getTags(), media.getTags()); } if (!dirty) { dirty = !checkEquality(newMedia.getTitle(), media.getTitle()); } if (!dirty) { dirty = !checkEquality(newMedia.getUrl(), media.getUrl()); } if (!dirty && extensionManager != null) { ExtensionResultHolder<Boolean> resultHolder = new ExtensionResultHolder<Boolean>(); extensionManager.getProxy().checkDirtyState(media, newMedia, resultHolder); dirty = resultHolder.getResult() != null && resultHolder.getResult(); } return dirty; } protected Class<?> getStartingValueType(PopulateValueRequest populateValueRequest) throws ClassNotFoundException, IllegalAccessException { Class<?> startingValueType = null; if (!populateValueRequest.getProperty().getName().contains(FieldManager.MAPFIELDSEPARATOR)) { startingValueType = populateValueRequest.getReturnType(); } else { String valueClassName = populateValueRequest.getMetadata().getMapFieldValueClass(); if (valueClassName != null) { startingValueType = Class.forName(valueClassName); } if (startingValueType == null) { startingValueType = populateValueRequest.getReturnType(); } } if (startingValueType == null) { throw new IllegalAccessException("Unable to determine the valueType for the rule field (" + populateValueRequest.getProperty().getName() + ")"); } else if (Media.class.equals(startingValueType)) { startingValueType = MediaImpl.class; } return startingValueType; } protected String convertMediaToJson(Media media) { try { ObjectMapper om = new ObjectMapper(); Media unwrapped = media; if (media.isUnwrappableAs(Media.class)) { unwrapped = media.unwrap(Media.class); } return om.writeValueAsString(unwrapped); } catch (Exception e) { throw new RuntimeException(e); } } protected void updateMediaFields(Media oldMedia, Media newMedia) { if (newMedia == null) { return; } oldMedia.setAltText(newMedia.getAltText()); oldMedia.setTags(newMedia.getTags()); oldMedia.setTitle(newMedia.getTitle()); oldMedia.setUrl(newMedia.getUrl()); } protected Object extractParent(PopulateValueRequest populateValueRequest, Serializable instance) throws IllegalAccessException, FieldNotAvailableException { Object parent = instance; String parentName = populateValueRequest.getProperty().getName(); if (parentName.contains(".")) { parent = populateValueRequest.getFieldManager().getFieldValue(instance, parentName.substring(0, parentName.lastIndexOf("."))); } if (!populateValueRequest.getPersistenceManager().getDynamicEntityDao().getStandardEntityManager().contains(parent)) { try { populateValueRequest.getPersistenceManager().getDynamicEntityDao().persist(parent); } catch (Exception e) { throw new ParentEntityPersistenceException("Unable to Persist the parent entity during rule builder field population", e); } } return parent; } }