/**
* Copyright 2008 The University of North Carolina at Chapel Hill
*
* 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.
*/
package edu.unc.lib.dl.fedora;
import static edu.unc.lib.dl.fedora.AuthorizationException.AuthorizationErrorType.INDETERMINATE;
import static edu.unc.lib.dl.fedora.AuthorizationException.AuthorizationErrorType.NOT_APPLICABLE;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.client.WebServiceFaultException;
import org.springframework.ws.client.WebServiceIOException;
import org.springframework.ws.client.core.WebServiceMessageCallback;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.springframework.ws.soap.SoapMessage;
import org.springframework.ws.soap.client.SoapFaultClientException;
import org.springframework.ws.soap.saaj.SaajSoapMessageFactory;
import org.springframework.ws.transport.http.HttpComponentsMessageSender;
import edu.unc.lib.dl.fedora.types.ArrayOfString;
import edu.unc.lib.dl.fedora.types.DescribeRepository;
import edu.unc.lib.dl.fedora.types.DescribeRepositoryResponse;
import edu.unc.lib.dl.fedora.types.FieldSearchQuery;
import edu.unc.lib.dl.fedora.types.FindObjects;
import edu.unc.lib.dl.fedora.types.FindObjectsResponse;
import edu.unc.lib.dl.fedora.types.GetDatastreamDissemination;
import edu.unc.lib.dl.fedora.types.GetDatastreamDisseminationResponse;
import edu.unc.lib.dl.fedora.types.GetObjectProfile;
import edu.unc.lib.dl.fedora.types.GetObjectProfileResponse;
import edu.unc.lib.dl.fedora.types.MIMETypedStream;
import edu.unc.lib.dl.fedora.types.ObjectFields;
import edu.unc.lib.dl.fedora.types.ObjectProfile;
/**
* The AccessClient is a Fedora 3.0 API-A SOAP client bean. After the fedoraUrl, username and password properties are
* set, call the init() method before using it.
* <p>
* For Fedora 3.0 API documentation, please consult the <a
* href="https://fedora-commons.org/confluence/display/FCR30/API-A">wiki</a>.
* </p>
*
* @author count0
*
*/
public class AccessClient extends WebServiceTemplate {
/**
* These are the actions support by the Fedora API-A service.
*
* @author count0
*
*/
private enum Action {
describeRepository("describeRepository"), findObjects("findObjects"), getDatastreamDissemination(
"getDatastreamDissemination"), getObjectProfile("getObjectProfile");
String uri = null;
Action(String action) {
uri = "http://www.fedora.info/definitions/1/0/api/#" + action;
}
WebServiceMessageCallback callback() {
return new WebServiceMessageCallback() {
@Override
public void doWithMessage(WebServiceMessage message) {
((SoapMessage) message).setSoapAction(uri);
}
};
}
}
private static final Log log = LogFactory.getLog(AccessClient.class);
private String fedoraContextUrl;
private String password;
private String username;
/**
* Encapsulates all marshalled web service calls.
*
* @param request
* a marshalled request object
* @param action
* the Fedora API-A action
* @return an unmarshalled response object
*/
private Object callService(Object request, Action action) throws FedoraException {
return callService(request, action, true);
}
private Object callService(Object request, Action action, boolean retry) throws FedoraException {
Object response = null;
try {
response = this.marshalSendAndReceive(request, action.callback());
} catch (WebServiceIOException e) {
if (e.getMessage().contains("503")) {
throw new FedoraTimeoutException(e);
} else if (java.net.SocketTimeoutException.class.isInstance(e.getCause())) {
throw new FedoraTimeoutException(e);
} else {
throw new ServiceException(e);
}
} catch (SoapFaultClientException e) {
try {
FedoraFaultMessageResolver.resolveFault(e);
} catch (AuthorizationException ae) {
if (retry && (NOT_APPLICABLE.equals(ae.getType()) || INDETERMINATE.equals(ae.getType()))) {
log.warn("Authorization failed, attempting to reestablish connection to Fedora.");
try {
this.init();
} catch (Exception e1) {
log.error("Failed to reestablish connection to Fedora", e);
throw ae;
}
return callService(request, action, false);
}
throw ae;
}
} catch (WebServiceFaultException e) {
throw new ServiceException(e);
}
return response;
}
public DescribeRepositoryResponse describeRepository(DescribeRepository o) throws ServiceException, FedoraException {
DescribeRepositoryResponse response = (DescribeRepositoryResponse) this.callService(o, Action.describeRepository);
return response;
}
public List<PID> findAllObjectPIDs() throws FedoraException, ServiceException {
List<PID> result = new ArrayList<PID>();
FindObjects fo = new FindObjects();
fo.setMaxResults(BigInteger.valueOf(100000));
FieldSearchQuery query = new FieldSearchQuery();
fo.setQuery(query);
ArrayOfString fields = new ArrayOfString();
fields.getItem().add("pid");
fo.setResultFields(fields);
FindObjectsResponse response = (FindObjectsResponse) this.callService(fo, Action.findObjects);
for (ObjectFields o : response.getResult().getResultList().getObjectFields()) {
String s = o.getPid().getValue();
result.add(new PID(s));
}
return result;
}
public MIMETypedStream getDatastreamDissemination(PID pid, String dsid, String timestamp) throws FedoraException,
ServiceException {
GetDatastreamDissemination req = new GetDatastreamDissemination();
req.setPid(pid.getPid());
// TODO: test that NotFoundException is throw for non-existant datastreams
req.setDsID(dsid);
if (timestamp != null) {
req.setAsOfDateTime(timestamp);
}
GetDatastreamDisseminationResponse response = (GetDatastreamDisseminationResponse) this.callService(req,
Action.getDatastreamDissemination);
return response.getDissemination();
}
public ObjectProfile getObjectProfile(PID pid, String timestamp) throws FedoraException, ServiceException {
GetObjectProfile req = new GetObjectProfile();
req.setPid(pid.getPid());
if (timestamp != null) {
req.setAsOfDateTime(timestamp);
}
GetObjectProfileResponse response = (GetObjectProfileResponse) this.callService(req, Action.getObjectProfile);
return response.getObjectProfile();
}
/**
* Get the Fedora base URL.
*
* @return Fedora base URL
*/
public String getFedoraContextUrl() {
return fedoraContextUrl;
}
/**
* Get the Fedora password.
*
* @return
*/
public String getPassword() {
return password;
}
/**
* Get the Fedora username
*
* @return
*/
public String getUsername() {
return username;
}
/**
* Initializes this client bean, calling the initializers of dependencies.
*
* @throws Exception
* when initialization fails
*/
public void init() throws Exception {
SaajSoapMessageFactory msgFactory = new SaajSoapMessageFactory();
msgFactory.afterPropertiesSet();
this.setMessageFactory(msgFactory);
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("edu.unc.lib.dl.fedora.types");
marshaller.afterPropertiesSet();
this.setMarshaller(marshaller);
this.setUnmarshaller(marshaller);
HttpComponentsMessageSender sender = new HttpComponentsMessageSender();
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(this.username, this.password);
sender.setCredentials(credentials);
sender.afterPropertiesSet();
this.setMessageSender(sender);
// this.setFaultMessageResolver(new FedoraFaultMessageResolver());
this.setDefaultUri(this.getFedoraContextUrl() + "/services/access");
this.afterPropertiesSet();
}
/**
* Set the Fedora base URL.
*
* @param fedoraUrl
*/
public void setFedoraContextUrl(String fedoraContextUrl) {
this.fedoraContextUrl = fedoraContextUrl;
}
/**
* Set the Fedora password.
*
* @param password
*/
public void setPassword(String password) {
this.password = password;
}
/**
* Set the Fedora username
*
* @param username
*/
public void setUsername(String username) {
this.username = username;
}
}