I am doing the push notification tutorial on windows phone. At start there is no problem with pushing the notifications.
However, after a few days I opened back the coding and got this error :
System.NullReferenceException was unhandled by user code
HResult=-2147467261
Message=Object reference not set to an instance of an object.
Source=UtemFtmkDB
StackTrace:
at UtemFtmkDB.MainPage.ButtonSave_Click(Object sender, RoutedEventArgs e)
at System.Windows.Controls.Primitives.ButtonBase.OnClick()
at System.Windows.Controls.Button.OnClick()
at System.Windows.Controls.Primitives.ButtonBase.<OnMouseLeftButtonUp>b__3()
InnerException:
In App.xaml.cs :
private void AcquirePushChannel()
{
CurrentChannel = HttpNotificationChannel.Find("MyPushChannel");
if (CurrentChannel == null)
{
CurrentChannel = new HttpNotificationChannel("MyPushChannel");
CurrentChannel.Open();
CurrentChannel.BindToShellTile();
}
}
Whenever I retrieve data from :App.CurrentChannel.ChannelUri.ToString() I get this error. Why?
It's possible that the channel hasn't reached the "Connected" status yet.
Check:
CurrentChannel.ConnectionStatus == ChannelConnectionStatus.Connected
Once that's true, you should have a non-null value for ChannelUri.
You'll want to hook up to the ChannelUriUpdated event:
private void AcquirePushChannel()
{
CurrentChannel = HttpNotificationChannel.Find("MyPushChannel");
if(null != CurrentChannel)
{
CurrentChannel.ChannelUriUpdated += CurrentChannelOnChannelUriUpdated;
}
else
{
CurrentChannel = new HttpNotificationChannel("MyPushChannel");
CurrentChannel.ChannelUriUpdated += CurrentChannelOnChannelUriUpdated;
CurrentChannel.Open();
CurrentChannel.BindToShellTile();
}
}
private void CurrentChannelOnChannelUriUpdated(object sender, NotificationChannelUriEventArgs args)
{
// you can now get the URI from args.ChannelUri
}
Note that the channel uri might also change for an existing channel. So you should listen for the ChannelUriUpdated event regardless of wheter you are creating a new channel or not.
Related
I need to change the default value of the Residential Delivery checkbox on the Customers AR.30.30.00 screen (Shipping tab) to checked by default. See screenshot:
In 2017R2, this event handler worked without error:
public class CustomerMaint_Extension : PXGraphExtension<CustomerMaint>
{
protected virtual void LocationExtAddress_CResedential_FieldDefaulting(PXCache sender, PXFieldDefaultingEventArgs e)
{
var row = (LocationExtAddress)e.Row;
if (row != null)
{
e.NewValue = true; // checked by default
}
}
}
I'm updating this customization for 2020R2. It appears that LocationExtAddress has been replaced with DefLocationExt in newer versions. (Resedential is mis-spelled intentionally in the code... that's how Acumatica defined it.) I've tried changing the event handler to:
protected virtual void DefLocationExt_CResedential_FieldDefaulting(PXCache sender, PXFieldDefaultingEventArgs e)
{
var row = (DefLocationExt)e.Row;
if (row != null)
{
e.NewValue = true; // checked by default
}
}
But this results in a run-time error:
Failed to subscribe the event PX.Objects.AR.CustomerMaint_Extension::DefLocationExt_CResedential_FieldDefaulting in the graph PX.Objects.AR.CustomerMaint. The method signature looks like an event handler, but the cache DefLocationExt has not been found in the list of auto-initialized caches. Remove unused event handlers from the code.
How can I attach an event to this field in 2020R2?
Try a generic event handler and see if you get the same result.
It might look something like this.
protected virtual void _(Events.FieldDefaulting<PX.Objects.CR.Standalone.Location.cResedential> e)
{
var row = (PX.Objects.CR.Standalone.Location)e.Row;
if (row != null)
{
e.NewValue = true; // checked by default
}
}
I need to run some address validation on Customer Location addresses using a 3rd party API to determine if the address is residential or commercial. This validation should run whenever an address field is changed. In other words, the validation should be run in the Address_RowUpdated event handler.
Because the function is calling a 3rd party API, I believe that it should be done in a separate thread, using PXLongOperation so that it does not hold up address saving and fails gracefully if the API is unavailable or returns an error.
However, I am not sure if the architecture of running a long operation within an event handler is supported or if a different approach would be better.
Here is my code.
public class CustomerLocationMaint_Extension : PXGraphExtension<CustomerLocationMaint>
{
protected virtual void Address_RowUpdated(PXCache sender, PXRowUpdatedEventArgs e)
{
PX.Objects.CR.Address row = (PX.Objects.CR.Address)e.Row;
if (row != null)
{
Location location = this.Base.Location.Current;
PXCache locationCache = Base.LocationCurrent.Cache;
PXLongOperation.StartOperation(Base, delegate
{
RunCheckResidential(location, locationCache);
});
this.Base.LocationCurrent.Cache.IsDirty = true;
}
}
protected void RunCheckResidential(Location location, PXCache locationCache)
{
string messages = "";
PX.Objects.CR.Address defAddress = PXSelect<PX.Objects.CR.Address,
Where<PX.Objects.CR.Address.addressID, Equal<Required<Location.defAddressID>>>>.Select(Base, location.DefAddressID);
FValidator validator = new FValidator();
AddressValidationReply reply = validator.Validate(defAddress);
AddressValidationResult result = reply.AddressResults[0];
bool isResidential = location.CResedential ?? false;
if (result.Classification == FClassificationType.RESIDENTIAL)
{
isResidential = true;
} else if (result.Classification == FClassificationType.BUSINESS)
{
isResidential = false;
} else
{
messages += "Residential classification is: " + result.Classification + "\r\n";
}
location.CResedential = isResidential;
locationCache.Update(location);
Base.LocationCurrent.Update(location);
Base.Actions.PressSave();
// Display relevant messages
if (reply.HighestSeverity == NotificationSeverityType.SUCCESS)
String addressCorrection = validator.AddressCompare(result.EffectiveAddress, defAddress);
if (!string.IsNullOrEmpty(addressCorrection))
messages += addressCorrection;
}
PXSetPropertyException message = new PXSetPropertyException(messages, PXErrorLevel.Warning);
PXLongOperation.SetCustomInfo(new LocationMessageDisplay(message));
//throw new PXOperationCompletedException(messages); // Shows message if you hover over the success checkmark, but you have to hover to see it so not ideal
}
public class LocationMessageDisplay : IPXCustomInfo
{
public void Complete(PXLongRunStatus status, PXGraph graph)
{
if (status == PXLongRunStatus.Completed && graph is CustomerLocationMaint)
{
((CustomerLocationMaint)graph).RowSelected.AddHandler<Location>((sender, e) =>
{
Location location = e.Row as Location;
if (location != null)
{
sender.RaiseExceptionHandling<Location.cResedential>(location, location.CResedential, _message);
}
});
}
}
private PXSetPropertyException _message;
public LocationMessageDisplay(PXSetPropertyException message)
{
_message = message;
}
}
}
UPDATE - New Approach
As suggested, this code now calls the LongOperation within the Persist method.
protected virtual void Address_RowUpdated(PXCache sender, PXRowUpdatedEventArgs e)
{
PX.Objects.CR.Address row = (PX.Objects.CR.Address)e.Row;
if (row != null)
{
Location location = Base.Location.Current;
LocationExt locationExt = PXCache<Location>.GetExtension<LocationExt>(location);
locationExt.UsrResidentialValidated = false;
Base.LocationCurrent.Cache.IsDirty = true;
}
}
public delegate void PersistDelegate();
[PXOverride]
public virtual void Persist(PersistDelegate baseMethod)
{
baseMethod();
var location = Base.Location.Current;
PXCache locationCache = Base.LocationCurrent.Cache;
LocationExt locationExt = PXCache<Location>.GetExtension<LocationExt>(location);
if (locationExt.UsrResidentialValidated == false)
{
PXLongOperation.StartOperation(Base, delegate
{
CheckResidential(location);
});
}
}
public void CheckResidential(Location location)
{
CustomerLocationMaint graph = PXGraph.CreateInstance<CustomerLocationMaint>();
graph.Clear();
graph.Location.Current = location;
LocationExt locationExt = location.GetExtension<LocationExt>();
locationExt.UsrResidentialValidated = true;
try
{
// Residential code using API (this will change the value of the location.CResedential field)
} catch (Exception e)
{
throw new PXOperationCompletedWithErrorException(e.Message);
}
graph.Location.Update(location);
graph.Persist();
}
PXLongOperation is meant to be used in the context of a PXAction callback. This is typically initiated by a menu item or button control, including built-in actions like Save.
It is an anti-pattern to use it anytime a value changes in the web page. It should be used only when a value is persisted (by Save action) or by another PXAction event handler. You should handle long running validation when user clicks on a button or menu item not when he changes the value.
For example, the built in Validate Address feature is run only when the user clicks on the Validate Address button and if validated requests are required it is also run in a Persist event called in the context of the Save action to cancel saving if validation fails.
This is done to ensure user expectation that a simple change in a form/grid value field doesn't incur a long validation wait time that would lead the user to believe the web page is unresponsive. When the user clicks on Save or a specific Action button it is deemed more reasonable to expect a longer wait time.
That being said, it is not recommended but possible to wrap your PXLongOperation call in a dummy Action and asynchronously click on the invisible Action button to get the long operation running in the proper context from any event handler (except Initialize):
using PX.Data;
using System.Collections;
namespace PX.Objects.SO
{
public class SOOrderEntry_Extension : PXGraphExtension<SOOrderEntry>
{
public PXAction<SOOrder> TestLongOperation;
[PXUIField(DisplayName = "Test Long Operation", Visible = false, Visibility = PXUIVisibility.Invisible)]
[PXButton]
public virtual IEnumerable testLongOperation(PXAdapter adapter)
{
PXLongOperation.StartOperation(Base, delegate ()
{
System.Threading.Thread.Sleep(2000);
Base.Document.Ask("Operation Done", MessageButtons.OK);
});
return adapter.Get();
}
public void SOOrder_OrderDesc_FieldUpdated(PXCache sender, PXFieldUpdatedEventArgs e)
{
if (!PXLongOperation.Exists(Base.UID))
{
// Calling Action Button asynchronously so it can run in the context of a PXAction callback
Base.Actions["TestLongOperation"].PressButton();
}
}
}
}
This question is specific to a lately strange behavior of the Azure mobile Apps Android sdk. Everything was working fine for weeks. Now, my android client app suddenly can't connect to my web app any more. A Toast says "Error while processing request". In Android Studio debugger, I found the exception inside the SDK file MobileServiceConnection.java.
java.io.IOException: stream was reset: PROTOCOL_ERROR
In Azure Portal, my app shows "Healthy" status, but I can see the HTTP errors. Please help.
Following is my code, which was working fine and now throws error.
// Create the Mobile Service Client instance, using the provided mobile app URL.
try {
mClient = new MobileServiceClient(mMobileBackendUrl, activityContext).withFilter(
new ServiceFilter() {
#Override
public ListenableFuture<ServiceFilterResponse> handleRequest(ServiceFilterRequest request, NextServiceFilterCallback nextServiceFilter) {
// Get the request contents
String url = request.getUrl();
String content = request.getContent();
if (url != null) {
Log.d("Request URL:", url);
}
if (content != null) {
Log.d("Request Content:", content);
}
// Execute the next service filter in the chain
ListenableFuture<ServiceFilterResponse> responseFuture = nextServiceFilter.onNext(request);
Futures.addCallback(responseFuture, new FutureCallback<ServiceFilterResponse>() {
#Override
public void onFailure(Throwable exception) {
Log.d("Exception:", exception.getMessage());
}
#Override
public void onSuccess(ServiceFilterResponse response) {
if (response != null && response.getContent() != null) {
Log.d("Response Content:", response.getContent());
}
}
});
return responseFuture;
}
}
);
setAzureClient(mClient);
}catch(MalformedURLException e){
createAndShowDialog(new Exception("There was an error creating the Mobile Service. Verify the URL"), "Error");
}catch(Exception e){
createAndShowDialog("There was an error creating the Mobile Service. "+ e.toString(), "Error");
}
Toast.makeText(context, context.getString(R.string.online_authentication), Toast.LENGTH_SHORT).show();
authenticate();
}
private void authenticate() { // give access only to authenticated users via Google account authentication
HashMap<String, String> parameters = new HashMap<>();
parameters.put("access_type", "offline");//use "Refresh tokens"
//login with the Google provider. This will create a call to onActivityResult() method inside the context Activity, which will then call the onActivityResult() below.
mClient.login(MobileServiceAuthenticationProvider.Google, url_scheme_of_your_app, GOOGLE_LOGIN_REQUEST_CODE, parameters);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// When request completes
if (requestCode == 1) {
try {
MobileServiceActivityResult result = mClient.onActivityResult(data);
if (result.isLoggedIn()) {
Toast.makeText(context, context.getString(R.string.azure_auth_login_success) /*+ " " + mClient.getCurrentUser().getUserId()*/, Toast.LENGTH_SHORT).show();
mUserId = mClient.getCurrentUser().getUserId();
} else {//>>>>THIS IS WHERE I AM GETTING THE ERROR
String errorMessage = result.getErrorMessage();
Toast.makeText(context, errorMessage, Toast.LENGTH_SHORT).show();// Error While processing request (it comes form the MobileServiceConnection.java file inside sdk)
}
}catch(Exception e){
Toast.makeText(context, e.toString(), Toast.LENGTH_LONG).show();
}
}
}
I found the answer myself. The error was due to an Azure App Service HTTP2 connection issue. It has nothing to do with the app code. For anyone facing the same problem, here is the solution.
Go to https://resources.azure.com/
Make sure you are in Read/Write mode by clicking in the option to the left of your name.
From the left column, browse to: https://resources.azure.com/subscriptions/yourSubscriptionId/resourceGroups/yourWebAppResourceGroup/providers/Microsoft.Web/sites/yourWebAppName/config/web
Find and Change the property: "http20Enabled": from true to false by clicking EDIT, Update value to “false” and then clicking in Save or PATCH.
I want to be able to detect when a user signs on to my application using passive acs, so that I can add them to my database if this is the first time using my app. Right now I am subscribing to WSFederationAuthenticationModule.SignedIn but I feel I'm missing something. Mainly I'm not sure the best place to subscribe to the event, I got it to work inside PostAuthenticateRequest but its a bit hacky. Any suggestions?
this code is from global.asax
public override void Init()
{
base.Init();
PostAuthenticateRequest += (s, e) =>
{
try
{
FederatedAuthentication.WSFederationAuthenticationModule.SignedIn -= SignedIn;
}
finally
{
FederatedAuthentication.WSFederationAuthenticationModule.SignedIn += SignedIn;
}
};
}
private void SignedIn(object sender, EventArgs e)
{
//do something
}
EDIT:
For now I'm going to use a flag variable to make sure I only subscribe once to SignedIn. Unless someone has any other suggestions that is :) thanks for the help Sandrino. Here is what I have at the moment.
private static bool isFirstRequest = true;
public override void Init()
{
base.Init();
PostAuthenticateRequest += (s, e) => {
if (isFirstRequest)
{
FederatedAuthentication
.WSFederationAuthenticationModule.SignedIn += SignedIn;
isFirstRequest = false;
}
};
}
private void SignedIn(object sender, EventArgs e)
{
//do something
}
EDIT:
A little more info. This problem happens if I'm using the azure emulator, it probably happens when deployed as well but I haven't tried that. I have tested if I am just not able to debug by trying to write to a text file and no text file was created.
Why do you subscribe to the SignedIn event each time the PostAuthenticateRequest event is raised? You can simple subscribe to it when the application starts (in the Global.asax) and it will be raised for each user that signed in:
public class MvcApplication : System.Web.HttpApplication
{
...
protected void Application_Start()
{
...
FederatedAuthentication.ServiceConfigurationCreated += (s, e) =>
{
FederatedAuthentication.WSFederationAuthenticationModule.SignedIn += new EventHandler(OnUserSignedIn);
};
}
private void OnUserSignedIn(object sender, EventArgs e)
{
// Custom logic here.
}
}
The SignedIn event is the best way to detect a user sign in before the application continues. Take a look at the following diagram. Before redirecting back to a page, the SignedIn event is raised to allow you to detect an user sign in:
Reference: http://msdn.microsoft.com/en-us/library/ee517293.aspx
I created a class that derives from ClaimsAuthenticationManager. There is only one method that you have to override, which is
public virtual IClaimsPrincipal Authenticate(string resourceName, IClaimsPrincipal incomingPrincipal);
In my app, I use this method to check if the user, who has successfully authenticated, is really a user of my app (i.e. they exist in my database). If not, I direct them to a signup page.
My class looks something like this:
public override IClaimsPrincipal Authenticate(string resourceName, IClaimsPrincipal incomingPrincipal)
{
if (incomingPrincipal.Identity.IsAuthenticated)
{
var identity = incomingPrincipal.Identity as IClaimsIdentity;
User user = null;
// Get name identifier and identity provider
var nameIdentifierClaim = identity.Claims.SingleOrDefault(c => c.ClaimType.Equals(ClaimTypes.NameIdentifier, StringComparison.OrdinalIgnoreCase));
var identityProviderClaim = identity.Claims.SingleOrDefault(c => c.ClaimType.Equals(CustomClaimTypes.IdentityProviderClaimType, StringComparison.OrdinalIgnoreCase));
if (nameIdentifierClaim == null || identityProviderClaim == null)
{
throw new AuthenticationErrorException("Invalid claims", "The claims provided by your Identity Provider are invalid. Please contact your administrator.");
}
try
{
//checking the database here...
using (var context = new CloudContext())
{
user = (from u in context.Users
where u.IdentityProvider == identityProviderClaim.Value &&
u.NameIdentifier == nameIdentifierClaim.Value &&
!u.Account.PendingDelete
select u).FirstOrDefault();
}
}
catch (System.Data.DataException ex)
{
Console.WriteLine(ex.Message);
if (ex.InnerException != null)
Console.WriteLine(ex.InnerException);
throw;
}
}
return incomingPrincipal;
}
Then, in your web.config, you add a section to the <microsoft.identitymodel> area, as so:
<claimsAuthenticationManager type="CloudAnalyzer.UI.Security.CloudAnalyzerClaimsAuthenticationManager" />
I learned this trick from the sample app located here: Windows Azure Marketplace. Even if you're not going to publish in the Window Azure Marketplace it's a good sample with some helpful code snippets you can use for ACS integration.
I am developping a BlackBerry application which communicates with the server via HTTP requests(javax.microedition.io.HttpConnection). On device, user clicks some UI items, and device sends the requests to server, when the response comes, UI changes. Communication takes place under new thread, while UI thread pushes and pops ProgressDialogScreen.
The problem is sometimes, when response comes and ProgressDialogScreen is popped, UI does not change but after couple seconds UI changes. If you have requested in between when ProgressDialogScreen is popped and when new Screen is pushed, there comes the mess. First oldest new Screen is pushed, and the newest new Screen is pushed. And this situation can be observed like server responsing wrong requests. This problems occur on simulator and device.
The other problem is, sometimes two same response returns for one request. I was able to see these two problems on simulator at the logs, but i have not able to see this issue on device since i can not see the logs.
EDIT:
String utf8Response;
HttpConnection httpConn = null;
try{
httpConn = (HttpConnection) Connector.open(url);
httpConn.setRequestMethod(HttpConnection.GET);
httpConn.setRequestProperty("Content-Type", "text/html; charset=UTF8");
if(sessionIdCookie != null){
//may throw IOException, if the connection is in the connected state.
httpConn.setRequestProperty("Cookie", sessionIdCookie);
}
}catch (Exception e) {
//...
}
try{
httpConn.getResponseCode();
return httpConn;
}catch (IOException e) {
// ...
}
byte[] responseStr = new byte[(int)httpConn.getLength()];
DataInputStream strm = httpConn.openDataInputStream();
strm.readFully(responseStr);
try{
strm.close();
}catch (IOException e) {
// ....
}
utf8Response = new String(responseStr, "UTF-8");
If this code successfully run, this piece of code runs and new screen is pushed:
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
Vector accounts = Parser.parse(utf8Response,Parser.ACCOUNTS);
if (accounts.size() == 0){
DialogBox.inform(Account.NO_DEPOSIT);
return;
}
currentScreen = new AccountListScreen(accounts);
changeScreen(null,currentScreen);
}
});
public void changeScreen(final AbstractScreen currentScreen,final AbstractScreen nextScreen) {
if (currentScreen != null)
UiApplication.getUiApplication().popScreen(currentScreen);
if (nextScreen != null)
UiApplication.getUiApplication().pushScreen(nextScreen);
}
EDITv2:
private static void progress(final Stoppable runThis, String text,boolean cancelable) {
progress = new ProgressBar(runThis, text,cancelable);
Thread threadToRun = new Thread() {
public void run() {
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
try{
UiApplication.getUiApplication().pushScreen(progress);
}catch(Exception e){
Logger.log(e);
}
}
});
try {
runThis.run();
} catch (Throwable t) {
t.printStackTrace();
}
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
try {
UiApplication.getUiApplication().popScreen(progress);
} catch (Exception e) { }
}
});
}
};
threadToRun.start();
}
By the way ProgressBar is extended from net.rim.device.api.ui.container.PopupScreen and Stoppable is extended from Runnable
I preferred to pop progress bar after new Screen is prepared and pushed. This way there will be no new request between request and response.
Why not do:
private static void progress(final Stoppable runThis, String text,boolean cancelable) {
progress = new ProgressBar(runThis, text,cancelable);
UiApplication.getUiApplication().pushScreen(progress);
[...]
Seems like you are parsing on the UI Thread. Please remove Vector accounts = Parser.parse(utf8Response,Parser.ACCOUNTS); from ui thread and do it in a separate thread.