/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
/*
* ReplicationSSOStore.java
*
* Created on December 5, 2005, 12:56 PM
*
*/
package com.sun.enterprise.ee.web.authenticator;
import com.sun.appserv.ha.spi.BackingStore;
import com.sun.appserv.ha.spi.BackingStoreException;
import com.sun.appserv.ha.util.SimpleMetadata;
import com.sun.appserv.ha.util.SimpleMetadataFactory;
import com.sun.enterprise.ee.web.sessmgmt.*;
import com.sun.enterprise.security.web.SingleSignOnEntry;
import com.sun.enterprise.web.ServerConfigLookup;
import com.sun.logging.LogDomains;
import org.apache.catalina.Container;
import org.apache.catalina.session.StandardSession;
import org.apache.catalina.session.StoreBase;
import java.io.*;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author Larry White
*/
public class ReplicationSSOStore implements StorePoolElement, ReplicationSSOStorePoolElement {
final static String MODE_SSO = ReplicationState.MODE_SSO;
final static String MODE_WEB = ReplicationState.MODE_WEB;
/**
* The logger to use for logging ALL web container related messages.
*/
protected static final Logger _logger
= LogDomains.getLogger(LogDomains.WEB_LOGGER);
private static boolean useReplicationUnicastLoadResponseBatching = false;
static {
ServerConfigLookup lookup = new ServerConfigLookup();
useReplicationUnicastLoadResponseBatching
= lookup.isReplicationUnicastLoadResponseBatchingEnabled();
}
/** Creates a new instance of ReplicationSSOStore */
public ReplicationSSOStore() {
}
//begin StorePoolElement methods
public void cleanup() {
//FIXME
//closeStatements();
//closeConnection();
}
public SingleSignOnEntry loadSSO(String ssoId, ReplicationSingleSignOn repSingleSignOn) throws IOException {
try {
BackingStore backingStore = parent.getBackingStore();
SimpleMetadata metaData = (SimpleMetadata) backingStore.load(ssoId, null);
if(_logger.isLoggable(Level.FINE)) {
_logger.fine("ReplicationSSOStore>>loadSSO:id=" + ssoId +
", metaData=" + metaData);
}
return deserializeAndSave(metaData);
} catch (BackingStoreException ex) {
IOException ex1 =
(IOException) new IOException("Error during load: " + ex.getMessage()).initCause(ex);
throw ex1;
}
}
public SingleSignOnEntry deserializeAndSave(SimpleMetadata metaData) throws IOException {
try {
HASingleSignOnEntry result = getSSOEntry(metaData);
if(result != null) {
this.save(result.getId(), result); // TODO :: don't do this for third party store
}
return result;
} catch (Exception ex) {
IOException ex1 =
(IOException) new IOException("Error during load: " + ex.getMessage()).initCause(ex);
throw ex1;
}
}
//end StorePoolElement methods
//begin ReplicationSSOStorePoolElement methods
/** Loads the sso entry
* @param ssoId
* @param repSingleSignOn
* @throws IOException
* @return
*/
public SimpleMetadata __load(String ssoId, ReplicationSingleSignOn repSingleSignOn) throws BackingStoreException {
if(_logger.isLoggable(Level.FINE)) {
_logger.fine("ReplicationSSOStore>>loadSSO:ssoId: " + ssoId);
}
if(ssoId == null) {
return null;
}
//ReplicationState localCachedState = repSingleSignOn.getFromReplicationCache(ssoId);
ReplicationState localCachedState = repSingleSignOn.transferFromReplicationCache(ssoId);
ReplicationState broadcastResultState = findSSOEntryViaBroadcast(ssoId);
ReplicationState bestState = ReplicationState.getBestResult(
localCachedState, broadcastResultState);
if(_logger.isLoggable(Level.FINE)) {
_logger.fine("ReplicationSSOStore>>__load:id=" + ssoId + ", localCachedState = " +
localCachedState + ", broadcastResultState from broadcast or unicast=" +
broadcastResultState + ", bestState = " + bestState);
}
SimpleMetadata result = ReplicationState.createSimpleMetadataFrom(
bestState, false); // TODO :: revisit the second param.
return result;
}
private ReplicationState getBestResult(ReplicationState localState, ReplicationState broadcastResultState) {
if(_logger.isLoggable(Level.FINE)) {
_logger.fine("getBestResult:localState=" + localState + "other=" + broadcastResultState);
}
//System.out.println("getBestResult:localState=" + localState + "other=" + broadcastResultState);
if(localState == null) {
return broadcastResultState;
}
//localState is not null
if(broadcastResultState == null) {
return localState;
}
//both are non-null
if(broadcastResultState.getVersion() >= localState.getVersion()) {
return broadcastResultState;
} else {
return localState;
}
}
/**
* return the name of this instance
* @returns instanceName
*/
public String getInstanceName() {
return ReplicationUtil.getInstanceName();
}
private ReplicationState findSSOEntryViaBroadcast(String ssoId) throws BackingStoreException {
if(_logger.isLoggable(Level.FINER)) {
_logger.entering("ReplicationSSOStore", "findSSOEntryViaBroadcast", ssoId);
}
if(container == null) {
_logger.warning("ReplicationSSOStore>>findSSOEntryViaBroadcast: container is null");
}
if(parent == null) {
return null;
}
BackingStore replicator = parent.getBackingStore();
JxtaBackingStoreImpl jxtaReplicator = null;
if(replicator instanceof JxtaBackingStoreImpl) {
jxtaReplicator = (JxtaBackingStoreImpl)replicator;
}
if(_logger.isLoggable(Level.FINE)) {
_logger.fine("in findSSOEntryViaBroadcast: replicator: " + replicator);
}
ReplicationState queryResult = jxtaReplicator != null ?
jxtaReplicator.__load(ssoId, null) : null; //id
return queryResult;
}
public void save(String ssoId, SingleSignOnEntry ssoEntry) throws IOException {
if(_logger.isLoggable(Level.FINE)) {
_logger.fine("ReplicationSSOStore>>save:ssoId = " + ssoId + " :ssoEntry = " + ssoEntry +
" :parent=" + parent);
if(container == null) {
_logger.warning("ReplicationSSOStore>>save: container null");
}
}
if(parent == null) {
return;
}
BackingStore replicator = parent.getBackingStore();
SimpleMetadata simpleMetadata =
createSimpleMetadata(ssoId, (HASingleSignOnEntry)ssoEntry);
try {
replicator.save(ssoId, //id
simpleMetadata, true); //TODO: Check last param
} catch (BackingStoreException ex) {
// FIXME evaluate log level
if (_logger.isLoggable(Level.FINE)) {
_logger.log(Level.FINE, " exception occurred in save", ex);
}
}
}
public ReplicationState getTransmitState(String ssoId, SingleSignOnEntry ssoEntry) throws IOException {
if(parent == null) {
return null;
}
BackingStore replicator = parent.getBackingStore();
if(!(replicator instanceof JxtaBackingStoreImpl)) {
return null;
}
JxtaBackingStoreImpl jxtaReplicator = (JxtaBackingStoreImpl)replicator;
ReplicationState transmitState = null;
SimpleMetadata simpleMetadata =
createSimpleMetadata(ssoId, (HASingleSignOnEntry)ssoEntry);
//need this here because save not called
simpleMetadata.setOwningInstanceName(ReplicationUtil.getInstanceName());
try {
transmitState = jxtaReplicator.getSimpleTransmitState(ssoId, simpleMetadata);
} catch (BackingStoreException ex) {}
return transmitState;
}
/** Saves the state
* @param state
* @throws IOException */
public void saveForRepair(ReplicationState state) throws IOException {
long tempStartTime = 0L;
if(_logger.isLoggable(Level.FINER)) {
_logger.entering("ReplicationSSOStore", "saveForRepair", state);
tempStartTime = System.currentTimeMillis();
}
this.transmitState(state, "save", true);
//this.transmitState(state, "save", false);
if(_logger.isLoggable(Level.FINE)) {
_logger.fine("ReplicationSSOStore>>saveForRepair time = " + (System.currentTimeMillis() - tempStartTime));
}
if(_logger.isLoggable(Level.FINER)) {
_logger.exiting("ReplicationSSOStore", "saveForRepair");
}
}
public void associate(StandardSession session, String ssoId) throws IOException {
if(_logger.isLoggable(Level.FINER)) {
_logger.entering("ReplicationSSOStore", "associate", ssoId);
}
if(session instanceof HASession) {
((HASession)session).setSsoId(ssoId);
//((HASession)session).incrementVersion();
}
ReplicationManagerBase mgr = (ReplicationManagerBase)session.getManager();
if(_logger.isLoggable(Level.FINE)) {
_logger.fine("ReplicationSSOStore>>associate: mgr = " + mgr);
}
//FIXME: for time-based different manager class
if(mgr instanceof ReplicationWebEventPersistentManager) {
((ReplicationWebEventPersistentManager)mgr).doValveSave(session);
}
if(_logger.isLoggable(Level.FINER)) {
_logger.exiting("ReplicationSSOStore", "associate");
}
}
public void remove(String ssoId) throws IOException {
if(_logger.isLoggable(Level.FINER)) {
_logger.entering("ReplicationSSOStore>", "remove", ssoId);
}
if(container == null) {
_logger.warning("ReplicationSSOStore>>remove: container null");
}
if(parent == null) {
return;
}
BackingStore replicator = parent.getBackingStore();
if(_logger.isLoggable(Level.FINE)) {
_logger.fine("ReplicationSSOStore>>remove: replicator: " + replicator);
}
try {
replicator.remove(ssoId); //ssoid
} catch (BackingStoreException ex) {
// FIXME evaluate log level
if (_logger.isLoggable(Level.FINE)) {
_logger.log(Level.FINE, "exception occurred in remove", ex);
}
}
}
/**
* The application id
*/
protected String applicationId = null;
protected String getApplicationId() {
//injected by ReplicationSingleSignOn
return applicationId;
}
public void setApplicationId(String value) {
applicationId = value;
}
public void removeInActiveSessions(String ssoId) throws IOException {
//FIXME need new version
if (_logger.isLoggable(Level.FINER)) {
_logger.entering("ReplicationSSOStore", "removeInActiveSessions", ssoId);
}
ReplicationState transmitState =
new ReplicationState(MODE_SSO, //sso mode
ssoId, //id
getApplicationId(), //appid using virtual server name
0L, //version
0L, //lastaccesstime
0L, //maxInactiveInterval
ssoId, //extraParam (ssoId here but not needed)
null, //queryResult (not used here)
null, //FIXME instanceName
"removeInActiveSessions", //command
null, //state - not needed
null, //trunkState - not needed
null); //containerExtraParamState - not needed
this.doTransmit(transmitState);
}
/** set the container */
public void setContainer(Container container) {
this.container = container;
debug(" container: "+container);
}
/** set the parent of this store */
public void setParent(ReplicationSingleSignOn parent) {
this.parent = parent;
debug(" parent: "+parent);
}
public void updateLastAccessTime(String ssoId, long lat) throws IOException {
if(_logger.isLoggable(Level.FINER)) {
_logger.entering("ReplicationSSOStore>", "updateLastAccessTime", ssoId);
}
if(container == null) {
_logger.warning("ReplicationSSOStore>>updateLastAccessTime: container null");
}
if(parent == null) {
return;
}
BackingStore replicator = parent.getBackingStore();
if(_logger.isLoggable(Level.FINE)) {
_logger.fine("ReplicationSSOStore>>updateLastAccessTime: replicator: " + replicator);
}
try {
//FIXED: replicator.updateLastAccessTime(ssoId, lat, 0L);
SimpleMetadata smd = SimpleMetadataFactory.createSimpleMetadata(lat, 0L);
replicator.save(ssoId, smd, false);
} catch (BackingStoreException ex) {
// FIXME evaluate log level
if (_logger.isLoggable(Level.FINE)) {
_logger.log(Level.FINE, "exception occurred in remove", ex);
}
}
}
//end ReplicationSSOStorePoolElement methods
protected void transmitSSOEntry(String ssoId, SingleSignOnEntry ssoEntry, String command) throws IOException {
ReplicationState transmitState =
createReplicationState(ssoId, (HASingleSignOnEntry)ssoEntry, command);
if(_logger.isLoggable(Level.FINER)) {
_logger.entering("ReplicationSSOStore", "transmitSSOEntry",
new Object[]{ssoId, ssoEntry, command});
}
this.doTransmit(transmitState);
}
protected void transmitSSOEntry(String ssoId, SingleSignOnEntry ssoEntry, String command, boolean wait) throws IOException {
ReplicationState transmitState =
createReplicationState(ssoId, (HASingleSignOnEntry)ssoEntry, command);
if(_logger.isLoggable(Level.FINER)) {
_logger.entering("ReplicationSSOStore", "transmitSSOEntry",
new Object[]{ssoId, ssoEntry, command, wait});
}
this.doTransmit(transmitState, wait);
}
protected void transmitState(ReplicationState state, String command) throws IOException {
ReplicationState transmitState =
createRepairReplicationState(state, command);
if(_logger.isLoggable(Level.FINER)) {
_logger.exiting("ReplicatedSSOStore", "transmitState",
new Object[] {state, command});
}
this.doTransmit(transmitState);
}
protected void transmitState(ReplicationState state, String command, boolean wait) throws IOException {
ReplicationState transmitState =
createRepairReplicationState(state, command);
if(_logger.isLoggable(Level.FINER)) {
_logger.entering("ReplicatedSSOStore", "transmitState",
new Object[] {state, command, wait});
}
this.doTransmit(transmitState, wait);
}
protected ReplicationState createRepairReplicationState(ReplicationState state, String command)
throws IOException {
ReplicationState transmitState =
new ReplicationState(MODE_SSO, //sso mode
state.getId().toString(), //id (will be ssoId here)
state.getAppId(), //containerId analog of appid here
state.getVersion(), //version
state.getLastAccess(), //lastaccesstime
state.getMaxInactiveInterval(), //maxInactiveInterval (seconds)
state.getExtraParam(), //extraParam (is username here)
state.getQueryResult(), //queryResult
state.getInstanceName(), //instanceName
command, //command
state.getState(), //state
state.getTrunkState(), //trunkState
state.getContainerExtraParamsState()); //containerExtraParamState
if(_logger.isLoggable(Level.FINE)) {
_logger.fine("ReplicationSSOStore>>createRepairReplicationState:transmitState=" + transmitState);
}
return transmitState;
}
protected ReplicationState createReplicationState(String ssoId, HASingleSignOnEntry ssoEntry, String command)
throws IOException {
byte[] ssoEntryState = this.getByteArray(ssoEntry);
ReplicationState transmitState =
new ReplicationState(MODE_SSO, //sso mode
ssoId, //id (ssoId for this)
null, //appid
0L,
ssoEntry.lastAccessTime, //lastaccesstime
0L, //maxInactiveInterval (seconds)
ssoEntry.username, //extraParam (username here)
null, //queryResult (not used here)
null, //FIXME instanceName
command, //command
ssoEntryState, //state
null, //trunkState - not needed
null); //containerExtraParamState - not needed
if(_logger.isLoggable(Level.FINE)) {
_logger.fine("ReplicationSSOStore>>createReplicationState:transmitState=" + transmitState);
}
return transmitState;
}
protected SimpleMetadata createSimpleMetadata(String ssoId, HASingleSignOnEntry ssoEntry)
throws IOException {
if(_logger.isLoggable(Level.FINER)) {
_logger.entering("ReplicationSSOStore", "createSimpleMetadata",
new Object[]{ssoId, ssoEntry});
}
byte[] ssoEntryState = this.getByteArray(ssoEntry);
String userName = null;
if(_logger.isLoggable(Level.FINE)) {
_logger.fine("ReplicationSSOStore>>createSimpleMetadata:ssoEntry.principal=" + ssoEntry.principal);
}
if(ssoEntry.principal != null) {
userName = ssoEntry.principal.getName();
}
SSOExtraParams extraParams = ((HASingleSignOnEntry)ssoEntry).getExtraParameters();
((HASingleSignOnEntry)ssoEntry).getExtraParameters().initUsername();
if(_logger.isLoggable(Level.FINE)) {
_logger.fine("ReplicationSSOStore>>createSimpleMetadata:userName=" + userName);
}
SimpleMetadata<SSOExtraParams> metaData =
SimpleMetadataFactory.createSimpleMetadata(ssoEntry.getVersion(), //version
ssoEntry.lastAccessTime, //lastaccesstime
0L, //maxInactiveInterval (seconds)
ssoEntryState, //state
((HASingleSignOnEntry)ssoEntry).getExtraParameters());
if (_logger.isLoggable(Level.FINER)) {
_logger.exiting("ReplicationSsoStore", "createSimpleMetadata", metaData);
}
return metaData;
}
/**
* Given a byte[] containing session data, return a session
* object
*
* @param replicationState
* The byte[] with the session data
*
* @return
* A newly created session for the given session data, and associated
* with this Manager
*/
protected HASingleSignOnEntry getSSOEntry(ReplicationState replicationState)
throws IOException, ClassNotFoundException {
if (replicationState == null || replicationState.getState() == null) {
return null;
} else {
return getSSOEntry(replicationState.getState(),
replicationState.getExtraParam());
}
}
protected HASingleSignOnEntry getSSOEntry(SimpleMetadata metadata)
throws IOException, ClassNotFoundException {
if (metadata == null || metadata.getState() == null) {
return null;
} else {
return getSSOEntry(metadata.getState(),
((SSOExtraParams) metadata.getExtraParam()).username); // TODO :: is the last param correct?
}
}
protected HASingleSignOnEntry getSSOEntry(byte[] state, String userName)
throws IOException, ClassNotFoundException {
HASingleSignOnEntry _ssoEntry = null;
BufferedInputStream bis = null;
ByteArrayInputStream bais = null;
ObjectInputStream ois = null;
String ssoId = null;
try
{
bais = new ByteArrayInputStream(state);
bis = new BufferedInputStream(bais);
if(_logger.isLoggable(Level.FINE)) {
_logger.fine("ReplicationSSOStore>>getSSOEntry:userName=" + userName);
}
if(_logger.isLoggable(Level.FINEST)) {
_logger.finest("loaded ssoEntry from replicationssostore, length = "+state.length);
}
ois = new ObjectInputStream(bis);
if(ois != null) {
try {
_ssoEntry = readSSOEntry(ois);
}
finally {
if (ois != null) {
try {
ois.close();
bis = null;
}
catch (IOException e) {
}
}
}
}
}
catch(ClassNotFoundException e)
{
// FIXME evaluate log level
if (_logger.isLoggable(Level.FINE)) {
_logger.log(Level.FINE, "exception occurred in getSSOEntry", e);
}
throw e;
}
catch(IOException e)
{
// FIXME evaluate log level
if (_logger.isLoggable(Level.FINE)) {
_logger.log(Level.FINE, "exception occurred in getSSOEntry");
}
throw e;
}
if(_ssoEntry.username == null) {
_ssoEntry.username = userName;
}
return _ssoEntry;
}
/**
* Create a sso entry object from an input stream.
*
* @param ois
* The input stream containing the serialized session
*
* @return
* The resulting sso entry object
*/
public HASingleSignOnEntry readSSOEntry(ObjectInputStream ois)
throws ClassNotFoundException, IOException {
HASingleSignOnEntry ssoEntry
= new HASingleSignOnEntry(null, null, null, null, null, null);
ssoEntry.readObjectData(ois);
return ssoEntry;
}
//state is already formatted as a response
public void sendResponse(ReplicationState state) {
if(_logger.isLoggable(Level.FINE)) {
_logger.fine("ReplicationSSOStore>>sendResponse");
}
this.doReturnTransmit(state);
}
public void sendQueryResponse(ReplicationState state, String returnInstance) {
if(_logger.isLoggable(Level.FINE)) {
_logger.fine("ReplicationSSOStore>>sendQueryResponse:returnInstance: " + returnInstance);
}
this.doReturnQueryTransmit(state, returnInstance);
}
//FIXME: this could return null which would mean a problem
//might want to throw exception in that case
protected ReplicationState doReturnTransmit(ReplicationState transmitState) {
//FIXME for now test version
JxtaReplicationSender replicationSender =
JxtaReplicationSender.createInstance();
ReplicationState resultState =
replicationSender.sendReplicationStateResponse(transmitState);
return resultState;
}
//FIXME: this could return null which would mean a problem
//might want to throw exception in that case
protected ReplicationState doReturnQueryTransmit(ReplicationState transmitState, String returnInstance) {
//FIXME for now test version
JxtaReplicationSender replicationSender =
JxtaReplicationSender.createInstance();
ReplicationState resultState =
replicationSender.sendReplicationStateQueryResponse(transmitState, returnInstance, useReplicationUnicastLoadResponseBatching);
return resultState;
}
//FIXME: this could return null which would mean a problem
//might want to throw exception in that case
protected ReplicationState doTransmit(ReplicationState transmitState) {
//FIXME
JxtaReplicationSender replicationSender =
JxtaReplicationSender.createInstance();
ReplicationState resultState =
replicationSender.sendReplicationState(transmitState);
return resultState;
}
//FIXME: this could return null which would mean a problem
//might want to throw exception in that case
protected ReplicationState doTransmit(ReplicationState transmitState, boolean wait) {
//FIXME
JxtaReplicationSender replicationSender =
JxtaReplicationSender.createInstance();
ReplicationState resultState =
replicationSender.sendReplicationState(transmitState, wait);
return resultState;
}
/**
* Create an byte[] for the ssoEntry
*
* @param ssoEntry
* The ssoEntry we are serializing
*
*/
protected byte[] getByteArray(HASingleSignOnEntry ssoEntry)
throws IOException {
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
byte[] obs;
try {
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(new BufferedOutputStream(bos));
writeSSOEntry(ssoEntry, oos);
oos.close();
oos = null;
obs = bos.toByteArray();
}
finally {
if ( oos != null ) {
oos.close();
}
}
return obs;
}
/**
* Serialize an ssoEntry into an output stream.
*
* @param ssoEntry
* The ssoEntry to be serialized
*
* @param oos
* The output stream the session should be written to
*/
public void writeSSOEntry(HASingleSignOnEntry ssoEntry, ObjectOutputStream oos)
throws IOException {
if ( ssoEntry == null ) {
return;
}
ssoEntry.writeObjectData(oos);
//oos.writeObject(ssoEntry);
}
public void debug(String s){
if(debug && _logger.isLoggable(Level.FINE)) {
_logger.fine("ReplicationSSOStore: "+s);
}
}
private boolean debug = false; //set this to true for debug info
protected Container container = null;
protected ReplicationSingleSignOn parent = null;
}