/**
* Licensed to Apereo under one or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information regarding copyright ownership. Apereo
* licenses this file to you 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 the
* following location:
*
* <p>http://www.apache.org/licenses/LICENSE-2.0
*
* <p>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.apereo.portal.portlets.portletadmin.xmlsupport;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.MissingResourceException;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apereo.portal.portlet.om.IPortletType;
import org.apereo.portal.portlet.registry.IPortletTypeRegistry;
import org.apereo.portal.portletpublishing.xml.PortletPublishingDefinition;
import org.apereo.portal.portletpublishing.xml.Step;
import org.apereo.portal.utils.threading.MapCachingDoubleCheckedCreator;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;
@Service("channelPublishingDefinitionDao")
public class XmlChannelPublishingDefinitionDao
implements IChannelPublishingDefinitionDao, ResourceLoaderAware, InitializingBean {
private static final String SHARED_PARAMETERS_PATH =
"/org/apereo/portal/portlets/SharedParameters.cpd.xml";
protected final Log logger = LogFactory.getLog(this.getClass());
private Unmarshaller unmarshaller;
private MapCachingDoubleCheckedCreator<Integer, PortletPublishingDefinition> cpdCreator;
private List<Step> sharedParameters;
private Map<Integer, PortletPublishingDefinition> cpdCache;
private IPortletTypeRegistry portletTypeRegistry;
private ResourceLoader resourceLoader;
public XmlChannelPublishingDefinitionDao() throws JAXBException {
JAXBContext context = JAXBContext.newInstance(PortletPublishingDefinition.class);
unmarshaller = context.createUnmarshaller();
}
@javax.annotation.Resource(name = "cpdCache")
public void setCpdCache(Map<Integer, PortletPublishingDefinition> cpdCache) {
this.cpdCache = cpdCache;
}
@Autowired(required = true)
public void setPortletTypeRegistry(IPortletTypeRegistry portletTypeRegistry) {
this.portletTypeRegistry = portletTypeRegistry;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
@Override
public void afterPropertiesSet() throws Exception {
this.cpdCreator = new CpdCreator(this.cpdCache);
}
/* (non-Javadoc)
* @see org.apereo.portal.portlets.portletadmin.xmlsupport.IChannelPublishingDefinitionDao#getChannelPublishingDefinition(int)
*/
@Override
public PortletPublishingDefinition getChannelPublishingDefinition(int channelTypeId) {
return this.cpdCreator.get(channelTypeId);
}
/* (non-Javadoc)
* @see org.apereo.portal.portlets.portletadmin.xmlsupport.IChannelPublishingDefinitionDao#getChannelPublishingDefinitions()
*/
@Override
public Map<IPortletType, PortletPublishingDefinition> getChannelPublishingDefinitions() {
final List<IPortletType> channelTypes = this.portletTypeRegistry.getPortletTypes();
final Map<IPortletType, PortletPublishingDefinition> cpds =
new LinkedHashMap<IPortletType, PortletPublishingDefinition>(channelTypes.size());
for (final IPortletType channelType : channelTypes) {
final PortletPublishingDefinition cpd =
this.getChannelPublishingDefinition(channelType.getId());
cpds.put(channelType, cpd);
}
return cpds;
}
private PortletPublishingDefinition loadChannelPublishingDefinition(int channelTypeId) {
// if the CPD is not already in the cache, determine the CPD URI
final String cpdUri;
if (channelTypeId >= 0) {
final IPortletType type = this.portletTypeRegistry.getPortletType(channelTypeId);
if (type == null) {
throw new IllegalArgumentException(
"No ChannelType registered with id: " + channelTypeId);
}
cpdUri = type.getCpdUri();
} else {
throw new IllegalArgumentException(
"No ChannelType registered with id: " + channelTypeId);
}
// read and parse the CPD
final PortletPublishingDefinition def;
final Resource cpdResource = this.resourceLoader.getResource("classpath:" + cpdUri);
if (!cpdResource.exists()) {
throw new MissingResourceException(
"Failed to find CPD '" + cpdUri + "' for channel type " + channelTypeId,
this.getClass().getName(),
cpdUri);
}
final InputStream cpdStream;
try {
cpdStream = cpdResource.getInputStream();
} catch (IOException e) {
throw new MissingResourceException(
"Failed to load CPD '" + cpdUri + "' for channel type " + channelTypeId,
this.getClass().getName(),
cpdUri);
}
try {
def = (PortletPublishingDefinition) this.unmarshaller.unmarshal(cpdStream);
final List<Step> sharedParameters = this.getSharedParameters();
def.getSteps().addAll(sharedParameters);
// add the CPD to the cache and return it
this.cpdCache.put(channelTypeId, def);
return def;
} catch (JAXBException e) {
} finally {
IOUtils.closeQuietly(cpdStream);
}
return null;
}
private List<Step> getSharedParameters() {
if (this.sharedParameters != null) {
return this.sharedParameters;
}
// read and parse the shared CPD
final Resource paramResource =
this.resourceLoader.getResource("classpath:" + SHARED_PARAMETERS_PATH);
if (!paramResource.exists()) {
throw new MissingResourceException(
"Failed to find shared parameters CPD '" + SHARED_PARAMETERS_PATH + "'",
this.getClass().getName(),
SHARED_PARAMETERS_PATH);
}
final InputStream paramStream;
try {
paramStream = paramResource.getInputStream();
} catch (IOException e) {
throw new MissingResourceException(
"Failed to load CPD '" + SHARED_PARAMETERS_PATH + "'",
this.getClass().getName(),
SHARED_PARAMETERS_PATH);
}
// parse the shared CPD and add its steps to the end of the type-specific
try {
PortletPublishingDefinition config =
(PortletPublishingDefinition) unmarshaller.unmarshal(paramStream);
this.sharedParameters = config.getSteps();
} catch (JAXBException e) {
logger.warn("Failed to parse: " + paramResource, e);
} finally {
IOUtils.closeQuietly(paramStream);
}
return this.sharedParameters;
}
private class CpdCreator
extends MapCachingDoubleCheckedCreator<Integer, PortletPublishingDefinition> {
public CpdCreator(Map<Integer, PortletPublishingDefinition> cache) {
super(cache);
}
@Override
protected PortletPublishingDefinition createInternal(Integer key, Object... args) {
return loadChannelPublishingDefinition(key);
}
@Override
protected Integer getKey(Object... args) {
return (Integer) args[0];
}
}
}