/*
* Copyright (C) 2005-2012 BetaCONCEPT Limited
*
* This file is part of Astroboa.
*
* Astroboa 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 3 of the License, or
* (at your option) any later version.
*
* Astroboa 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 Astroboa. If not, see <http://www.gnu.org/licenses/>.
*/
package org.betaconceptframework.astroboa.client;
import javax.security.auth.Subject;
import org.apache.commons.lang.StringUtils;
import org.betaconceptframework.astroboa.api.model.exception.CmsException;
import org.betaconceptframework.astroboa.api.security.AstroboaCredentials;
import org.betaconceptframework.astroboa.api.security.IdentityPrincipal;
import org.betaconceptframework.astroboa.api.security.exception.CmsClientNotLoggedInException;
import org.betaconceptframework.astroboa.api.security.management.IdentityStore;
import org.betaconceptframework.astroboa.api.service.ContentService;
import org.betaconceptframework.astroboa.api.service.DefinitionService;
import org.betaconceptframework.astroboa.api.service.ImportService;
import org.betaconceptframework.astroboa.api.service.RepositoryService;
import org.betaconceptframework.astroboa.api.service.RepositoryUserService;
import org.betaconceptframework.astroboa.api.service.SerializationService;
import org.betaconceptframework.astroboa.api.service.SpaceService;
import org.betaconceptframework.astroboa.api.service.TaxonomyService;
import org.betaconceptframework.astroboa.api.service.TopicService;
import org.betaconceptframework.astroboa.client.service.ContentServiceClientWrapper;
import org.betaconceptframework.astroboa.client.service.DefinitionServiceClientWrapper;
import org.betaconceptframework.astroboa.client.service.IdentityStoreClientWrapper;
import org.betaconceptframework.astroboa.client.service.ImportServiceClientWrapper;
import org.betaconceptframework.astroboa.client.service.RepositoryServiceClientWrapper;
import org.betaconceptframework.astroboa.client.service.RepositoryUserServiceClientWrapper;
import org.betaconceptframework.astroboa.client.service.SerializationServiceClientWrapper;
import org.betaconceptframework.astroboa.client.service.SpaceServiceClientWrapper;
import org.betaconceptframework.astroboa.client.service.TaxonomyServiceClientWrapper;
import org.betaconceptframework.astroboa.client.service.TopicServiceClientWrapper;
import org.betaconceptframework.astroboa.context.AstroboaClientContext;
import org.betaconceptframework.astroboa.context.AstroboaClientContextHolder;
import org.betaconceptframework.astroboa.model.factory.CmsRepositoryEntityFactory;
/**
* This is the starting point for accessing a Astroboa repository server and
* make use of its services.
*
* <p>
* In order to successfully use services of a Astroboa repository server, Astroboa repository client
* must successfully connect to a repository at that server.
* </p>
*
* <p>
* This can be done following either ways :
* <ul>
* <li>Declaring ONLY the server name on the constructor {@link #AstroboaClient(String)} and at some point of time BEFORE using any of
* the services, user must login using one of the provided login methods.
* <li>Instantiate the client with the default constructor {@link #AstroboaClient()}, supply Astroboa server with
* {@link #initialize(String)} and then BEFORE using any of the
* services, user must login using one of the provided login methods.
* </ul>
* </p>
*
* <p>
* Astroboa repository client can connect to a local or a remote Astroboa repository server. A local Astroboa repository server
* runs in the same JVM with this client whereas a remote Astroboa repository server runs in a different JVM from this client's JVM.
* </p>
*
* <p>
* Astroboa repository client determines whether to connect to a local or remote Astroboa repository server from the provided
* server name. If this is BLANK or has the value {@link #INTERNAL_CONNECTION} then it tries to find a Astroboa repository server in
* the currently running JVM, otherwise it uses the provided server name or ip and port to connect through TCP/IP to a remote server.
* </p>
*
* <p>
* Note that if the specified server name is <code>localhost</code> or <code>127.0.0.1</code> then it will try first to connect locally
* and if that is not successful then it will try to connect remotely.
* </p>
*
* <p>
* By default all Astroboa repository servers listen to default port 1099. If this is changed then append the server name or ip
* with the new port separated by ':', e.g. localhost:1098
* </p>
*
* <p>
* Astroboa repository client provides also access to the {@link IdentityStore} for the connected repository.
* </p>
*
* <p>
* The main idea is that there could be different users accessing an {@link IdentityStore} which by its turn
* manages users for a Astroboa repository.
* </p>
*
* <p>
* Finally, it should be noted that this class is NOT Thread safe, i.e. user must make sure that the same instance
* must not be accessed by more than one threads at the same time.
*
* </p>
*
* @author Gregory Chomatas (gchomatas@betaconcept.com)
* @author Savvas Triantafyllou (striantafyllou@betaconcept.com)
*
*/
public class AstroboaClient {
public final static String INTERNAL_CONNECTION = "internalConnection";
private String serverHostNameOrIpAndPortToConnectTo;
private ContentService contentService;
private RepositoryService repositoryService;
private RepositoryUserService repositoryUserService;
private TaxonomyService taxonomyService;
private TopicService topicService;
private SpaceService spaceService;
private DefinitionService definitionService;
private SerializationService serializationService;
private ImportService importService;
private String authenticationToken;
private CmsRepositoryEntityFactory cmsRepositoryEntityFactory;
private AstroboaClientContext clientContext;
private IdentityStore identityStore;
private String loggedInUser;
private String connectedRepositoryId;
private String externalIdentityStoreJNDIName;
private boolean anonymousUser = false;
public AstroboaClient(){
}
public AstroboaClient(String serverHostNameOrIpAndPortToConnectTo){
initialize(serverHostNameOrIpAndPortToConnectTo);
}
/**
* @see RepositoryService#login(String, AstroboaCredentials)
*
* @param repositoryId Repository identifier as provided
* in repositories-conf.xml
*
* @param credentials Credentials used to authenticate user that wants to connect to repository.
*/
public void login(String repositoryId, AstroboaCredentials credentials) {
login(repositoryId, credentials, null);
}
/**
*
* @see RepositoryService#login(String, AstroboaCredentials, String)
*
* @param repositoryId Repository identifier as provided
* in repositories-conf.xml
*
* @param credentials Credentials used to authenticate user that wants to connect to repository.
*
* @param permanentKey representing a trusted client whose token is never expired
*/
public void login(String repositoryId, AstroboaCredentials credentials, String permanentKey) {
if(getRepositoryService() != null){
authenticationToken = getRepositoryService().login(repositoryId, credentials, permanentKey);
cmsRepositoryEntityFactory = new CmsRepositoryEntityFactory(){
@Override
public String getAuthenticationToken() {
return authenticationToken;
}
};
if (getIdentityStore()!=null){
//Login to Identity Store as well with the same credentials
((IdentityStoreClientWrapper)getIdentityStore()).login(credentials, permanentKey);
}
if (credentials == null){
keepRepositoryAndUser(repositoryId, "unknown");
}
else{
keepRepositoryAndUser(repositoryId, credentials.getUsername());
}
}
}
/**
* Allows client to login to an Astroboa repository without authenticating
* user. This method runs only within Local context for security reasons.
*
* @param repositoryId Repository identifier as provided
* in repositories-conf.xml
*
* @param subject Subject created during authentication by another system.
*/
public void login(String repositoryId, Subject subject) {
login(repositoryId, subject, null);
}
/**
* Allows client to login to an Astroboa repository without authenticating
* user. This method runs only within Local context for security reasons.
*
* @param repositoryId Repository identifier as provided
* in repositories-conf.xml
*
* @param subject Subject created during authentication by another system.
*
* @param permanentKey representing a trusted client whose token is never expired
*/
public void login(String repositoryId, Subject subject, String permanentKey) {
if (getRepositoryService() != null){
authenticationToken = ((RepositoryServiceClientWrapper)getRepositoryService()).login(repositoryId, subject, permanentKey);
cmsRepositoryEntityFactory = new CmsRepositoryEntityFactory(){
@Override
public String getAuthenticationToken() {
return authenticationToken;
}
};
//Login to Identity Store as well with the same credentials
if (getIdentityStore()!=null)
{
((IdentityStoreClientWrapper)getIdentityStore()).login(subject, permanentKey);
}
if (subject == null )
{
//Normally this should never happen
keepRepositoryAndUser(repositoryId, "unknown");
}
else if (subject.getPrincipals(IdentityPrincipal.class)!= null &&
! subject.getPrincipals(IdentityPrincipal.class).isEmpty())
{
keepRepositoryAndUser(repositoryId, subject.getPrincipals(IdentityPrincipal.class).iterator().next().getName());
}
else
{
keepRepositoryAndUser(repositoryId, subject.toString());
}
}
}
/**
* Allows client to login to an Astroboa repository without providing
* admin user name and password.
*
* @param repositoryId Repository identifier as provided
* in repositories-conf.xml
*
* @param key Predefined secret key provided in repositories-conf.xml
*
*/
public void loginAsAdministrator(String repositoryId, String key) {
loginAsAdministrator(repositoryId, key, null);
}
/**
* Allows client to login to an Astroboa repository without providing
* admin user name and password.
*
* @param repositoryId Repository identifier as provided
* in repositories-conf.xml
*
* @param key Predefined secret key provided in repositories-conf.xml
*
* @param permanentKey representing a trusted client whose token is never expired
*/
public void loginAsAdministrator(String repositoryId, String key, String permanentKey) {
if (getRepositoryService() != null){
authenticationToken = ((RepositoryServiceClientWrapper)getRepositoryService()).
loginAsAdministrator(repositoryId, key, permanentKey);
cmsRepositoryEntityFactory = new CmsRepositoryEntityFactory(){
@Override
public String getAuthenticationToken() {
return authenticationToken;
}
};
//Login to Identity Store as well with the same credentials
if (getIdentityStore()!=null)
{
((IdentityStoreClientWrapper)getIdentityStore()).loginAsAdministrator(key, permanentKey);
}
keepRepositoryAndUser(repositoryId, "Administrator defined in configuration");
}
}
/**
* Allows client to login to an Astroboa repository as Anonymous user.
*
* @param repositoryId Repository identifier as provided
* in repositories-conf.xml
*
*/
public void loginAsAnonymous(String repositoryId) {
loginAsAnonymous(repositoryId, null);
}
/**
* Allows client to login to an Astroboa repository as Anonymous user.
*
* @param repositoryId Repository identifier as provided
* in repositories-conf.xml
*
* @param permanentKey representing a trusted client whose token is never expired
*/
public void loginAsAnonymous(String repositoryId, String permanentKey) {
if (getRepositoryService() != null){
authenticationToken = ((RepositoryServiceClientWrapper)getRepositoryService()).
loginAsAnonymous(repositoryId, permanentKey);
cmsRepositoryEntityFactory = new CmsRepositoryEntityFactory(){
@Override
public String getAuthenticationToken() {
return authenticationToken;
}
};
//Login to Identity Store as well with the same credentials
if (getIdentityStore()!=null)
{
((IdentityStoreClientWrapper)getIdentityStore()).loginAsAnonymous(permanentKey);
}
keepRepositoryAndUser(repositoryId, IdentityPrincipal.ANONYMOUS);
}
}
/**
* Configure client to connect to provided Astroboa Server
*
* @param serverHostNameOrIpAndPortToConnectTo The dns name or ip of the server that provides the Astroboa Repository Services
*/
public void initialize(String serverHostNameOrIpAndPortToConnectTo) {
this.serverHostNameOrIpAndPortToConnectTo = serverHostNameOrIpAndPortToConnectTo;
resetClientState();
}
private void resetClientState(){
contentService = null;
repositoryService = null;
repositoryUserService = null;
taxonomyService = null;
topicService = null;
spaceService = null;
definitionService = null;
serializationService = null;
importService = null;
identityStore = null;
authenticationToken = null;
cmsRepositoryEntityFactory = null;
connectedRepositoryId = null;
clientContext = null;
loggedInUser = null;
externalIdentityStoreJNDIName = null;
anonymousUser = false;
}
/**
*
* @return
*/
public AstroboaClient copy(){
if (authenticationToken == null){
throw new CmsClientNotLoggedInException(serverHostNameOrIpAndPortToConnectTo);
}
AstroboaClient clone = new AstroboaClient(serverHostNameOrIpAndPortToConnectTo);
clone.authenticationToken = new String(authenticationToken.getBytes());
clone.cmsRepositoryEntityFactory = cmsRepositoryEntityFactory;
clone.clientContext = clientContext;
clone.externalIdentityStoreJNDIName = externalIdentityStoreJNDIName;
clone.clientContext = clientContext;
if (connectedRepositoryId != null){
clone.connectedRepositoryId = new String(connectedRepositoryId);
}
if (loggedInUser != null){
clone.loggedInUser = new String(loggedInUser);
}
clone.anonymousUser = anonymousUser;
if (clone.getIdentityStore()!=null){
AstroboaClient identityStoreClient = ((IdentityStoreClientWrapper)getIdentityStore()).getAstroboaClientForIdentityStore();
if (identityStoreClient == null ||
identityStoreClient == this){
((IdentityStoreClientWrapper)clone.getIdentityStore()).setAstroboaClientForIdentityStore(clone);
}
else{
((IdentityStoreClientWrapper)clone.getIdentityStore()).setAstroboaClientForIdentityStore(identityStoreClient.copy());
}
}
clone.activateClientContext();
return clone;
}
/**
* Retrieve {@link ContentService}
*
* @return Content Service
*/
public ContentService getContentService(){
if (contentService == null){
contentService = new ContentServiceClientWrapper(this, serverHostNameOrIpAndPortToConnectTo);
}
return contentService;
}
/**
* Retrieve {@link RepositoryService}
*
* @return Repository Service
*/
public RepositoryService getRepositoryService(){
if (repositoryService == null){
repositoryService = new RepositoryServiceClientWrapper(this, serverHostNameOrIpAndPortToConnectTo);
}
return repositoryService;
}
/**
* Retrieve {@link RepositoryUserService}
*
* @return RepositoryUserService
*/
public RepositoryUserService getRepositoryUserService(){
if (repositoryUserService == null){
repositoryUserService = new RepositoryUserServiceClientWrapper(this, serverHostNameOrIpAndPortToConnectTo);
}
return repositoryUserService;
}
/**
* Retrieve Astroboa server dns name of ip
*
* @return Astroboa server dns name of ip
*/
public String getServerHostNameOrIpAndPortToConnectTo() {
return serverHostNameOrIpAndPortToConnectTo;
}
/**
* Retrieve {@link TaxonomyService}
*
* @return TaxonomyService
*/
public TaxonomyService getTaxonomyService() {
if (taxonomyService == null){
taxonomyService = new TaxonomyServiceClientWrapper(this, serverHostNameOrIpAndPortToConnectTo);
}
return taxonomyService;
}
/**
* Retrieve {@link TopicService}
*
* @return TopicService
*/
public TopicService getTopicService() {
if (topicService == null){
topicService = new TopicServiceClientWrapper(this,serverHostNameOrIpAndPortToConnectTo);
}
return topicService;
}
/**
* Retrieve {@link SpaceService}
*
* @return SpaceService
*/
public SpaceService getSpaceService() {
if (spaceService == null){
spaceService = new SpaceServiceClientWrapper(this,serverHostNameOrIpAndPortToConnectTo);
}
return spaceService;
}
/**
* Retrieve {@link DefinitionService}
*
* @return DefinitionService
*/
public DefinitionService getDefinitionService() {
if (definitionService == null){
definitionService = new DefinitionServiceClientWrapper(this,serverHostNameOrIpAndPortToConnectTo);
}
return definitionService;
}
public String getAuthenticationToken() {
return authenticationToken;
}
public void setAuthenticationToken(String authenticationToken) {
this.authenticationToken = authenticationToken;
}
/**
* Retrieve {@link CmsRepositoryEntityFactory}
*
* @return CmsRepositoryEntityFactory
*/
public CmsRepositoryEntityFactory getCmsRepositoryEntityFactory(){
if (cmsRepositoryEntityFactory== null){
throw new CmsException("Astroboa client must log in before it can use the entity factory");
}
return cmsRepositoryEntityFactory;
}
/**
* Used to activate this client's context in current Thread
*/
public void activateClientContext(){
if (clientContext != null){
AstroboaClientContextHolder.registerClientContext(clientContext, true);
}
else if (authenticationToken != null){
//We have authentication token. Use that
AstroboaClientContextHolder.activateClientContextForAuthenticationToken(authenticationToken);
}
}
/**
* @param clientContext the clientContext to set
*/
public void setClientContext(AstroboaClientContext clientContext) {
this.clientContext = clientContext;
}
/**
* Retrieve {@link IdentityStore}
*
* @return IdentityStore
*/
public IdentityStore getIdentityStore() {
if (identityStore == null){
if (StringUtils.isBlank(authenticationToken)){
throw new CmsException("You have to login to repository before you can use IdentityStore");
}
identityStore = new IdentityStoreClientWrapper(this,serverHostNameOrIpAndPortToConnectTo);
}
return identityStore;
}
/**
* Return information about Astroboa server, Astroboa repository and logged in user
* @return
*/
public String getInfo(){
StringBuilder builder = new StringBuilder();
builder.append("AstroboaClient for server ");
builder.append((StringUtils.isBlank(serverHostNameOrIpAndPortToConnectTo) || INTERNAL_CONNECTION.equals(serverHostNameOrIpAndPortToConnectTo) ? "localhost":
serverHostNameOrIpAndPortToConnectTo));
builder.append(", Repository : ");
builder.append(connectedRepositoryId);
builder.append(", User : ");
builder.append(loggedInUser);
builder.append(", Authentication Token : ");
builder.append(authenticationToken);
return builder.toString();
}
private void keepRepositoryAndUser(String repositoryId, String username){
connectedRepositoryId =repositoryId;
loggedInUser = username;
anonymousUser = StringUtils.equals(IdentityPrincipal.ANONYMOUS, loggedInUser);
}
public String getConnectedRepositoryId() {
return connectedRepositoryId;
}
public boolean isConnectedToARemoteServer(){
if (getRepositoryService() != null){
return ((RepositoryServiceClientWrapper)getRepositoryService()).isClientConnectedToARemoteServer();
}
return false;
}
/**
* Retrieve {@link SerializationService}
*
* @return Export Service
*/
public SerializationService getSerializationService(){
if (serializationService == null){
serializationService = new SerializationServiceClientWrapper(this, serverHostNameOrIpAndPortToConnectTo);
}
return serializationService;
}
/**
* Retrieve {@link ImportService}
*
* @return Import Service
*/
public ImportService getImportService(){
if (importService == null){
importService = new ImportServiceClientWrapper(this, serverHostNameOrIpAndPortToConnectTo);
}
return importService;
}
@Override
public String toString() {
return getInfo();
}
public void setExternalIdentityStoreJNDIName(
String externalIdentityStoreJNDIName) {
this.externalIdentityStoreJNDIName = externalIdentityStoreJNDIName;
}
public String getExternalIdentityStoreJNDIName() {
return externalIdentityStoreJNDIName;
}
public boolean sessionHasExpired(){
return authenticationToken == null ||
(getRepositoryService() != null && ((RepositoryServiceClientWrapper)getRepositoryService()).tokenHasExpired(authenticationToken));
}
public String getIdentity(){
return loggedInUser;
}
public boolean isUserAnonymous(){
return anonymousUser;
}
}