I copy/pasted code from ArcGIS .NET ashx proxy into my Controller method, now debugging is jumping all over the place (multi-threaded, apparently) - asp.net-mvc

I have a controller that takes in JSON data via HTTP POST. Inside this controller is a call to a method that I copied/pasted from ArcGIS's .NET implementation of the proxy that's needed to connect to ArcGIS's servers. For the sake of the problem I'm having, that part was irrelavent.
Before copying/pasting, the execution flow was line by line. But now, after copying and pasting (and subsequently adding the call to the method), my debugging execution flow is jumping all over the place (because of different threads going on at the same time). I don't know why this is happening- I didn't see anything that had to do with threads in the code I copied and pasted. Could you tell me why this is happening just because of code that I copied/pasted that appears to not have anything to do with multithreading?
Here's my controller code that makes the call to the method I copied/pasted:
[HttpPost]
public void PostPicture(HttpRequestMessage msg)
{
HttpContext context = HttpContext.Current;
ProcessRequest(context);
...
Here's the code I copied and pasted from ArcGIS (I'm sorry, it's very long):
public void ProcessRequest(HttpContext context)
{
HttpResponse response = context.Response;
System.Net.HttpWebRequest req = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(context.Request.Url);
req.Method = context.Request.HttpMethod;
req.ServicePoint.Expect100Continue = false;
// Set body of request for POST requests
if (context.Request.InputStream.Length > 0)
{
byte[] bytes = new byte[context.Request.InputStream.Length];
context.Request.InputStream.Read(bytes, 0, (int)context.Request.InputStream.Length);
req.ContentLength = bytes.Length;
string ctype = context.Request.ContentType;
if (String.IsNullOrEmpty(ctype))
{
req.ContentType = "application/x-www-form-urlencoded";
}
else
{
req.ContentType = ctype;
}
using (Stream outputStream = req.GetRequestStream())
{
outputStream.Write(bytes, 0, bytes.Length);
}
}
// Send the request to the server
System.Net.WebResponse serverResponse = null;
try
{
serverResponse = req.GetResponse();
}
catch (System.Net.WebException webExc)
{
response.StatusCode = 500;
response.StatusDescription = webExc.Status.ToString();
response.Write(webExc.Response);
response.End();
return;
}
// Set up the response to the client
if (serverResponse != null)
{
response.ContentType = serverResponse.ContentType;
using (Stream byteStream = serverResponse.GetResponseStream())
{
// Text response
if (serverResponse.ContentType.Contains("text") ||
serverResponse.ContentType.Contains("json"))
{
using (StreamReader sr = new StreamReader(byteStream))
{
string strResponse = sr.ReadToEnd();
response.Write(strResponse);
}
}
else
{
// Binary response (image, lyr file, other binary file)
BinaryReader br = new BinaryReader(byteStream);
byte[] outb = br.ReadBytes((int)serverResponse.ContentLength);
br.Close();
// Tell client not to cache the image since it's dynamic
response.CacheControl = "no-cache";
// Send the image to the client
// (Note: if large images/files sent, could modify this to send in chunks)
response.OutputStream.Write(outb, 0, outb.Length);
}
serverResponse.Close();
}
}
response.End();
}
public bool IsReusable
{
get
{
return false;
}
}
// Gets the token for a server URL from a configuration file
// TODO: ?modify so can generate a new short-lived token from username/password in the config file
private string getTokenFromConfigFile(string uri)
{
try
{
ProxyConfig config = ProxyConfig.GetCurrentConfig();
if (config != null)
return config.GetToken(uri);
else
throw new ApplicationException(
"Proxy.config file does not exist at application root, or is not readable.");
}
catch (InvalidOperationException)
{
// Proxy is being used for an unsupported service (proxy.config has mustMatch="true")
HttpResponse response = HttpContext.Current.Response;
response.StatusCode = (int)System.Net.HttpStatusCode.Forbidden;
response.End();
}
catch (Exception e)
{
if (e is ApplicationException)
throw e;
// just return an empty string at this point
// -- may want to throw an exception, or add to a log file
}
return string.Empty;
}
}
[XmlRoot("ProxyConfig")]
public class ProxyConfig
{
#region Static Members
private static object _lockobject = new object();
public static ProxyConfig LoadProxyConfig(string fileName)
{
ProxyConfig config = null;
lock (_lockobject)
{
if (System.IO.File.Exists(fileName))
{
XmlSerializer reader = new XmlSerializer(typeof(ProxyConfig));
using (System.IO.StreamReader file = new System.IO.StreamReader(fileName))
{
config = (ProxyConfig)reader.Deserialize(file);
}
}
}
return config;
}
public static ProxyConfig GetCurrentConfig()
{
ProxyConfig config = HttpRuntime.Cache["proxyConfig"] as ProxyConfig;
if (config == null)
{
string fileName = GetFilename(HttpContext.Current);
config = LoadProxyConfig(fileName);
if (config != null)
{
CacheDependency dep = new CacheDependency(fileName);
HttpRuntime.Cache.Insert("proxyConfig", config, dep);
}
}
return config;
}
public static string GetFilename(HttpContext context)
{
return context.Server.MapPath("~/proxy.config");
}
#endregion
ServerUrl[] serverUrls;
bool mustMatch;
[XmlArray("serverUrls")]
[XmlArrayItem("serverUrl")]
public ServerUrl[] ServerUrls
{
get { return this.serverUrls; }
set { this.serverUrls = value; }
}
[XmlAttribute("mustMatch")]
public bool MustMatch
{
get { return mustMatch; }
set { mustMatch = value; }
}
public string GetToken(string uri)
{
foreach (ServerUrl su in serverUrls)
{
if (su.MatchAll && uri.StartsWith(su.Url, StringComparison.InvariantCultureIgnoreCase))
{
return su.Token;
}
else
{
if (String.Compare(uri, su.Url, StringComparison.InvariantCultureIgnoreCase) == 0)
return su.Token;
}
}
if (mustMatch)
throw new InvalidOperationException();
return string.Empty;
}
}
public class ServerUrl
{
string url;
bool matchAll;
string token;
[XmlAttribute("url")]
public string Url
{
get { return url; }
set { url = value; }
}
[XmlAttribute("matchAll")]
public bool MatchAll
{
get { return matchAll; }
set { matchAll = value; }
}
[XmlAttribute("token")]
public string Token
{
get { return token; }
set { token = value; }
}
}

Related

Swagger 2 Feign client code oAuth flow throwing error url values must be not be absolute

I exposed Rest APIs, and I generated client code using Swagger 2 Java language with Feign library. The code gen generated the below OAuth RequestInterceptor. I am getting the below error when I use the oAuth as auth.
Error
feign.RetryableException: url values must be not be absolute.
at com.sam.feign.auth.OAuth.updateAccessToken(OAuth.java:95)
at com.sam.feign.auth.OAuth.apply(OAuth.java:83)
at feign.SynchronousMethodHandler.targetRequest(SynchronousMethodHandler.java:161)
at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:110)
at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:89)
at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:100)
at com.sun.proxy.$Proxy9.getUser(Unknown Source)
at com.sam.feign.clients.UserApiTest.getUserTest(UserApiTest.java:38)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:538)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:760)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206)
Caused by: java.lang.IllegalArgumentException: url values must be not be absolute.
at feign.RequestTemplate.uri(RequestTemplate.java:434)
at feign.RequestTemplate.uri(RequestTemplate.java:421)
at feign.RequestTemplate.append(RequestTemplate.java:388)
at com.sam.feign.auth.OAuth$OAuthFeignClient.execute(OAuth.java:163)
at org.apache.oltu.oauth2.client.OAuthClient.accessToken(OAuthClient.java:65)
at org.apache.oltu.oauth2.client.OAuthClient.accessToken(OAuthClient.java:55)
at org.apache.oltu.oauth2.client.OAuthClient.accessToken(OAuthClient.java:71)
at com.sam.feign.auth.OAuth.updateAccessToken(OAuth.java:93)
... 34 more
Swagger Generated oAuth supporting file
public class OAuth implements RequestInterceptor {
static final int MILLIS_PER_SECOND = 1000;
public interface AccessTokenListener {
void notify(BasicOAuthToken token);
}
private volatile String accessToken;
private Long expirationTimeMillis;
private OAuthClient oauthClient;
private TokenRequestBuilder tokenRequestBuilder;
private AuthenticationRequestBuilder authenticationRequestBuilder;
private AccessTokenListener accessTokenListener;
public OAuth(Client client, TokenRequestBuilder requestBuilder) {
this.oauthClient = new OAuthClient(new OAuthFeignClient(client));
this.tokenRequestBuilder = requestBuilder;
}
public OAuth(Client client, OAuthFlow flow, String authorizationUrl, String tokenUrl, String scopes) {
this(client, OAuthClientRequest.tokenLocation(tokenUrl).setScope(scopes));
switch(flow) {
case accessCode:
case implicit:
tokenRequestBuilder.setGrantType(GrantType.AUTHORIZATION_CODE);
break;
case password:
tokenRequestBuilder.setGrantType(GrantType.PASSWORD);
break;
case application:
tokenRequestBuilder.setGrantType(GrantType.CLIENT_CREDENTIALS);
break;
default:
break;
}
authenticationRequestBuilder = OAuthClientRequest.authorizationLocation(authorizationUrl);
}
public OAuth(OAuthFlow flow, String authorizationUrl, String tokenUrl, String scopes) {
this(new Client.Default(null, null), flow, authorizationUrl, tokenUrl, scopes);
}
#Override
public void apply(RequestTemplate template) {
// If the request already have an authorization (eg. Basic auth), do nothing
if (template.headers().containsKey("Authorization")) {
return;
}
// If first time, get the token
if (expirationTimeMillis == null || System.currentTimeMillis() >= expirationTimeMillis) {
updateAccessToken(template);
}
if (getAccessToken() != null) {
template.header("Authorization", "Bearer " + getAccessToken());
}
}
public synchronized void updateAccessToken(RequestTemplate template) {
OAuthJSONAccessTokenResponse accessTokenResponse;
try {
accessTokenResponse = oauthClient.accessToken(tokenRequestBuilder.buildBodyMessage());
} catch (Exception e) {
throw new RetryableException(400, e.getMessage(), template.request().httpMethod(), e, null, template.request());
}
if (accessTokenResponse != null && accessTokenResponse.getAccessToken() != null) {
setAccessToken(accessTokenResponse.getAccessToken(), accessTokenResponse.getExpiresIn());
if (accessTokenListener != null) {
accessTokenListener.notify((BasicOAuthToken) accessTokenResponse.getOAuthToken());
}
}
}
public synchronized void registerAccessTokenListener(AccessTokenListener accessTokenListener) {
this.accessTokenListener = accessTokenListener;
}
public synchronized String getAccessToken() {
return accessToken;
}
public synchronized void setAccessToken(String accessToken, Long expiresIn) {
this.accessToken = accessToken;
this.expirationTimeMillis = System.currentTimeMillis() + expiresIn * MILLIS_PER_SECOND;
}
public TokenRequestBuilder getTokenRequestBuilder() {
return tokenRequestBuilder;
}
public void setTokenRequestBuilder(TokenRequestBuilder tokenRequestBuilder) {
this.tokenRequestBuilder = tokenRequestBuilder;
}
public AuthenticationRequestBuilder getAuthenticationRequestBuilder() {
return authenticationRequestBuilder;
}
public void setAuthenticationRequestBuilder(AuthenticationRequestBuilder authenticationRequestBuilder) {
this.authenticationRequestBuilder = authenticationRequestBuilder;
}
public OAuthClient getOauthClient() {
return oauthClient;
}
public void setOauthClient(OAuthClient oauthClient) {
this.oauthClient = oauthClient;
}
public void setOauthClient(Client client) {
this.oauthClient = new OAuthClient( new OAuthFeignClient(client));
}
public static class OAuthFeignClient implements HttpClient {
private Client client;
public OAuthFeignClient() {
this.client = new Client.Default(null, null);
}
public OAuthFeignClient(Client client) {
this.client = client;
}
public <T extends OAuthClientResponse> T execute(OAuthClientRequest request, Map<String, String> headers,
String requestMethod, Class<T> responseClass)
throws OAuthSystemException, OAuthProblemException {
RequestTemplate req = new RequestTemplate()
.append(request.getLocationUri())
.method(requestMethod)
.body(request.getBody());
for (Entry<String, String> entry : headers.entrySet()) {
req.header(entry.getKey(), entry.getValue());
}
Response feignResponse;
String body = "";
try {
feignResponse = client.execute(req.request(), new Options());
body = Util.toString(feignResponse.body().asReader());
} catch (IOException e) {
throw new OAuthSystemException(e);
}
String contentType = null;
Collection<String> contentTypeHeader = feignResponse.headers().get("Content-Type");
if(contentTypeHeader != null) {
contentType = StringUtil.join(contentTypeHeader.toArray(new String[0]), ";");
}
return OAuthClientResponseFactory.createCustomResponse(
body,
contentType,
feignResponse.status(),
responseClass
);
}
public void shutdown() {
// Nothing to do here
}
}
}
ApiClient.java have the below absolute URL which configured in swagger spec
public ApiClient() {
objectMapper = createObjectMapper();
apiAuthorizations = new LinkedHashMap<String, RequestInterceptor>();
feignBuilder = Feign.builder()
.encoder(new FormEncoder(new JacksonEncoder(objectMapper)))
.decoder(new JacksonDecoder(objectMapper))
.logger(new Slf4jLogger());
}
public ApiClient(String[] authNames) {
this();
for(String authName : authNames) {
RequestInterceptor auth = null;
if ("client-credentils-oauth2".equals(authName)) {
auth = new OAuth(OAuthFlow.application, "", "http://localhost:8080/app/oauth/token", "user.create");
} else if ("password-oauth2".equals(authName)) {
auth = new OAuth(OAuthFlow.password, "", "http://localhost:8080/app/oauth/token", "openid");
} else {
throw new RuntimeException("auth name \"" + authName + "\" not found in available auth names");
}
addAuthorization(authName, auth);
}
}
Used the below dependencies
swagger-codegen-maven-plugin v2.4.28
feign-version 11.6
feign-form-version 3.8.0
oltu-version 1.0.1
Java 8
I am invoking the client by using below code
UserApi api = new ApiClient("client-credentils-oauth2","admin", "admin", null, null).buildClient(UserApi.class);
api.getUser(login, tenant)
I made the few changes in the generated oAuth.java file to make it work. Expecting the client generated code should work without making any manual changes.
public <T extends OAuthClientResponse> T execute(OAuthClientRequest request, Map<String, String> headers,
String requestMethod, Class<T> responseClass)
throws OAuthSystemException, OAuthProblemException {
// Added the below 3 lines
URI targetUri = URI.create(uri);
String target = targetUri.getScheme() + "://" + targetUri.getAuthority() ;
String path = targetUri.getPath();
RequestTemplate req = new RequestTemplate()
.uri(path)
.method(requestMethod)
.body(request.getBody())
.target(target); // Added this line
for (Entry<String, String> entry : headers.entrySet()) {
req.header(entry.getKey(), entry.getValue());
}
req = req.resolve(new HashMap<String, Object>()); // Added this line
Response feignResponse;
String body = "";
try {
feignResponse = client.execute(req.request(), new Options());
body = Util.toString(feignResponse.body().asReader());
} catch (IOException e) {
throw new OAuthSystemException(e);
}
String contentType = null;
Collection<String> contentTypeHeader = feignResponse.headers().get("Content-Type");
if(contentTypeHeader != null) {
contentType = StringUtil.join(contentTypeHeader.toArray(new String[0]), ";");
}
return OAuthClientResponseFactory.createCustomResponse(
body,
contentType,
feignResponse.status(),
responseClass
);
}
Appreciate if someone can assist me with this issue

NSUrlSession photo upload task to AzureFunction inside BGTaskScheduler does not get executed when iOS charger cable is unplugged on iOS 14.4

We are working on a Xamarin Forms app that is supposed to upload photos to API in the background. The app is being custom-made for a client by their request, so they will set their phones to whatever permissions need to be set.
Below works fine if the charging cable is plugged in.
I am using BGTaskScheduler (iOS13+) and queuing both types of tasks (BGProcessingTaskRequest and BGAppRefreshTaskRequest) so that if the cable plugged in it would fire off BGProcessingTaskRequest and if not it would wait for BGAppRefreshTaskRequest to get its processing time.
I have added RefreshTaskId and UploadTaskId to Info.plist
AppDelegate.cs in iOS project looks following
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
LoadApplication(new App());
BGTaskScheduler.Shared.Register(UploadTaskId, null, task => HandleUpload(task as BGProcessingTask));
BGTaskScheduler.Shared.Register(RefreshTaskId, null, task => HandleAppRefresh(task as BGAppRefreshTask));
return base.FinishedLaunching(app, options);
}
public override void HandleEventsForBackgroundUrl(UIApplication application, string sessionIdentifier, Action completionHandler)
{
Console.WriteLine("HandleEventsForBackgroundUrl");
BackgroundSessionCompletionHandler = completionHandler;
}
public override void OnActivated(UIApplication application)
{
Console.WriteLine("OnActivated");
}
public override void OnResignActivation(UIApplication application)
{
Console.WriteLine("OnResignActivation");
}
private void HandleAppRefresh(BGAppRefreshTask task)
{
HandleUpload(task);
}
public override void DidEnterBackground(UIApplication application)
{
ScheduleUpload();
}
private void HandleUpload(BGTask task)
{
var uploadService = new UploadService();
uploadService.EnqueueUpload();
task.SetTaskCompleted(true);
}
private void ScheduleUpload()
{
var upload = new BGProcessingTaskRequest(UploadTaskId)
{
RequiresNetworkConnectivity = true,
RequiresExternalPower = false
};
BGTaskScheduler.Shared.Submit(upload, out NSError error);
var refresh = new BGAppRefreshTaskRequest(RefreshTaskId);
BGTaskScheduler.Shared.Submit(refresh, out NSError refreshError);
if (error != null)
Console.WriteLine($"Could not schedule BGProcessingTask: {error}");
if (refreshError != null)
Console.WriteLine($"Could not schedule BGAppRefreshTask: {refreshError}");
}
The mechanism that does the upload UploadService is using NSUrlSession, it also writes a temporary file to use CreateUploadTask(request, NSUrl.FromFilename(tempFileName)) that is supposed to work in the background, whole mechanism looks following:
public NSUrlSession uploadSession;
public async void EnqueueUpload()
{
var accountsTask = await App.PCA.GetAccountsAsync();
var authResult = await App.PCA.AcquireTokenSilent(App.Scopes, accountsTask.First())
.ExecuteAsync();
if (uploadSession == null)
uploadSession = InitBackgroundSession(authResult.AccessToken);
var datastore = DependencyService.Get<IDataStore<Upload>>();
var uploads = await datastore.GetUnuploaded();
foreach (var unUploaded in uploads)
{
try
{
string folder = unUploaded.Description;
string subfolder = unUploaded.Category;
if (string.IsNullOrEmpty(folder) || string.IsNullOrEmpty(subfolder))
continue;
var uploadDto = new Dtos.Upload
{
FolderName = folder,
SubFolderName = subfolder,
Image = GetImageAsBase64(unUploaded.ImagePath)
};
var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var fileName = Path.GetFileName(unUploaded.ImagePath);
var tempFileName = Path.Combine(documents, $"{fileName}.txt");
string stringContent = await new StringContent(JsonConvert.SerializeObject(uploadDto), Encoding.UTF8, "application/json").ReadAsStringAsync();
await File.WriteAllTextAsync(tempFileName, stringContent);
using (var url = NSUrl.FromString(UploadUrlString))
using (var request = new NSMutableUrlRequest(url)
{
HttpMethod = "POST",
})
{
request.Headers.SetValueForKey(NSObject.FromObject("application/json"), new NSString("Content-type"));
try
{
uploadSession.CreateUploadTask(request, NSUrl.FromFilename(tempFileName));
}
catch (Exception e)
{
Console.WriteLine($"NSMutableUrlRequest failed {e.Message}");
}
}
}
catch (Exception e)
{
if (e.Message.Contains("Could not find a part of the path"))
{
await datastore.DeleteItemAsync(unUploaded.Id);
Console.WriteLine($"deleted");
}
Console.WriteLine($"uploadStore failed {e.Message}");
}
}
}
private string GetImageAsBase64(string path)
{
using (var reader = new StreamReader(path))
using (MemoryStream ms = new MemoryStream())
{
reader.BaseStream.CopyTo(ms);
return Convert.ToBase64String(ms.ToArray());
}
}
public NSUrlSession InitBackgroundSession(string authToken = null, IDataStore<Upload> dataStore = null)
{
Console.WriteLine("InitBackgroundSession");
using (var configuration = NSUrlSessionConfiguration.CreateBackgroundSessionConfiguration(Identifier))
{
configuration.AllowsCellularAccess = true;
configuration.Discretionary = false;
configuration.AllowsConstrainedNetworkAccess = true;
configuration.AllowsExpensiveNetworkAccess = true;
if (string.IsNullOrWhiteSpace(authToken) == false)
{
configuration.HttpAdditionalHeaders = NSDictionary.FromObjectsAndKeys(new string[] { $"Bearer {authToken}" }, new string[] { "Authorization" });
}
return NSUrlSession.FromConfiguration(configuration, new UploadDelegate(dataStore), null);
}
}
}
public class UploadDelegate : NSUrlSessionTaskDelegate, INSUrlSessionDelegate
{
public IDataStore<Upload> Datastore { get; }
public UploadDelegate(IDataStore<Upload> datastore)
{
this.Datastore = datastore;
}
public override void DidCompleteWithError(NSUrlSession session, NSUrlSessionTask task, NSError error)
{
Console.WriteLine(string.Format("DidCompleteWithError TaskId: {0}{1}", task.TaskIdentifier, (error == null ? "" : " Error: " + error.Description)));
if (error == null)
{
ProcessCompletedTask(task);
}
}
public void ProcessCompletedTask(NSUrlSessionTask sessionTask)
{
try
{
Console.WriteLine(string.Format("Task ID: {0}, State: {1}, Response: {2}", sessionTask.TaskIdentifier, sessionTask.State, sessionTask.Response));
if (sessionTask.Response == null || sessionTask.Response.ToString() == "")
{
Console.WriteLine("ProcessCompletedTask no response...");
}
else
{
var resp = (NSHttpUrlResponse)sessionTask.Response;
Console.WriteLine("ProcessCompletedTask got response...");
if (sessionTask.State == NSUrlSessionTaskState.Completed && resp.StatusCode == 201)
{
Console.WriteLine("201");
}
}
}
catch (Exception ex)
{
Console.WriteLine("ProcessCompletedTask Ex: {0}", ex.Message);
}
}
public override void DidBecomeInvalid(NSUrlSession session, NSError error)
{
Console.WriteLine("DidBecomeInvalid" + (error == null ? "undefined" : error.Description));
}
public override void DidFinishEventsForBackgroundSession(NSUrlSession session)
{
Console.WriteLine("DidFinishEventsForBackgroundSession");
}
public override void DidSendBodyData(NSUrlSession session, NSUrlSessionTask task, long bytesSent, long totalBytesSent, long totalBytesExpectedToSend)
{
}
}
Everything works if the iOS charger cable is plugged in, however, if it isn't nothing fires. I have a network debugging set up with plenty of logging into the console, and I can see that nothing happens on iPhone.
"Low power mode" setting on iOS is off.
I have watched Background execution demystified and I am setting session configuration.Discretionary = false;
How do I make the NSUrlSession upload task to fire when iOS charger cable is unplugged on iOS 14.4?
Following works without charging cable:
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
public Action BackgroundSessionCompletionHandler { get; set; }
public static string UploadTaskId { get; } = "XXX.upload";
public static NSString UploadSuccessNotificationName { get; } = new NSString($"{UploadTaskId}.success");
public static string RefreshTaskId { get; } = "XXX.refresh";
public static NSString RefreshSuccessNotificationName { get; } = new NSString($"{RefreshTaskId}.success");
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
LoadApplication(new App());
BGTaskScheduler.Shared.Register(UploadTaskId, null, task => HandleUpload(task as BGProcessingTask));
BGTaskScheduler.Shared.Register(RefreshTaskId, null, task => HandleAppRefresh(task as BGAppRefreshTask));
return base.FinishedLaunching(app, options);
}
public override bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options)
{
AuthenticationContinuationHelper.SetAuthenticationContinuationEventArgs(url);
return true;
}
public override void HandleEventsForBackgroundUrl(UIApplication application, string sessionIdentifier, Action completionHandler)
{
Console.WriteLine("HandleEventsForBackgroundUrl");
BackgroundSessionCompletionHandler = completionHandler;
}
public override void OnActivated(UIApplication application)
{
Console.WriteLine("OnActivated");
var uploadService = new UploadService();
uploadService.EnqueueUpload();
}
public override void OnResignActivation(UIApplication application)
{
Console.WriteLine("OnResignActivation");
}
private void HandleAppRefresh(BGAppRefreshTask task)
{
task.ExpirationHandler = () =>
{
Console.WriteLine("BGAppRefreshTask ExpirationHandler");
var refresh = new BGAppRefreshTaskRequest(RefreshTaskId);
BGTaskScheduler.Shared.Submit(refresh, out NSError refreshError);
if (refreshError != null)
Console.WriteLine($"BGAppRefreshTask ExpirationHandler Could not schedule BGAppRefreshTask: {refreshError}");
};
HandleUpload(task);
}
public override void DidEnterBackground(UIApplication application) => ScheduleUpload();
private void HandleUpload(BGTask task)
{
Console.WriteLine("HandleUpload");
var uploadService = new UploadService();
uploadService.EnqueueUpload();
task.SetTaskCompleted(true);
}
private void ScheduleUpload()
{
Console.WriteLine("ScheduleUpload");
var upload = new BGProcessingTaskRequest(UploadTaskId)
{
RequiresNetworkConnectivity = true,
RequiresExternalPower = false
};
BGTaskScheduler.Shared.Submit(upload, out NSError error);
var refresh = new BGAppRefreshTaskRequest(RefreshTaskId);
BGTaskScheduler.Shared.Submit(refresh, out NSError refreshError);
if (error != null)
Console.WriteLine($"Could not schedule BGProcessingTask: {error}");
if (refreshError != null)
Console.WriteLine($"Could not schedule BGAppRefreshTask: {refreshError}");
}
}
then Upload service:
public class UploadService : IUploadService
{
private const string uploadUrlString = "https://Yadyyadyyada";
public async void EnqueueUpload()
{
var accountsTask = await App.PCA.GetAccountsAsync();
var authResult = await App.PCA.AcquireTokenSilent(App.Scopes, accountsTask.First())
.ExecuteAsync();
try
{
var uploadDto = new object();
var message = new HttpRequestMessage(HttpMethod.Post, uploadUrlString);
message.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", authResult.AccessToken);
message.Content = new StringContent(JsonConvert.SerializeObject(uploadDto), Encoding.UTF8, "application/json");
var response = await httpClient.SendAsync(message);
if (response.IsSuccessStatusCode)
{
var json = await response.Content.ReadAsStringAsync();
}
}
catch (Exception e)
{
Console.WriteLine($"EnqueueUpload {e.Message}");
}
}
}

Vaadin upload with PipedInputStream & PipedOutputStream example

I just started learning Vaadin 8 and my first example is Upload button. I was stuck with an issue where I could not solve the problem for many hours and hours.
Here it is,
I am returning PipedOutputStream in the receiveUpload method,
Here is the code for receiveUpload method,
public OutputStream receiveUpload(String filename, String mimeType) {
this.fileName = filename;
this.mimeType = mimeType;
try {
pipedOutputStream = new PipedOutputStream();
pipedInputStream = new PipedInputStream(pipedOutputStream);
if (filename == null || filename.trim().length() == 0) {
upload.interruptUpload();
} else {
}
} catch (Exception e) {
e.printStackTrace();
}
return pipedOutputStream;
}
In the uploadSucceeded method, I need to take the pipedinputstream and send it another method to load the stream into the database
public void uploadSucceeded(SucceededEvent event) {
try {
fileUploadOperation.upload(pipedInputStream); --> I need to push all the stream data in one go into a method to generate a file at the business layer
} catch (Exception e) {
e.printStackTrace();
}
}
When I was running the application, it hangs out for a long time and I could not figure out where it is. Later I could notice that both piped input and piped output streams should be created in separate threads or at least one of them in a separate thread but don't know how to handle it.
Any help
I am pasting the complete class for more information,
public class WebCommunityView implements Receiver, FailedListener, SucceededListener, StartedListener, FinishedListener {
private PipedOutputStream pipedOutputStream = null;
private PipedInputStream pipedInputStream = null;
private Upload upload = null;
private String fileName = null, mimeType = null;
private Grid<FileListProperties> fileListGrid = null;
public final static WebCommunityView newInstance(WebContentScreen screen) {
vw.initBody();
return vw;
}
protected void initBody() {
VerticalLayout verticalLayout = new VerticalLayout();
fileListGrid = new Grid<FileListProperties>();
fileListGrid.addColumn(FileListProperties::getCreatedDate).setCaption("Date");
fileListGrid.addColumn(FileListProperties::getFileName).setCaption("File Name");
fileListGrid.addColumn(FileListProperties::getUserName).setCaption("User Name");
fileListGrid.addComponentColumn(this::buildDownloadButton).setCaption("Download");
fileListGrid.setItems(loadGridWithFileInfo());
upload = new Upload("", this);
upload.setImmediateMode(false);
upload.addFailedListener((Upload.FailedListener) this);
upload.addSucceededListener((Upload.SucceededListener) this);
upload.addStartedListener((Upload.StartedListener) this);
upload.addFinishedListener((Upload.FinishedListener) this);
Label fileUploadLabel = new Label("Label"));
verticalLayout.addComponent(currentListLabel);
verticalLayout.addComponent(fileListGrid);
verticalLayout.addComponent(fileUploadLabel);
verticalLayout.addComponent(upload);
mainbody.addComponent(verticalLayout);
}
#Override
public void uploadSucceeded(SucceededEvent event) {
try {
//Model Layer
fileUploadOperation.upload(pipedInputStream);
fileUploadOperation.commit();
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void uploadFailed(FailedEvent event) {
if (event.getFilename() == null) {
Notification.show("Upload failed", Notification.Type.HUMANIZED_MESSAGE);
}
try {
//Model Layer
fileUploadOperation.abort();
} catch (Exception e) {
e.printStackTrace();
}
}
public OutputStream receiveUpload(String filename, String mimeType) {
this.fileName = filename;
this.mimeType = mimeType;
try {
pipedOutputStream = new PipedOutputStream();
new Thread() {
public void run() {
try {
System.out.println("pipedInputStream Thread started");
pipedInputStream = new PipedInputStream(pipedOutputStream);
} catch (Exception e) {
e.printStackTrace();
}
};
}.start();
if (filename == null || filename.trim().length() == 0) {
screen.displayMessage("Please select a file to upload !", WebContentScreen.MESSAGE_TYPE_WARNING);
upload.interruptUpload();
} else {
Properties properties = new Properties();
properties.setProperty("NAME", fileName);
properties.setProperty("MIME_TYPE", mimeType);
//Model Layer
fileUploadOperation.initialize(properties);
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("pipedOutputStream:"+pipedOutputStream);
return pipedOutputStream;
}
private List<FileListProperties> loadGridWithFileInfo() {
List<FileListProperties> list = null;
DateFormat dateFormat = new SimpleDateFormat("dd-MMM-yyyy");
try {
list = new ArrayList<FileListProperties>(1);
Collection<FileInfo> fileInfoList = fileCommandQuery.lstFilesForDownload();
for (Iterator iterator = fileInfoList.iterator(); iterator.hasNext();) {
FileInfo fileInfo = (FileInfo) iterator.next();
Properties properties = fileInfo.getProperties();
Collection<String> mandatoryParameters = fileInfo.getMandatoryProperties();
FileListProperties fileListProperties = new FileListProperties();
for (Iterator iterator2 = mandatoryParameters.iterator(); iterator2.hasNext();) {
String key = (String) iterator2.next();
String value = properties.getProperty(key);
if (key != null && key.equalsIgnoreCase("NAME")) {
fileListProperties.setFileName(value);
} else if (key != null && key.equalsIgnoreCase("USER_NAME")) {
fileListProperties.setUserName(value);
} else if (key != null && key.equalsIgnoreCase("CREATED_DATE")) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(1550566760000L);
fileListProperties.setCreatedDate(dateFormat.format(calendar.getTime()));
}
}
if (fileListProperties != null) {
list.add(fileListProperties);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
dateFormat = null;
}
return list;
}
private Button buildDownloadButton(FileListProperties fileListProperties) {
Button button = new Button("...");
button.addClickListener(e -> downloadFile(fileListProperties));
return button;
}
private void downloadFile(FileListProperties fileListProperties) {
}
}
The problem in your example is that you need to do the actual file handling in a separate thread. The Viritin add-on contains a component called UploadFileHandler that simplifies this kind of usage a lot. It will provide you InputStream to consume. The integration test for the component contains this kind of usage example.
Also, my recent blog entry about the subject might help.

JSF2.0 multi-language composite component

To internationalize a composite component, you have to put a .properties file that has the exact same name than the component itself and in the same folder.
From the xhtml, you can access these translations through ${cc.resourceBundleMap.key}.
Until now, all is fine and works for me. Where the problems starts is when I add more .properties files for other languages. No matter which local is my computer in, the picked language is the default one (component.properties).
This seems to be a recurrent problem since Ziletka also reports the same problem in How to localize JSF 2 composite components, but remained unanswered.
I have tried all sort of possibilities:
no default .properties file
component_fr.properties
component_fr_CA.properties
component_fr_FR.properties
component_en.properties
component_en_CA.properties
component_en_US.properties
but it results in a:
javax.el.ELException: [...] /resources/component/component.xhtml default="${cc.resourceBundleMap.key}": java.lang.NullPointerException
with default .properties file plus Language specification
component.properties
component_fr.properties
component_en.properties
only the default is loaded.
with default .properties file plus Language and Country specifications
component.properties
component_fr_CA.properties
component_fr_FR.properties
component_en_CA.properties
component_en_US.properties
And again: only the default is loaded.
I would love to avoid having to rely on the backing bean to provide the translations and can't resolve into believing that it is not supported. Can anyone help?
This feature was implemented long time ago in MyFaces Core. See: MYFACES-3308. The test case done can be found here
The locale applied to the composite component depends on the value retrieved from UIViewRoot.getLocale().
Apparently the problem is still there and its root is in the javax.faces.component.UIComponent class particularly in the findComponentResourceBundleLocaleMatch method. The snipped is below
private Resource findComponentResourceBundleLocaleMatch(FacesContext context,
String resourceName, String libraryName) {
Resource result = null;
ResourceBundle resourceBundle = null;
int i;
if (-1 != (i = resourceName.lastIndexOf("."))) {
resourceName = resourceName.substring(0, i) +
".properties"; //THE PROBLEM IS HERE
if (null != context) {
result = context.getApplication().getResourceHandler().
createResource(resourceName, libraryName);
InputStream propertiesInputStream = null;
try {
propertiesInputStream = result.getInputStream();
resourceBundle = new PropertyResourceBundle(propertiesInputStream);
} catch (IOException ex) {
Logger.getLogger(UIComponent.class.getName()).log(Level.SEVERE, null, ex);
} finally{
if(null != propertiesInputStream){
try{
propertiesInputStream.close();
} catch(IOException ioe){
if (LOGGER.isLoggable(Level.SEVERE)) {
LOGGER.log(Level.SEVERE, null, ioe);
}
}
}
}
}
}
result = (null != resourceBundle) ? result : null;
return result;
}
You can see it at the line with a comment that states 'THE PROBLEM IS HERE'. Precisely when it looks for a properties file to load it does not respect any language and/or country code. It always loads a default resource.
Possible solution
The 'problematic' method is called from another method getResourceBundleMap of the same class and a part of the code you are interested in is marked with a comment (line #1000)
// Step 2: if this is a composite component, look for a
// ResourceBundle as a Resource
Which is no surprise as you need a composite component. So the solution would be to define a backing component class for your composite component and redefine resourceBundleMap loading. Below you may find the implementation that respects language only which means it would work for files like componentName_en.properties and componentName_de.properties but would not for something like componentName_en_US.properties
Your .properties files should be in the same directory as the definition of your component
testComponent.properties
testComponent_de.properties
testComponent_en.properties
testComponent_fr.properties
in your component testComponent.xhtmlspecify a definition class in componentType attribute.
<cc:interface componentType="test.component">
....
</cc:interface>
The component may look as following. I used the original code mostly with couple changes. The idea is to override the problematic method and withing the code try reading a properties file for a specified language first and if it is not found, read the default one.
#FacesComponent("test.component")
public class TestComponent extends UINamingContainer {
private static final String PROPERTIES_EXT = ".properties";
private Logger LOGGER = <use one you like>;
private Map<String, String> resourceBundleMap = null;
#Override
public Map<String, String> getResourceBundleMap() {
ResourceBundle resourceBundle = null;
if (null == resourceBundleMap) {
FacesContext context = FacesContext.getCurrentInstance();
UIViewRoot root = context.getViewRoot();
Locale currentLocale = null;
if (null != context) {
if (null != root) {
currentLocale = root.getLocale();
}
}
if (null == currentLocale) {
currentLocale = Locale.getDefault();
}
if (this.getAttributes().containsKey(Resource.COMPONENT_RESOURCE_KEY)) {
Resource ccResource = (Resource)
this.getAttributes().get(Resource.COMPONENT_RESOURCE_KEY);
if (null != ccResource) {
InputStream propertiesInputStream = null;
try {
propertiesInputStream = ccResource.getInputStream();
resourceBundle = findComponentResourceBundleLocaleMatch(ccResource.getResourceName(),
ccResource.getLibraryName(), currentLocale.getLanguage());
} catch (IOException ex) {
LOGGER.error(null, ex);
} finally {
if (null != propertiesInputStream) {
try {
propertiesInputStream.close();
} catch (IOException ioe) {
LOGGER.error(null, ioe);
}
}
}
}
}
if (null != resourceBundle) {
final ResourceBundle bundle = resourceBundle;
resourceBundleMap =
new Map() {
// this is an immutable Map
public String toString() {
StringBuffer sb = new StringBuffer();
Iterator<Map.Entry<String, Object>> entries =
this.entrySet().iterator();
Map.Entry<String, Object> cur;
while (entries.hasNext()) {
cur = entries.next();
sb.append(cur.getKey()).append(": ").append(cur.getValue()).append('\n');
}
return sb.toString();
}
// Do not need to implement for immutable Map
public void clear() {
throw new UnsupportedOperationException();
}
public boolean containsKey(Object key) {
boolean result = false;
if (null != key) {
result = (null != bundle.getObject(key.toString()));
}
return result;
}
public boolean containsValue(Object value) {
Enumeration<String> keys = bundle.getKeys();
boolean result = false;
while (keys.hasMoreElements()) {
Object curObj = bundle.getObject(keys.nextElement());
if ((curObj == value) ||
((null != curObj) && curObj.equals(value))) {
result = true;
break;
}
}
return result;
}
public Set<Map.Entry<String, Object>> entrySet() {
HashMap<String, Object> mappings = new HashMap<String, Object>();
Enumeration<String> keys = bundle.getKeys();
while (keys.hasMoreElements()) {
String key = keys.nextElement();
Object value = bundle.getObject(key);
mappings.put(key, value);
}
return mappings.entrySet();
}
#Override
public boolean equals(Object obj) {
return !((obj == null) || !(obj instanceof Map))
&& entrySet().equals(((Map) obj).entrySet());
}
public Object get(Object key) {
if (null == key) {
return null;
}
try {
return bundle.getObject(key.toString());
} catch (MissingResourceException e) {
return "???" + key + "???";
}
}
public int hashCode() {
return bundle.hashCode();
}
public boolean isEmpty() {
Enumeration<String> keys = bundle.getKeys();
return !keys.hasMoreElements();
}
public Set keySet() {
Set<String> keySet = new HashSet<String>();
Enumeration<String> keys = bundle.getKeys();
while (keys.hasMoreElements()) {
keySet.add(keys.nextElement());
}
return keySet;
}
// Do not need to implement for immutable Map
public Object put(Object k, Object v) {
throw new UnsupportedOperationException();
}
// Do not need to implement for immutable Map
public void putAll(Map t) {
throw new UnsupportedOperationException();
}
// Do not need to implement for immutable Map
public Object remove(Object k) {
throw new UnsupportedOperationException();
}
public int size() {
int result = 0;
Enumeration<String> keys = bundle.getKeys();
while (keys.hasMoreElements()) {
keys.nextElement();
result++;
}
return result;
}
public java.util.Collection values() {
ArrayList<Object> result = new ArrayList<Object>();
Enumeration<String> keys = bundle.getKeys();
while (keys.hasMoreElements()) {
result.add(
bundle.getObject(keys.nextElement()));
}
return result;
}
};
}
if (null == resourceBundleMap) {
resourceBundleMap = Collections.EMPTY_MAP;
}
}
return resourceBundleMap;
}
private ResourceBundle findComponentResourceBundleLocaleMatch(String resourceName, String libraryName, String lng) {
FacesContext context = FacesContext.getCurrentInstance();
ResourceBundle resourceBundle = null;
int i;
if (-1 != (i = resourceName.lastIndexOf("."))) {
if (null != context) {
InputStream propertiesInputStream = null;
try {
propertiesInputStream = getResourceInputStream(context, resourceName.substring(0, i), libraryName, lng);
resourceBundle = new PropertyResourceBundle(propertiesInputStream);
} catch (IOException ex) {
LOGGER.error(null, ex);
} finally {
if (null != propertiesInputStream) {
try {
propertiesInputStream.close();
} catch (IOException ioe) {
LOGGER.error(null, ioe);
}
}
}
}
}
return resourceBundle;
}
private InputStream getResourceInputStream(FacesContext context, final String resourceName, String libraryName, String lng) throws IOException {
InputStream propertiesInputStream = null;
propertiesInputStream = getPropertiesResourceInputStream(context, String.format("%s_%s%s", resourceName, lng, PROPERTIES_EXT), libraryName);
if (null == propertiesInputStream) {
propertiesInputStream = getPropertiesResourceInputStream(context, resourceName + PROPERTIES_EXT, libraryName);
}
return propertiesInputStream;
}
private InputStream getPropertiesResourceInputStream(FacesContext context, final String resourceName, String libraryName) throws IOException {
Resource result = context.getApplication().getResourceHandler().createResource(resourceName, libraryName);
if (null == result) {
return null;
}
return result.getInputStream();
}
}
Done.
However that is obviously a bug in the Mojarra and I hope it will be fixed soon. Closer look to the code related to the composite components reveals that the default .properties file for a component is read twice which I guess is not a very good idea too, but this is another story.
PS. You may easily adjust te code to respect country code as well if you wish.

Import LESS from server

In my ASP.NET MVC application I have an action that returns LESS variables.
I would like to import these variables into my main LESS file.
What is the recommended approach for doing this since DotLess will only import files with .less or .css extensions?
I found the easiest solution was to implement IFileReader.
The implementation below makes a HTTP request to any LESS path prefixed with "~/assets", otherwise we use the default FileReader.
Note that this is prototype code:
public class HttpFileReader : IFileReader
{
private readonly FileReader inner;
public HttpFileReader(FileReader inner)
{
this.inner = inner;
}
public bool DoesFileExist(string fileName)
{
if (!fileName.StartsWith("~/assets"))
return inner.DoesFileExist(fileName);
using (var client = new CustomWebClient())
{
client.HeadOnly = true;
try
{
client.DownloadString(ConvertToAbsoluteUrl(fileName));
return true;
}
catch
{
return false;
}
}
}
public byte[] GetBinaryFileContents(string fileName)
{
throw new NotImplementedException();
}
public string GetFileContents(string fileName)
{
if (!fileName.StartsWith("~/assets"))
return inner.GetFileContents(fileName);
using (var client = new CustomWebClient())
{
try
{
var content = client.DownloadString(ConvertToAbsoluteUrl(fileName));
return content;
}
catch
{
return null;
}
}
}
private static string ConvertToAbsoluteUrl(string virtualPath)
{
return new Uri(HttpContext.Current.Request.Url,
VirtualPathUtility.ToAbsolute(virtualPath)).AbsoluteUri;
}
private class CustomWebClient : WebClient
{
public bool HeadOnly { get; set; }
protected override WebRequest GetWebRequest(Uri address)
{
var request = base.GetWebRequest(address);
if (HeadOnly && request.Method == "GET")
request.Method = "HEAD";
return request;
}
}
}
To register the reader, execute the following when your application starts:
var configuration = new WebConfigConfigurationLoader().GetConfiguration();
configuration.LessSource = typeof(HttpFileReader);

Resources