/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.portletbridge.bridge.scope;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import javax.portlet.faces.annotation.BridgePreDestroy;
import org.jboss.portletbridge.bridge.factory.BridgeLoggerFactoryImpl;
import org.jboss.portletbridge.bridge.logger.BridgeLogger;
import org.jboss.portletbridge.bridge.logger.BridgeLogger.Level;
import org.jboss.portletbridge.listener.PortletBridgeListener;
/**
* This class keeps all request attributes that are required to be stored between portlet requests. These parameters are
* described in the chapter 5.1.2 "Managing Lifecycle State".
*
* @author asmirnov, kenfinnigan
*/
public class BridgeRequestScopeImpl extends ConcurrentHashMap<String, Object> implements BridgeRequestScope {
private static final long serialVersionUID = -5796085561862187555L;
private static final BridgeLogger logger = BridgeLoggerFactoryImpl
.getLogger(BridgeRequestScopeImpl.class.getName());
private static final int DEFAULT_INITIAL_CAPACITY = 16;
private static final float DEFAULT_LOAD_FACTOR = .75f;
private static final int DEFAULT_CONCURRENCY_LEVEL = 4;
private String uniqRequestScopeId;
private String portletName;
private String sessionId;
private String viewId;
private String portletMode;
private Vector<String> excludedEntries;
public BridgeRequestScopeImpl(String portletName, String sessionId, String viewId, String portletMode) {
super(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
initScope(portletName, sessionId, viewId, portletMode);
}
public BridgeRequestScopeImpl(String portletName, String sessionId, String viewId, String portletMode,
int initialCapacity) {
super(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
initScope(portletName, sessionId, viewId, portletMode);
}
public BridgeRequestScopeImpl(String portletName, String sessionId, String viewId, String portletMode,
int initialCapacity, float loadFactor, int concurrencyLevel) {
super(initialCapacity, loadFactor, concurrencyLevel);
initScope(portletName, sessionId, viewId, portletMode);
}
public BridgeRequestScopeImpl(String portletName, String sessionId, String viewId, String portletMode,
Map<String, Object> requestScopeDataMap) {
super(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
initScope(portletName, sessionId, viewId, portletMode);
putAll(requestScopeDataMap);
}
private void initScope(String portletName, String sessionId, String viewId, String portletMode) {
this.portletName = portletName;
this.sessionId = sessionId;
this.viewId = viewId;
this.portletMode = portletMode;
}
public String getId() {
if (null == this.uniqRequestScopeId) {
long timeInMillis = Calendar.getInstance().getTimeInMillis();
this.uniqRequestScopeId = new StringBuffer(BridgeRequestScopeUtil.generateBridgeRequestScopeIdPrefix(
portletName, sessionId, viewId, portletMode)).append(':').append(Long.toString(timeInMillis))
.toString();
}
return this.uniqRequestScopeId;
}
public String getPortletName() {
return this.portletName;
}
public String getSessionId() {
return this.sessionId;
}
public String getViewId() {
return this.viewId;
}
public String getPortletMode() {
return this.portletMode;
}
public void setExcludedEntries(List<String> excludedNames) {
this.excludedEntries = new Vector<String>(excludedNames);
}
public void addExcludedEntries(List<String> excludedNames) {
if (null != this.excludedEntries) {
this.excludedEntries.addAll(excludedNames);
} else {
this.setExcludedEntries(excludedNames);
}
}
public List<String> getExcludedEntries(List<String> excludedNames) {
return this.excludedEntries;
}
public boolean isExcluded(String key, Object value) {
return BridgeRequestScopeUtil.isExcluded(key, value, this.excludedEntries);
}
@Override
public Object putIfAbsent(String key, Object value) {
if (!isExcluded(key, value)) {
return callPreDestroy(key, super.putIfAbsent(key, value));
}
return null;
}
/**
* Only put the value into the map if it isn't excluded.
*/
@Override
public Object put(String key, Object value) {
if (!isExcluded(key, value)) {
return callPreDestroy(key, super.put(key, value));
}
return null;
}
/**
* Only put the values from the map parameter into the map if they aren't excluded.
*/
@Override
public void putAll(Map<? extends String, ? extends Object> map) {
for (Map.Entry<? extends String, ? extends Object> entry : map.entrySet()) {
put(entry.getKey(), entry.getValue());
}
}
public Object remove(String key) {
return callPreDestroy(key, super.remove(key));
}
public boolean remove(String key, Object value) {
callPreDestroy(key, value);
return super.remove(key, value);
}
@Override
public Object replace(String key, Object value) {
return callPreDestroy(key, super.replace(key, value));
}
@Override
public boolean replace(String key, Object oldValue, Object newValue) {
if (!super.replace(key, oldValue, newValue)) {
return false;
}
callPreDestroy(key, oldValue);
return true;
}
@Override
public void clear() {
for (String key : keySet()) {
callPreDestroy(key, get(key));
}
super.clear();
}
/**
* Per JSR-329 6.8.2, when terminating the Bridge Request Scope, any managed attributes with public, no-arg, void
* return methods annotated with BridgePreDestroy need to be called.
*
* @param obj
* Object requiring call to PreDestroy annotated methods
* @return Original Object
*/
private Object callPreDestroy(String key, Object obj) {
if (null != obj) {
for (Method method : obj.getClass().getMethods()) {
// Check for Method with BridgePreDestroy annotation
if (method.isAnnotationPresent(BridgePreDestroy.class)) {
// Check for Public method only
if (!Modifier.isPublic(method.getModifiers())) {
continue;
}
// Check for no arg method
if (method.getParameterTypes().length > 0) {
continue;
}
// Check for void return type
if (method.getReturnType() != Void.class) {
continue;
}
try {
// Invoke pre destroy method
method.invoke(obj, (Object) null);
} catch (Exception e) {
logger.log(Level.ERROR, "Error invoking @BridgePreDestroy method: " + method.getName() + " on: "
+ obj.getClass().getName(), e);
}
}
}
PortletBridgeListener listener = PortletBridgeListener.getCurrentInstance();
if (null != listener) {
listener.handleAttributeEvent(key, obj);
}
}
return obj;
}
}