How to perform a parallel CMIS requests in Alfresco? - alfresco

I have several CMIS requests and need to do them in parallel. But when I try it (using CompletableFutire or stream().parallel(), I got:
java.util.concurrent.ExecutionException: net.sf.acegisecurity.AuthenticationCredentialsNotFoundException: A valid SecureContext was not provided in the RequestContext
For that queries I do :
#Autowired
#Qualifier("searchService")
private org.alfresco.service.cmr.search.SearchService searchService;
....
searchService.query(searchParameters);
What am I doing wrong?
The following code is one of my attempt to perform CMIS in parallel:
List<CompletableFuture<Form14Row>> requests = Arrays.asList(setUpRow(1,beginnigString, endString, docType, NDBaseDocumentModel.DOC_KIND_GOST_R, searchParameters, "ГОСТ Р"),
setUpRow(2,beginnigString, endString, docType, NDBaseDocumentModel.DOC_KIND_GOST, searchParameters, "ГОСТ") );
CompletableFuture<Void> allRequests = CompletableFuture.allOf(
requests.toArray(new CompletableFuture[requests.size()])
);
CompletableFuture<List<Form14Row>> allPageContentsFuture = allRequests.thenApply(v -> {
return requests.stream()
.map(pageContentFuture -> pageContentFuture.join())
.collect(Collectors.toList());
});
//java.util.concurrent.ExecutionException: net.sf.acegisecurity.AuthenticationCredentialsNotFoundException: A valid SecureContext was not provided in the RequestContext
try {
List<Form14Row> rowss = allPageContentsFuture.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
...
private CompletableFuture<Form14Row> setUpRow(Integer index, String beginnigString, String endString, String docType, String docKind, SearchParameters searchParameters, String groupPosition) {
return CompletableFuture.supplyAsync(() -> {
String cql = "SELECT p.cmis:objectId FROM ecmcnddoc:common_attr_aspect AS d JOIN ecmcnddoc:biblio_attr_aspect AS p ON d.cmis:objectId = p.cmis:objectId JOIN ecmcnddoc:reg_attr_aspect AS s ON s.cmis:objectId = p.cmis:objectId JOIN ecmcnddoc:spec_attr_aspect AS asp ON asp.cmis:objectId = p.cmis:objectId WHERE p.cmis:objectTypeId='D:" + docType + "' AND d.ecmcnddoc:doc_kind_cp_ecmcdict_value='" + docKind + "' AND p.ecmcnddoc:biblio_fond='" + NDBaseDocumentModel.BIBLIO_FUND + "' AND s.ecmcnddoc:doc_reg_date >= TIMESTAMP '" + beginnigString + "T00:00:00.000+00:00' AND s.ecmcnddoc:doc_reg_date <= TIMESTAMP '" + endString + "T00:00:00.000+00:00' AND (asp.ecmcnddoc:doc_status='draft' OR asp.ecmcnddoc:doc_status='actual')";
searchParameters.setQuery(cql);
ResultSet rs = customSearchService.query(searchParameters); // Exception here
Form14Row isoRow = new Form14Row();
isoRow.setCount(rs.length());
isoRow.setIndex(index);
isoRow.setKindName(groupPosition);
return isoRow;
});
}
Everything works fine in serial execution

Security context is binded to main thread and is not propagated to children threads, you would need to set one.
Maybe you could try to adapt this snippet:
CompletableFuture.runAsync(() -> {
try {
AuthenticationUtil.pushAuthentication();
AuthenticationUtil.setFullyAuthenticatedUser(userName);
// Do your stuff...
} finally {
AuthenticationUtil.popAuthentication();
}
});

Related

Whats wrong with this Async HystrixCommand?

I need to send notifications from time to time, I perform this task asynchronously. I'm using HystrixCommand as below to perform an asynchronous RestTemplate call which is not working:
#HystrixCommand
public Future<String> notify(final Query query) {
return new AsyncResult<String>() {
#Override
public String invoke() {
String result = null;
try {
ResponseEntity<HashMap> restExchange = restTemplate.exchange(url,
HttpMethod.POST,
new HttpEntity<String>(mapper.writeValueAsString(queryMap), httpHeaders),
HashMap.class);
LOGGER.info("Response code from " + url + " = " + restExchange.getStatusCodeValue());
result = ""+ restExchange.getStatusCodeValue();
} catch(Exception e) {
LOGGER.error("Exception while sending notification! Message = " + e.getMessage(), e);
}
return result;
}
};
}
This is what I was trying to do earlier(which didn't work either):
#HystrixCommand
public String notify(final Query query) {
new Thread(new Runnable() {
#Override
public void run() {
try {
ResponseEntity<HashMap> restExchange = restTemplate.exchange(url, HttpMethod.POST,
new HttpEntity<String>(mapper.writeValueAsString(queryMap), httpHeaders), HashMap.class);
LOGGER.info("Response code from " + url + " = " + restExchange.getStatusCodeValue());
} catch (Exception e) {
LOGGER.error("Exception while sending notification! Message = " + e.getMessage(), e);
}
}
}).start();
}
P.S: Reason for adding sleuth to the tags is, performing this in a new Thread does not propagate the headers(baggage-*) so trying this hoping the Hystrix command will do the trick
Is the method notify being called from a method in the same class? If that is the case, try calling the method notify directly from a different class where the notify method's enclosing class is injected as a dependency.
Basically, try doing this:
Instead of this:
When using Runnable you have to wrap them in a trace representation. i.e. TraceRunnable. It's there in the docs - http://cloud.spring.io/spring-cloud-sleuth/spring-cloud-sleuth.html#_runnable_and_callable .
As for why the Hystrix stuff doesn't work - most likely it's related to https://github.com/spring-cloud/spring-cloud-sleuth/issues/612 .

Get Commit of a particular SHA-1 in jGit

I have a SHA-1 of a newly cloned repository. I want the author indent of this SHA-1.
So I need to use RevWalk and iterate the whole repository? Or is there a findXX method or other code I can use to get the RevCommit or another object that has the PersonIdent?
What I tried:
public void authorInfoOf(Repository repo, AnyObjectId head) {
try {
try (RevWalk walk = new RevWalk(repo)) {
ObjectDatabase db = repo.getObjectDatabase();
ObjectLoader k = repo.newObjectReader().open(head);
ObjectReader s;
// repo.newObjectReader().open(head);
ObjectStream st = k.openStream();
// RevWalk rw2 = new RevWalk(k);
RevCommit commit = null;// walk.parseCommit(ref.getObjectId());
PersonIdent authorIndent = commit.getAuthorIdent();
System.out.println("\nCommit-Message: " + commit.getFullMessage() + " " + authorIndent.getEmailAddress());
}
} catch (Exception e) {
System.out.println("Authir info of Anybject id Err " + e);
e.printStackTrace();
}
}
The RevCommit represents a particular commit in a Git repository. Use RevWalk::parseCommit() to obtain the RevCommit for a specific object id/SHA-1.
For example:
try( RevWalk walk = new RevWalk( repository ) ) {
RevCommit commit = walk.parseCommit( ref.getObjectId() );
}
parseCommit returns the matching commit object for the given ObjectId.
In order to convert a SHA-1 (string) into an ObjectId, use ObjectId::fromString():
ObjectId commitId = ObjectId.fromString( "ab434..." );
See also: How to obtain the RevCommit or ObjectId from a SHA1 ID string with JGit?
In the above example, a Ref was used to reference the object id. Refs represent named references to object ids like branches, tags, or special refs like HEAD. Repository::exactRef() can be used to resolve a string to a Ref object.
For example:
Ref headRef = repository.exactRef( "HEAD" );
This worked for me:
public RevCommit getCommit(String idString, Repository repository, Git git){
try{
ObjectId o1 = repository.resolve(idString);
if(o1 != null){
try( RevWalk walk = new RevWalk( repository ) ) {
RevCommit commit = walk.parseCommit( o1 );
PersonIdent ai = commit.getAuthorIdent();
System.out.println("bbb >>" + ai.getEmailAddress() + "|" + ai.getTimeZone() + "|" + ai.getWhen() + ", ref :" + idString);
return commit;
}
}else{
System.err.println("Could not get commit with SHA :" + idString);
}
}catch (Exception e) {
System.out.println("err :" + e);
e.printStackTrace();
}
return null;
}

ASP.NET & SQL Server : Incorrect syntax near '?'

I am receiving an error
Incorrect syntax near ?
when trying to use a update query function. The code is from SagePay http://www.sagepay.co.uk/file/12136/download-document/DotNetkit%201.2.6.7%20-%202014-08-14.zip?token=BJFwtM7qNnnm5ZCc_l_dOhq4INB0cQTPCxCd5JOpeh4 and relates to their server InFrame implementation.
As far as I can see the order is being passed correctly, and the list of fields, match the database, just not understanding why I am seeing this error. The code was originally created for MySQL but have had to adapt to SQL Server.
I've tried debugging, but cannot actually see what is being committed to the SQL Server from cmd.ExecuteNonQuery(); any help would be much appreciated, here is the code:
private static readonly List<String> FieldNames = new List<String>
{
VendorTxCodeField, AddressResultField, AddressStatusField, AmountField, AvsCv2Field, BankAuthCodeField, BasketField,
BillingFirstnamesField, BillingSurnameField, BillingPhoneField, BillingAddress1Field, BillingAddress2Field, BillingCityField,
BillingPostCodeField, BillingStateField, BillingCountryField, DeclineCodeField, DeliveryFirstnamesField, DeliverySurnameField, DeliveryPhoneField,
DeliveryAddress1Field, DeliveryAddress2Field, DeliveryCityField, DeliveryPostCodeField, DeliveryStateField, DeliveryCountryField,
CapturedAmountField, CardTypeField, CavvField, CurrencyField, CustomerEmailField, Cv2ResultField, ExpiryDateField, FraudResponseField,
GiftAidField, Last4DigitsField, LastUpdatedField, PayerIdField, PayerStatusField, PostCodeResultField,
RelatedVendorTxCodeField, SecurityKeyField, StatusField, StatusMessageField, SurchargeField, ThreeDSecureStatusField,
TransactionTypeField, TxAuthNoField, TokenIdField, VpsTxIdField
};
public static bool UpdateOrder(Order order, string vendorTxCode)
{
var result = false;
SqlConnection conn = null;
try
{
conn = new SqlConnection(ConnectionString);
conn.Open();
var cmd = new SqlCommand
{
Connection = conn, CommandText = "UPDATE Orders SET " + string.Join(",", FieldNames.Select(field => field + "=?" + field).ToList()) + " WHERE " + VendorTxCodeField + " =?" + VendorTxCodeField
};
cmd.Prepare();
AddOrderParameters(cmd, order);
cmd.ExecuteNonQuery();
result = true;
}
catch (SqlException ex)
{
Console.WriteLine("Error: {0}", ex);
}
finally
{
if (conn != null)
{
conn.Close();
}
}
return result;
}
private static void AddOrderParameters(SqlCommand command, Order order)
{
command.Parameters.AddWithValue(VendorTxCodeField, order.VendorTxCode);
command.Parameters.AddWithValue(AddressResultField, order.AddressResult);
command.Parameters.AddWithValue(AddressStatusField, order.AddressStatus);
command.Parameters.AddWithValue(AmountField, order.Amount);
command.Parameters.AddWithValue(AvsCv2Field, order.AvsCv2);
command.Parameters.AddWithValue(BankAuthCodeField, order.BankAuthCode);
command.Parameters.AddWithValue(BasketField, order.Basket);
command.Parameters.AddWithValue(BillingAddress1Field, order.BillingAddress1);
command.Parameters.AddWithValue(BillingAddress2Field, order.BillingAddress2);
command.Parameters.AddWithValue(BillingCityField, order.BillingCity);
command.Parameters.AddWithValue(BillingCountryField, order.BillingCountry);
command.Parameters.AddWithValue(BillingFirstnamesField, order.BillingFirstnames);
command.Parameters.AddWithValue(BillingPhoneField, order.BillingPhone);
command.Parameters.AddWithValue(BillingPostCodeField, order.BillingPostCode);
command.Parameters.AddWithValue(BillingStateField, order.BillingState);
command.Parameters.AddWithValue(BillingSurnameField, order.BillingSurname);
command.Parameters.AddWithValue(CapturedAmountField, order.CapturedAmount);
command.Parameters.AddWithValue(CardTypeField, order.CardType);
command.Parameters.AddWithValue(CavvField, order.Cavv);
command.Parameters.AddWithValue(CurrencyField, order.Currency);
command.Parameters.AddWithValue(CustomerEmailField, order.CustomerEmail);
command.Parameters.AddWithValue(Cv2ResultField, order.Cv2Result);
command.Parameters.AddWithValue(DeclineCodeField, order.DeclineCode);
command.Parameters.AddWithValue(DeliveryAddress1Field, order.DeliveryAddress1);
command.Parameters.AddWithValue(DeliveryAddress2Field, order.DeliveryAddress2);
command.Parameters.AddWithValue(DeliveryCityField, order.DeliveryCity);
command.Parameters.AddWithValue(DeliveryCountryField, order.DeliveryCountry);
command.Parameters.AddWithValue(DeliveryFirstnamesField, order.DeliveryFirstnames);
command.Parameters.AddWithValue(DeliveryPhoneField, order.DeliveryPhone);
command.Parameters.AddWithValue(DeliveryPostCodeField, order.DeliveryPostCode);
command.Parameters.AddWithValue(DeliveryStateField, order.DeliveryState);
command.Parameters.AddWithValue(DeliverySurnameField, order.DeliverySurname);
command.Parameters.AddWithValue(ExpiryDateField, order.ExpiryDate);
command.Parameters.AddWithValue(FraudResponseField, order.FraudResponse);
command.Parameters.AddWithValue(GiftAidField, order.GiftAid);
command.Parameters.AddWithValue(Last4DigitsField, order.Last4Digits);
command.Parameters.AddWithValue(LastUpdatedField, order.LastUpdated);
command.Parameters.AddWithValue(PayerIdField, order.PayerId);
command.Parameters.AddWithValue(PayerStatusField, order.PayerStatus);
command.Parameters.AddWithValue(PostCodeResultField, order.PostCodeResult);
command.Parameters.AddWithValue(RelatedVendorTxCodeField, order.RelatedVendorTxCode);
command.Parameters.AddWithValue(SecurityKeyField, order.SecurityKey);
command.Parameters.AddWithValue(StatusField, order.Status);
command.Parameters.AddWithValue(StatusMessageField, order.StatusMessage);
command.Parameters.AddWithValue(SurchargeField, order.Surcharge);
command.Parameters.AddWithValue(ThreeDSecureStatusField, order.ThreeDSecureStatus);
command.Parameters.AddWithValue(TokenIdField, order.TokenId);
command.Parameters.AddWithValue(TransactionTypeField, order.TransactionType);
command.Parameters.AddWithValue(TxAuthNoField, order.TxAuthNo);
command.Parameters.AddWithValue(VpsTxIdField, order.VpsTxId);
}
You have to use # for sql-parameters. Maybe this fixes your issue although i must admit that i don't understand the query because the column-names are the same as the values. However ...
string sql = #"UPDATE Orders SET {0}
Where {1}=#{1};";
sql = string.Format(sql
, string.Join(",", FieldNames.Select(field => string.Format("{0}=#{0}", field)))
, VendorTxCodeField);
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
for (int i = 0; i < FieldNames.Count; i++)
{
cmd.Parameters.AddWithValue(FieldNames[i], FieldNames[i]);
}
// open connection and execute the command...
}

DatabaseIOException When Executing Query "Delete"

Can anybody help telling me what is wrong with my code? I am trying to connect to SQLite database, and executing some queries. when trying to create and open the database, create and insert the table, no exception returned. but when I try to execute delete statement,
DatabaseIOException: File system error (12)
always returned. I don't know the cause of the exception exactly. would you tell me what usually cause this kind of exception? I don't even know when I need to close the database and when I don't need to. this solution also makes me confused.
here is my code:
public class DatabaseManager {
Logger log = new Logger();
Database db;
public DatabaseManager() {
createDatabase();
}
private void createDatabase() {
// Determine if an SDCard is present
boolean sdCardPresent = false;
String root = null;
Enumeration enum = FileSystemRegistry.listRoots();
while (enum.hasMoreElements()) {
root = (String) enum.nextElement();
if(root.equalsIgnoreCase("sdcard/")) {
sdCardPresent = true;
}
}
if(!sdCardPresent) {
alert("This application requires an SD card to be present. Exiting application...");
}
else {
try {
URI uri = URI.create("/SDCard/databases/MyAdvanceUI/myadvanceui.db");
db = DatabaseFactory.openOrCreate(uri);
db.close();
//alert("Database OK!");
} catch (Exception e) {
// TODO Auto-generated catch block
//alert("Exception in createDatabase(): " + e);
}
}
}
private void alert(final String message) {
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
Dialog.inform(message);
System.exit(0);
}
});
}
private void createTableTask() {
try {
URI uri = URI.create("/SDCard/databases/MyAdvanceUI/myadvanceui.db");
db = DatabaseFactory.open(uri);
Statement st = db.createStatement("CREATE TABLE IF NOT EXISTS t_task (id INTEGER PRIMARY KEY AUTOINCREMENT, "
+ "client TEXT, task TEXT)");
st.prepare();
st.execute();
st.close();
db.close();
//alert("Table Task created!");
} catch (Exception e) {
// TODO: handle exception
//alert("Exception in createTableTask(): " + e);
}
}
private void insertTableTask() {
String[] clients = { "Budi Setiawan", "Dian Kusuma", "Joko Ahmad", "Titi Haryanto", "Wahyu" };
String[] tasks = {
"Penawaran terhadap instalasi server",
"Follow up untuk keperluan produk terbaru",
"Pendekatan untuk membina relasi",
"Penawaran jasa maintenance",
"Penawaran terhadap instalasi database"
};
try {
URI uri = URI.create("/SDCard/databases/MyAdvanceUI/myadvanceui.db");
db = DatabaseFactory.open(uri);
for(int i = 0; i < clients.length; i++) {
Statement st = db.createStatement("INSERT INTO t_task (client, task) VALUES (?, ?)");
st.prepare();
st.bind(1, clients[i]);
st.bind(2, tasks[i]);
st.execute();
st.close();
}
db.close();
} catch (Exception e) {
// TODO: handle exception
//alert("Exception in insertTableTask(): " + e);
}
}
public void loadInitialData() {
createTableTask();
insertTableTask();
}
public Cursor getTasks() {
// TODO Auto-generated method stub
Cursor results = null;
try {
URI uri = URI.create("/SDCard/databases/MyAdvanceUI/myadvanceui.db");
db = DatabaseFactory.open(uri);
Statement st = db.createStatement("SELECT client, task FROM t_task");
st.prepare();
results = st.getCursor();
return results;
} catch (Exception e) {
// TODO: handle exception
//alert("Exception: " + e);
}
return results;
}
public void delete(String string) {
// TODO Auto-generated method stub
try {
URI uri = URI.create("/SDCard/databases/MyAdvanceUI/myadvanceui.db");
db = DatabaseFactory.open(uri);
Statement st = db.createStatement("DELETE FROM t_task WHERE client=?");
st.prepare();
st.bind(1, string);
st.execute();
} catch (Exception e) {
// TODO: handle exception
alert("Exception: " + e);
}
}
}
thank you for your help.
I don't see that you close the statement and close the database after select and delete actions. Most probably you can't open database because it wasn't closed correctly.
Big warning SD card isn't available when user mounted devices to PC as external drive. Some devices are going without SD card preinstalled. DB operations are really slow on 5 OS devices. Your alert method code wan't close db what could be issue to open it after on the next application start.
Warning As #pankar mentioned in comment you should add finally {} where you will close resources for sure. In your current implementation if you get exception in execution you will never close database.
Big improvements You don't need to create and prepare statement every loop. Just do it before for. Do bind and execute every loop. And close statement after for.
Improvements You could keep one opened db during application run cycle. It will save you some line of code and time for opening closing.
Notation It's bad practice to have parameter named like 'string'. I would rename it to something more meaningful.

Blackberry HttpConnection failure on device

I'm after some BlackBerry suggestions again. I'm developing a REST based app using the standard BB code that appends to the URI connection string (I'll post if you like but don't want to take up space here as I suspect that those of you that know about this know exactly what I mean).
The code works fine in the emulator in MDS mode and is good on the phone too with straight WiFi.
Now, the problem is when I come to use 3G on an actual phone. At that point it fails. Is this some kind of transcoding problem?
I'm using a raw HttpConnection.
An HTTP POST works (with body info) but the GET (which uses a cookie for auth purposes as a header requestproperty) fails.
The failure is only with header (GET) based info on non WiFi connections on the mobile device.
Any suggestions would be most appreciated.
public static String httpGet(Hashtable params, String uriIn) {
String result = null;
LoginDetails loginDetails = LoginDetails.getInstance();
HttpConnection _connection;
String uri = uriIn + "?api_key=" + loginDetails.getApi_key();
Enumeration e = params.keys();
// iterate through Hashtable keys Enumeration
while (e.hasMoreElements()) {
String key = (String) (e.nextElement());
String value = (String) params.get(key);
uri += "&" + key + "=" + value;
}
uri = uri + HelperMethods.getConnectionString();
try {
_connection = (HttpConnection) Connector.open(uri);
_connection.setRequestMethod(HttpConnection.GET);
_connection.setRequestProperty("Content-Type",
"text/plain; charset=UTF-8");
_connection.setRequestProperty("x-rim-authentication-passthrough",
"true");
_connection.setRequestProperty("Cookie", loginDetails.getCookie());
_connection.setRequestProperty("Content-Type", "application/json");
String charset = "UTF-8";
_connection.setRequestProperty("Accept-Charset", charset);
_connection.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded;charset=" + charset);
OutputStream _outputStream = _connection.openOutputStream();
int rc = _connection.getResponseCode();
InputStream _inputStream = _connection.openInputStream();
ByteArrayOutputStream bytestream = new ByteArrayOutputStream();
int ch;
while ((ch = _inputStream.read()) != -1) {
bytestream.write(ch);
}
result = new String(bytestream.toByteArray());
bytestream.close();
{
if (_outputStream != null)
try {
_outputStream.close();
} catch (Exception e1) {
}
if (_connection != null)
try {
_connection.close();
} catch (Exception e2) {
}
}
} catch (IOException e3) {
// TODO Auto-generated catch block
e3.printStackTrace();
}
return result;
}
And this uses:
public synchronized static String getConnectionString() {
String connectionString = null;
// Simulator behaviour is controlled by the USE_MDS_IN_SIMULATOR
// variable.
if (DeviceInfo.isSimulator()) {
connectionString = ";deviceside=true";
}
// Wifi is the preferred transmission method
else if (WLANInfo.getWLANState() == WLANInfo.WLAN_STATE_CONNECTED) {
connectionString = ";interface=wifi";
}
// Is the carrier network the only way to connect?
else if ((CoverageInfo.getCoverageStatus() & CoverageInfo.COVERAGE_DIRECT) == CoverageInfo.COVERAGE_DIRECT) {
String carrierUid = getCarrierBIBSUid();
if (carrierUid == null) {
// Has carrier coverage, but not BIBS. So use the carrier's TCP
// network
connectionString = ";deviceside=true";
} else {
// otherwise, use the Uid to construct a valid carrier BIBS
// request
connectionString = ";deviceside=false;connectionUID="+carrierUid + ";ConnectionType=mds-public";
}
}
// Check for an MDS connection instead (BlackBerry Enterprise Server)
else if ((CoverageInfo.getCoverageStatus() & CoverageInfo.COVERAGE_MDS) == CoverageInfo.COVERAGE_MDS) {
connectionString = ";deviceside=false";
}
// If there is no connection available abort to avoid hassling the user
// unnecssarily.
else if (CoverageInfo.getCoverageStatus() == CoverageInfo.COVERAGE_NONE) {
connectionString = "none";
}
// In theory, all bases are covered by now so this shouldn't be reachable.But hey, just in case ...
else {
connectionString = ";deviceside=true";
}
return connectionString;
}
/**
* Looks through the phone's service book for a carrier provided BIBS
* network
*
* #return The uid used to connect to that network.
*/
private synchronized static String getCarrierBIBSUid() {
ServiceRecord[] records = ServiceBook.getSB().getRecords();
int currentRecord;
for (currentRecord = 0; currentRecord < records.length; currentRecord++) {
if (records[currentRecord].getCid().toLowerCase().equals("ippp")) {
if (records[currentRecord].getName().toLowerCase()
.indexOf("bibs") >= 0) {
return records[currentRecord].getUid();
}
}
}
return null;
}
Fixed - see above.
It turns out that there were spaces in the uri's.
Quite why this worked over WiFi & not 3G etc. is still puzzling.

Resources