User registration works within the signup activity, however the login activity refuses to move to next activity - no matter what I try.
Logcat wih beginning of crash
--------- beginning of crash
2020-10-05 13:44:28.902 23498-23555/com.example.doadventuresv30 V/FA: Connection attempt already in progress
2020-10-05 13:44:29.123 23696-23724/com.example.doadventuresv30 V/FA: onActivityCreated
2020-10-05 13:44:29.140 23696-23743/com.example.doadventuresv30 V/FA: App measurement collection enabled
2020-10-05 13:44:29.141 23696-23743/com.example.doadventuresv30 V/FA: App measurement enabled for app package, google app id: com.example.doadventuresv30, 1:715052012741:android:8d25e7726c2397e69b4cac
2020-10-05 13:44:29.142 23696-23743/com.example.doadventuresv30 I/FA: App measurement initialized, version: 31049
2020-10-05 13:44:29.142 23696-23743/com.example.doadventuresv30 I/FA: To enable debug logging run: adb shell setprop log.tag.FA VERBOSE
2020-10-05 13:44:29.142 23696-23743/com.example.doadventuresv30 I/FA: To enable faster debug mode event logging run:
adb shell setprop debug.firebase.analytics.app com.example.doadventuresv30
2020-10-05 13:44:29.142 23696-23743/com.example.doadventuresv30 D/FA: Debug-level message logging enabled
2020-10-05 13:44:29.177 23696-23743/com.example.doadventuresv30 V/FA: Connecting to remote service
2020-10-05 13:44:29.190 23696-23743/com.example.doadventuresv30 V/FA: Connection attempt already in progress
2020-10-05 13:44:29.199 23696-23743/com.example.doadventuresv30 V/FA: Connection attempt already in progress
2020-10-05 13:44:29.200 23696-23743/com.example.doadventuresv30 V/FA: Activity resumed, time: 1176833846
2020-10-05 13:44:29.214 23696-23743/com.example.doadventuresv30 I/FA: Tag Manager is not found and thus will not be used
2020-10-05 13:44:29.234 23696-23743/com.example.doadventuresv30 V/FA: Connection attempt already in progress
2020-10-05 13:44:29.235 23696-23743/com.example.doadventuresv30 V/FA: Connection attempt already in progress
2020-10-05 13:44:29.237 23696-23743/com.example.doadventuresv30 V/FA: Screen exposed for less than 1000 ms. Event not sent. time: 12
2020-10-05 13:44:29.237 23696-23743/com.example.doadventuresv30 V/FA: Connection attempt already in progress
2020-10-05 13:44:29.237 23696-23743/com.example.doadventuresv30 V/FA: Activity paused, time: 1176833860
2020-10-05 13:44:29.260 23696-23724/com.example.doadventuresv30 V/FA: onActivityCreated
2020-10-05 13:44:29.294 23696-23743/com.example.doadventuresv30 V/FA: Activity resumed, time: 1176833952
2020-10-05 13:44:29.325 23696-23743/com.example.doadventuresv30 V/FA: Connection attempt already in progress
2020-10-05 13:44:29.325 23696-23743/com.example.doadventuresv30 V/FA: Connection attempt already in progress
2020-10-05 13:44:29.337 23696-23743/com.example.doadventuresv30 D/FA: Connected to remote service
2020-10-05 13:44:29.338 23696-23743/com.example.doadventuresv30 V/FA: Processing queued up service tasks: 8
2020-10-05 13:44:29.359 31341-22371/? V/FA-SVC: Logging event: origin=auto,name=user_engagement(_e),params=Bundle[{ga_event_origin(_o)=auto, engagement_time_msec(_et)=13025, ga_screen_class(_sc)=LoginActivity, ga_screen_id(_si)=-7054614402516150973}]
2020-10-05 13:44:29.365 31341-22371/? V/FA-SVC: Upload scheduled in approximately ms: 3361930
2020-10-05 13:44:29.365 31341-22371/? V/FA-SVC: Unscheduling upload
2020-10-05 13:44:29.367 31341-22371/? V/FA-SVC: Scheduling upload, millis: 3361930
2020-10-05 13:44:29.373 31341-22371/? V/FA-SVC: Background event processing time, ms: 14
2020-10-05 13:44:29.376 31341-22371/? V/FA-SVC: Logging event: origin=auto,name=screen_view(_vs),params=Bundle[{ga_event_origin(_o)=auto, ga_previous_class(_pc)=LoginActivity, ga_previous_id(_pi)=-7054614402516150973, ga_screen_class(_sc)=FeatureDashboardNewsDashboard1Activity, ga_screen_id(_si)=-7054614402516150972}]
2020-10-05 13:44:29.380 31341-22371/? V/FA-SVC: Upload scheduled in approximately ms: 3361915
2020-10-05 13:44:29.380 31341-22371/? V/FA-SVC: Unscheduling upload
2020-10-05 13:44:29.381 31341-22371/? V/FA-SVC: Scheduling upload, millis: 3361915
2020-10-05 13:44:29.384 31341-22371/? V/FA-SVC: Background event processing time, ms: 8
2020-10-05 13:44:29.387 31341-22371/? V/FA-SVC: Logging event: origin=auto,name=screen_view(_vs),params=Bundle[{ga_event_origin(_o)=auto, ga_screen_class(_sc)=SplashActivity, ga_screen_id(_si)=-7054614402516150975}]
2020-10-05 13:44:29.391 31341-22371/? V/FA-SVC: Upload scheduled in approximately ms: 3361904
2020-10-05 13:44:29.391 31341-22371/? V/FA-SVC: Unscheduling upload
2020-10-05 13:44:29.393 31341-22371/? V/FA-SVC: Scheduling upload, millis: 3361904
2020-10-05 13:44:29.395 31341-22371/? V/FA-SVC: Background event processing time, ms: 8
2020-10-05 13:44:29.397 31341-22371/? V/FA-SVC: Logging event: origin=auto,name=screen_view(_vs),params=Bundle[{ga_event_origin(_o)=auto, ga_previous_class(_pc)=SplashActivity, ga_previous_id(_pi)=-7054614402516150975, ga_screen_class(_sc)=SplashActivityLayout, ga_screen_id(_si)=-187945021663330027}]
2020-10-05 13:44:29.402 31341-22371/? V/FA-SVC: Upload scheduled in approximately ms: 3361893
2020-10-05 13:44:29.402 31341-22371/? V/FA-SVC: Unscheduling upload
2020-10-05 13:44:29.404 31341-22371/? V/FA-SVC: Scheduling upload, millis: 3361893
2020-10-05 13:44:29.406 31341-22371/? V/FA-SVC: Background event processing time, ms: 9
2020-10-05 13:44:29.411 31341-22371/? V/FA-SVC: Saving default event parameters, appId, data size: com.example.doadventuresv30, 2
2020-10-05 13:44:34.424 23696-23743/com.example.doadventuresv30 V/FA: Inactivity, disconnecting from the service
2020-10-05 13:44:40.323 1269-11445/? D/Telecom-CreateConnectionProcessor: CreateConnectionProcessor created for Call = [TC#87, NEW, null, xxxxxxxxxxxxxxx, A, childs(0), has_parent(false), [Capabilities:], [Properties:]]
2020-10-05 13:44:40.328 1269-11445/? D/Telecom-CreateConnectionProcessor: attemptNextPhoneAccount
2020-10-05 13:44:40.328 1269-11445/? I/Telecom-CreateConnectionProcessor: Trying attempt CallAttemptRecord(ComponentInfo{com.android.phone/com.android.services.telephony.TelephonyConnectionService}, ***, UserHandle{0},ComponentInfo{com.android.phone/com.android.services.telephony.TelephonyConnectionService}, ***, UserHandle{0})
2020-10-05 13:44:40.329 1269-11445/? D/Telecom-ConnectionServiceWrapper: cmcc encryption call is not enabled, return.
2020-10-05 13:44:40.330 1269-11445/? D/Telecom-ConnectionServiceWrapper: createConnection([TC#87, NEW, com.android.phone/com.android.services.telephony.TelephonyConnectionService, xxxxxxxxxxxxxxx, A, childs(0), has_parent(false), [Capabilities:], [Properties:]]) via ComponentInfo{com.android.phone/com.android.services.telephony.TelephonyConnectionService}.
2020-10-05 13:44:40.352 1269-1269/? D/Telecom-ConnectionServiceWrapper: Telecom -> ConnectionService[com.android.phone/com.android.services.telephony.TelephonyConnectionService]: addConnectionServiceAdapter com.android.server.telecom.ConnectionServiceWrapper$Adapter#df1b213
2020-10-05 13:44:40.354 1269-11445/? D/Telecom-ConnectionServiceWrapper: ConnectionService -> Telecom[com.android.phone/com.android.services.telephony.TelephonyConnectionService]: queryRemoteConnectionServices callingPackage=com.android.phone
2020-10-05 13:44:40.360 1269-11445/? D/Telecom-ConnectionServiceWrapper: queryRemoteConnectionServices: none; not sim call mgr.
2020-10-05 13:44:40.361 2686-2686/? I/Telephony-TelephonyConnectionService: onCreateIncomingConnection
public class LoginActivity extends AppCompatActivity implements View.OnClickListener {
TextView createTextView;
LinearLayout facebookLinearLayout, twitterLinearLayout, googlePlusLinearLayout;
Button loginButton, forgotButton;
ImageView bgImageView;
EditText emEmail, emPassword;
FirebaseAuth mAuth;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
initUI();
initDataBindings();
initActions();
emEmail = findViewById(R.id.emEmail);
emPassword = findViewById(R.id.emPassword);
findViewById(R.id.createTextView).setOnClickListener(this);
findViewById(R.id.loginButton).setOnClickListener(this);
mAuth = FirebaseAuth.getInstance();
}
//region Init Functions
private void initUI() {
forgotButton = findViewById(R.id.forgotButton);
createTextView = findViewById(R.id.createTextView);
facebookLinearLayout = findViewById(R.id.facebookLinearLayout);
twitterLinearLayout = findViewById(R.id.twitterLinearLayout);
googlePlusLinearLayout = findViewById(R.id.googlePlusLinearLayout);
loginButton = findViewById(R.id.loginButton);
bgImageView = findViewById(R.id.bgImageView);
}
private void initDataBindings() {
int id = R.drawable.login_background;
Utils.setImageToImageView(getApplicationContext(), bgImageView, id);
}
private void initActions() {
forgotButton.setOnClickListener(view -> {
Toast.makeText(getApplicationContext(), "Clicked Forgot Password", Toast.LENGTH_SHORT).show();
});
createTextView.setOnClickListener(view -> {
Toast.makeText(getApplicationContext(), "Clicked Create Account", Toast.LENGTH_SHORT).show();
});
facebookLinearLayout.setOnClickListener(view -> {
Toast.makeText(getApplicationContext(), "Clicked Facebook authentication - Not available yet", Toast.LENGTH_SHORT).show();
});
twitterLinearLayout.setOnClickListener(view -> {
Toast.makeText(getApplicationContext(), "Clicked Twitter authentication - Not available yet", Toast.LENGTH_SHORT).show();
});
googlePlusLinearLayout.setOnClickListener(view -> {
Toast.makeText(getApplicationContext(), "Clicked Google Plus authentication - Not available yet", Toast.LENGTH_SHORT).show();
});
loginButton.setOnClickListener(view -> {
Toast.makeText(getApplicationContext(), "Clicked Login", Toast.LENGTH_SHORT).show();
});
}
private void userlogin() {
String email = emEmail.getText().toString().trim();
String password = emPassword.getText().toString().trim();
if (email.isEmpty()) {
emEmail.setError("Email Required");
emEmail.requestFocus();
return;
}
if (!Patterns.EMAIL_ADDRESS.matcher(email).matches()) {
emEmail.setError("Please enter a valid email");
emEmail.requestFocus();
return;
}
if (password.isEmpty()) {
emPassword.setError("Password Required");
emPassword.requestFocus();
return;
}
mAuth.signInWithEmailAndPassword(email, password).addOnCompleteListener(new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
Log.d("Response", String.valueOf(task)); //view response
if (task.isSuccessful()) {
Intent intent = new Intent(LoginActivity.this, FeatureDashboardNewsDashboard1Activity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
} else {
Toast.makeText(getApplicationContext(), task.getException().getMessage(), Toast.LENGTH_SHORT).show();
}
}
});
}
#Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.createTextView:
startActivity(new Intent(this, SignUpActivity.class));
break;
case R.id.loginButton:
userlogin();
break;
}
}
}
I'm trying to navigate to the next activity
public class FeatureDashboardNewsDashboard1Activity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_feature_dashboard_news_dashboard1);
initData();
initUI();
initDataBinding();
initActions();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_search,menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
}else {
Toast.makeText(this, "Clicked "+ item.getTitle() , Toast.LENGTH_SHORT).show();
}
return super.onOptionsItemSelected(item);
}
private void initData() {
}
private void initUI() {
// Init Toolbar
initToolbar();
BottomNavigationView bottomNavigationView = findViewById(R.id.bottomNavigationView);
bottomNavigationView.setOnNavigationItemSelectedListener(item -> {
if (item.getItemId() == R.id.homeMenu) {
loadFragment(new FeatureDashboardNewsDashboard1Fragment());
}
return true;
});
loadFragment(new FeatureDashboardNewsDashboard1Fragment());
}
private void initDataBinding() {
}
private void initActions() {
}
private void initToolbar() {
Toolbar toolbar = findViewById(R.id.toolbar);
toolbar.setNavigationIcon(R.drawable.baseline_menu_black_24);
if (toolbar.getNavigationIcon() != null) {
toolbar.getNavigationIcon().setColorFilter(getResources().getColor(R.color.md_white_1000), PorterDuff.Mode.SRC_ATOP);
}
toolbar.setTitle("News Dashboard 1");
try {
toolbar.setTitleTextColor(getResources().getColor(R.color.md_white_1000));
} catch (Exception e) {
Log.e("TEAMPS", "Can't set color.");
}
try {
setSupportActionBar(toolbar);
} catch (Exception e) {
Log.e("TEAMPS", "Error in set support action bar.");
}
try {
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
} catch (Exception e) {
Log.e("TEAMPS", "Error in set display home as up enabled.");
}
}
private void loadFragment(FeatureDashboardNewsDashboard1Fragment fragment) {
this.getSupportFragmentManager().beginTransaction()
.replace(R.id.content_frame, fragment)
.commitAllowingStateLoss();
}
}
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity
android:name=".fragment.FeatureDashboardNewsDashboard1Fragment"
tools:ignore="Instantiatable" />
<activity
android:name=".Adapter.FeatureDashboardNewsDashboard1CoverFlowPagerAdapter"
tools:ignore="Instantiatable" />
<activity android:name=".FeatureDashboardNewsDashboard1Activity" />
<activity android:name=".LoginActivity" />
<activity android:name=".SignUpActivity" />
<activity android:name=".SplashActivityLayout" />
<activity android:name=".SplashActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />`
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
Log before using the if condition and see the response first i.e
Log.d("Response",String.valueOf(task)); //view response
if(task.isSuccessful()) {
Intent intent = new Intent(LoginActivity.this, FeatureDashboardNewsDashboard1Activity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
}else{
Toast.makeText(getApplicationContext(), task.getException().getMessage(),Toast.LENGTH_SHORT).show();
}
Related
In my launch options, I'm launching a specific activity in this case it's called "DailyActivity.java".
In that class, I have an OnClicklistener for a button that should open up a camera intent to take a picture and then perform text extraction on it.
fab_camera.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.DISPLAY_NAME, "myimage.jpg");
values.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES);
imageUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
values);
Intent camintent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
camintent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(camintent, 101); }});
And here is the onActivityResult that should run after startActivityForResult executes
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Uri image2 = data.getData();
InputImage image = null;
try {
image = InputImage.fromFilePath(this, image2);
TextRecognizer recognizer = TextRecognition.getClient();
When I click my button, my app crashes. I'm currently in the process of debugging and was wondering if it could be because I've launched from a specific activity initially? Would this affect future testing if I want the camera button to later redirect me to another activity(3rd one from the text extraction)?
This is the error I see in logcat when I click the button
2020-12-04 23:19:54.986 25037-25064/com.example.smartcalendar E/eglCodecCommon: GoldfishAddressSpaceHostMemoryAllocator: ioctl_ping failed for device_type=5, ret=-1
2020-12-04 23:19:59.633 25037-25037/com.example.smartcalendar E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.smartcalendar, PID: 25037
java.lang.SecurityException: Permission Denial: writing com.android.providers.media.MediaProvider uri content://media/external/images/media from pid=25037, uid=10087 requires android.permission.WRITE_EXTERNAL_STORAGE, or grantUriPermission()
at android.os.Parcel.createException(Parcel.java:1950)
at android.os.Parcel.readException(Parcel.java:1918)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:183)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:135)
at android.content.ContentProviderProxy.insert(ContentProviderNative.java:476)
at android.content.ContentResolver.insert(ContentResolver.java:1587)
at com.example.smartcalendar.DailyActivity$2.onClick(DailyActivity.java:128)
at android.view.View.performClick(View.java:6597)
at android.view.View.performClickInternal(View.java:6574)
at android.view.View.access$3100(View.java:778)
at android.view.View$PerformClick.run(View.java:25885)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Here are permissions from my Android Manifest file.
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
On android 6 or above you have to check for permission in the runtime before opening the camera intent. Here's how you do it:
fab_camera.setOnClickListener{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(Manifest.permission.CAMERA)
== PackageManager.PERMISSION_DENIED ||
checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_DENIED
) {
// Permission was not granted
// Ask for runtime permission
val permissions = arrayOf(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE)
requestPermissions(
permissions,
PERMISSION_CODE
)
} else {
// Permission already granted
openCamera()
}
} else {
// System OS < Marshmallow
openCamera()
}
}
I have created a simple event processor host which reads events from Azure IOT hub configured with the default options for lease management. But at regular intervals, I get following exception even though I am running single instance of the EventProcessorHost:
ReceiverDisconnectedException: New receiver with higher epoch is created hence current receiver with epoch is getting disconnected. Reason: LeaseLost
The client re-acquires the lease and process messages further.
Here is the initialization of the EventProcessorHost
EventProcessorHost eventProcessorHost = new EventProcessorHost(hostName, eventHubName, EventHubConsumerGroup.DefaultGroupName,
eventHubConnectionString, storageAccountConnectionString);
var options = new EventProcessorOptions();
options.ExceptionReceived += (sender, e) =>
{
Console.WriteLine(e.Exception);
};
eventProcessorHost.PartitionManagerOptions = PartitionManagerOptions.DefaultOptions;
eventProcessorHost.RegisterEventProcessorAsync<SimpleEventProcessor>(options).Wait();
Is this behaviour normal or I need to do something to avoid it for a single instance?
Below is my EventProcessor:
public class SimpleEventProcessor : IEventProcessor
{
Stopwatch checkpointStopWatch;
public async Task CloseAsync(PartitionContext context, CloseReason reason)
{
Console.WriteLine("Processor Shutting Down. Partition '{0}', Reason: '{1}'.", context.Lease.PartitionId, reason);
await context.CheckpointAsync();
}
public Task OpenAsync(PartitionContext context)
{
Console.WriteLine("SimpleEventProcessor initialized. Partition: '{0}', Offset: '{1}'", context.Lease.PartitionId, context.Lease.Offset);
this.checkpointStopWatch = new Stopwatch();
this.checkpointStopWatch.Start();
this.rawDataService = new RawDataService();
return Task.FromResult<object>(null);
}
public async Task ProcessEventsAsync(PartitionContext context, IEnumerable<EventData> messages)
{
foreach (EventData eventData in messages)
{
string data = Encoding.UTF8.GetString(eventData.GetBytes());
Console.WriteLine(string.Format("Message received. Partition: '{0}', Data: '{1}'", context.Lease.PartitionId, data));
}
}
}
I have tried implementing my own QueueProcessorFactory, and it works fine, besides one thing I can’t wrap my head around. After I have tried a message for 5 times (default) it runs CopyMessageToPoisonQueueAsync and then DeleteMessageAsync.
So far so good, but 10 minutes later the message appears in the queue again with a dequeue count 5, and it is in the poison queue too, and then the same procedure, CopyMessageToPoisonQueueAsync, DeleteMessageAsync, an extra item in posionqueue exactly the same as the one already copied and 10 min later the same procedure but with a dequeue count 6. Should I change the ExpirationTime and set it to now when deleting, or do I miss something else?
This is my code:
class Program
{
static void Main()
{
var config = new JobHostConfiguration();
config.Queues.MaxPollingInterval = TimeSpan.FromSeconds(3);
config.Queues.QueueProcessorFactory = new CustomQueueProcessorFactory();
var host = new JobHost(config);
host.RunAndBlock();
}
}
public class CustomQueueProcessorFactory : IQueueProcessorFactory
{
public List<CustomQueueProcessor> CustomQueueProcessors = new List<CustomQueueProcessor>();
public QueueProcessor Create(QueueProcessorFactoryContext context)
{
CustomQueueProcessor processor = new CustomQueueProcessor(context);
CustomQueueProcessors.Add(processor);
return processor;
}
public class CustomQueueProcessor : QueueProcessor
{
public CustomQueueProcessor(QueueProcessorFactoryContext context)
: base(context)
{
}
public override Task<bool> BeginProcessingMessageAsync(CloudQueueMessage message, CancellationToken cancellationToken)
{
return base.BeginProcessingMessageAsync(message, cancellationToken);
}
public override Task CompleteProcessingMessageAsync(CloudQueueMessage message, FunctionResult result, CancellationToken cancellationToken)
{
return base.CompleteProcessingMessageAsync(message, result, cancellationToken);
}
protected override Task CopyMessageToPoisonQueueAsync(CloudQueueMessage message, CloudQueue poisonQueue, CancellationToken cancellationToken)
{
return base.CopyMessageToPoisonQueueAsync(message, poisonQueue, cancellationToken);
}
protected override Task DeleteMessageAsync(CloudQueueMessage message, CancellationToken cancellationToken)
{
return base.DeleteMessageAsync(message, cancellationToken);
}
protected override async Task ReleaseMessageAsync(CloudQueueMessage message, FunctionResult result, TimeSpan visibilityTimeout, CancellationToken cancellationToken)
{
visibilityTimeout = TimeSpan.FromSeconds(2);
await base.ReleaseMessageAsync(message, result, visibilityTimeout, cancellationToken);
}
}
}
if I add some ConsoleWritelines i get this output:
Start omitted because of repetition
.
.
.
BeginProcessingMessageAsync message: d3c88182-ff39-4f81-8c29-b4ce0b2062ad dequeue count: 5 Date: 2017-06-26 13:33:42
Executing 'Functions.ProcessQueueMessage' (Reason='New queue message detected on '01testqueue'.', Id=17405a55-6d28-48b2-a874-718c0b741f61)
Test QueueProcessorFactory
Exception while executing function: Functions.ProcessQueueMessage
Microsoft.Azure.WebJobs.Host.FunctionInvocationException: Exception while executing function: Functions.ProcessQueueMessage ---> System.Exception: Derp!
at WebJobTest1.Functions.ProcessQueueMessage(String message, TextWriter log)
...message omitted....
CompleteProcessingMessageAsync message: d3c88182-ff39-4f81-8c29-b4ce0b2062ad dequeue count: 5
CopyMessageToPoisonQueueAsync message: d3c88182-ff39-4f81-8c29-b4ce0b2062ad dequeue count: 5
Message has reached MaxDequeueCount of 5. Moving message to queue '01testqueue-poison'.
CopyMessageToPoisonQueueAsync message: da643007-954a-4296-9e9a-54ebb0aec6c5 dequeue count: 5
Ten minute waiting time:
BeginProcessingMessageAsync message: d3c88182-ff39-4f81-8c29-b4ce0b2062ad dequeue count: 6 Date: 2017-06-26 13:43:46
Executing 'Functions.ProcessQueueMessage' (Reason='New queue message detected on '01testqueue'.', Id=c22fe457-cb70-4cc8-a8a4-550cd44a8345)
Test QueueProcessorFactory
Exception while executing function: Functions.ProcessQueueMessage
CompleteProcessingMessageAsync message: d3c88182-ff39-4f81-8c29-b4ce0b2062ad dequeue count: 6
CopyMessageToPoisonQueueAsync message: d3c88182-ff39-4f81-8c29-b4ce0b2062ad dequeue count: 6
Message has reached MaxDequeueCount of 5. Moving message to queue '01testqueue-poison'.
CopyMessageToPoisonQueueAsync message: c43fd9fb-c2d7-4745-91ae-33cdc407ede6 dequeue count: 6
According to your description, I assumed that it dues to the known issue when using Storage SDK 8.x with WebJobs SDK. Here are the similar issues:
Poison messages stay undeleted-but-invisible with the latest WindowsAzure.Storage 8.1.1
WebJobs V1.1.2 fails to remove poison messages from queue with V8.0.1 of Storage
Per my test, this issue has not been fixed for now. You could downgrade the Storage SDK or change CopyMessageToPoisonQueueAsync as follows:
protected override Task CopyMessageToPoisonQueueAsync(CloudQueueMessage message, CloudQueue poisonQueue, CancellationToken cancellationToken)
{
var newMessage = new CloudQueueMessage(message.Id, message.PopReceipt);
newMessage.SetMessageContent(message.AsBytes);
return base.CopyMessageToPoisonQueueAsync(newMessage, poisonQueue, cancellationToken);
}
I am working with an Azure Service Bus Queue (or potentially a topic if required), and would like to know how a Web Job can be used with the Queue.
When a message comes onto the queue it represents a process that will run within the web job (or be started from the webjob). This process might be quick, 30 seconds, or it might be slow, 1 hour etc.
Can I use a single Web Job for this and somehow say that it should be running no more than 10 of these processes at a time?
Yes you can use a WebJob. I have created a simple WebJob with Storage Queue to just guide how it can be done. The below workflow will run only ten process at a time and keep all the other requests in memory of ConcurrentQueue. You will have to implement the logic to dequeue it and consume it
public class Functions
{
public delegate void CompletedProcessHandler(object sender, CompletedProcessHandlerArgs args);
static readonly Dictionary<int, CustomProcess> _dictionary =
new Dictionary<int, CustomProcess>();
static readonly ConcurrentQueue<ProcessEntity> _remaining =
new ConcurrentQueue<ProcessEntity>();
// This function will get triggered/executed when a new message is written
// on an Azure Queue called queue.
public static void ProcessQueueMessage([QueueTrigger("testqueue")] ProcessEntity msg,
TextWriter log)
{
if (_dictionary.Count <= 10)
{
var newProcess = new CustomProcess((_dictionary.Last().Key) + 1,
msg.Duration);
}
else
{
_remaining.Enqueue(msg);
}
}
public static void CompletedProcess(object sender, CompletedProcessHandlerArgs args)
{
_dictionary[Int32.Parse(args.ProcessID)].Dispose();
_dictionary.Remove(Int32.Parse(args.ProcessID));
}
}
public class CustomProcess : IDisposable
{
public event Functions.CompletedProcessHandler OnProcessCompleted;
private CancellationTokenSource _token;
private string _id;
private Timer _timer;
public CustomProcess(int i, int duration)
{
_timer = new Timer { Enabled = true, Interval = duration * 1000 };
_timer.Elapsed += Timer_Elapsed;
_id = i.ToString();
_token = new CancellationTokenSource();
Task.Factory.StartNew(() => WriteMessages());
_timer.Start();
OnProcessCompleted += Functions.CompletedProcess;
}
private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
_token.Cancel();
OnProcessCompleted?.Invoke(this, new CompletedProcessHandlerArgs(_id));
}
private void WriteMessages()
{
while (!_token.Token.IsCancellationRequested)
{
Console.WriteLine("Test Message from process " + _id);
}
}
public void Dispose()
{
_token.Dispose();
_timer.Dispose();
}
}
public class CompletedProcessHandlerArgs : EventArgs
{
public string ProcessID { get; set; }
public CompletedProcessHandlerArgs(string ID)
{
ProcessID = ID;
}
}
public class ProcessEntity
{
public int Duration { get; set; }
}
In the app.config of the web job you need to provide the two app settings
<add name="AzureWebJobsDashboard"
connectionString="DefaultEndpointsProtocol=https;AccountName=[AccountName];AccountKey=[AccountKey]" />
<add name="AzureWebJobsStorage"
connectionString="DefaultEndpointsProtocol=https;AccountName=[AccountName];AccountKey=[AccountKey]" />
The Program file is the default one from the Visual Studio template
public class Program
{
// Please set the following connection strings in app.config for this WebJob to run:
// AzureWebJobsDashboard and AzureWebJobsStorage
static void Main()
{
var host = new JobHost();
// The following code ensures that the WebJob will be running continuously
host.RunAndBlock();
}
}
WebJob will keep dequeue the message the moment it comes. Since you want only 10 to run at a time you will have to enqueue the message in memory and wait for running process to complete before you start a new one
As #Rick has mentioned you can set the is_Singleton property to true in settings.job file of the web job
Yes, you can trigger a web job with an Azure Service Bus Queue or Topic. A good example to look at to get you going would be the Service Bus quick start project template in Visual Studio.
In particular, you want to look at the ServiceBusTrigger attribute that the Web Jobs SDK provides.
As for the scalability of the web job, this will scale according to your web app instances. So, if you had say 5 instances of your web app with always on enabled, then you would have 5 instances of your web job. As an additional comment on this, if you wanted just one instance of the web job in an environment of 5 web app instances, then you could set the is_singleton property to true in the settings.job file.
This following code of mine is facing synchronization issue with hibernate sessions. There are few parallel threads in my code, each of them own its own hibernate session. The problem is, changes made by one session is not perceived by others for some unknown reasons. The code is located in github here
The problem :
Here I explain it with three threads: PRODUCER, CONSUMER_1, CONSUMER_2. The CONSUMER_1 waits for producer to finish its work, even after that, at the end, it doesn't see the changes made by PRODUCER thread. Why is it so?
package org.example.hibernate;
import org.example.hibernate.model.User;
import org.example.hibernate.util.HibernateUtil;
import java.util.Random;
public class Main {
/**
* This object acts as synchronisation semaphore between threads.
* (Note : aware that wait within hibernate session is discouraged)
* Here it is used to show that the consumer tries to read/get after
* producer has successfully completed the transaction.
* So here, the producer notifies waiting threads with this object
*/
public static final Object LOCK = new Object();
/**
* user Id is primary key, a random int is suffixed to preserve uniqueness
* Here, Producer saves an Object of this ID, then consumer tries to read it
*/
private static final String USER_ID = "user-" + new Random().nextInt(10000);
/**
* This is producer thread, it inserts a record and notifies about it to
* other waiting threads.
*/
private static Thread PRODUCER = new Thread("producer") {
// this this creates a user and notifies threads waiting for some event
#Override
public void run() {
HibernateUtil.getInstance().executeInSession(new Runnable() {
#Override
public void run() {
User user = new User();
user.setId(USER_ID);
user.setName("name-" + USER_ID);
user.save();
}
});
// outside the session
synchronized (LOCK) {
print("Notifying all consumers");
LOCK.notifyAll();
}
print("dying...");
}
};
/**
* This thread tries to read first, if it misses, then waits for the producer to
* notify, after it receives notification it tries to read again
*/
private static Thread CONSUMER_1 = new Thread("consumer_one"){
// this thread checks if data available(user with specific ID),
// if not available, waits for the the producer to notify it
#Override
public void run() {
HibernateUtil.getInstance().executeInSession(new Runnable() {
#Override
public void run() {
try {
User readUser = User.getById(USER_ID);
if(readUser == null) { // data not available
synchronized (LOCK) {
print("Data not available, Waiting for the producer...");
LOCK.wait(); // wait for the producer
print("Data available");
}
print("waiting for some more time....");
Thread.sleep(2 * 1000);
print("Enough of waiting... now going to read");
}
readUser = User.getById(USER_ID);
if(readUser == null) {
// why does this happen??
throw new IllegalStateException(
Thread.currentThread().getName()
+ " : This shouldn't be happening!!");
} else {
print("SUCCESS: Read user :" + readUser);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
print("dying...");
}
};
/**
* this thread waits for the the producer to notify it, then tries to read
*/
private static Thread CONSUMER_2 = new Thread("consumer_two"){
#Override
public void run() {
HibernateUtil.getInstance().executeInSession(new Runnable() {
#Override
public void run() {
try {
synchronized (LOCK) {
print("Data not available, Waiting for the producer...");
LOCK.wait(); // wait for the producer notification
print("Data available");
}
print("waiting for some more time....");
Thread.sleep(2 * 1000);
print("Enough of waiting... now going to read");
User readUser = User.getById(USER_ID);
if(readUser == null) {
throw new IllegalStateException(
Thread.currentThread().getName() +
" : This shouldn't be happening!!");
} else {
print("SUCCESS :: Read user :" + readUser);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
print("dying...");
}
};
/**
* Just another print method to include time stamp and thread name
* #param msg
*/
public static void print(String msg) {
System.out.println(Thread.currentThread().getName() + " : "
+ System.currentTimeMillis()+ " : "+ msg);
}
public static void main(String[] args) throws InterruptedException {
// Initialise hibernate in main thread
HibernateUtil.getInstance();
PRODUCER.start();
CONSUMER_1.start();
CONSUMER_2.start();
PRODUCER.join();
CONSUMER_1.join();
CONSUMER_2.join();
print("Exiting....");
}
}
And the Output :
INFO: HHH000232: Schema update complete
[main] INFO org.example.hibernate.util.HibernateUtil - Hibernate Initialised..
consumer_two : 1415036718712 : Data not available, Waiting for the producer...
[producer] INFO org.example.hibernate.util.HibernateUtil - Starting the transaction...
[consumer_two] INFO org.example.hibernate.util.HibernateUtil - Starting the transaction...
[consumer_one] INFO org.example.hibernate.util.HibernateUtil - Starting the transaction...
consumer_one : 1415036718831 : Data not available, Waiting for the producer...
[producer] INFO org.example.hibernate.util.HibernateUtil - Committing the transaction...
producer : 1415036718919 : Notifying all consumers
producer : 1415036718919 : dying...
consumer_one : 1415036718919 : Data available
consumer_one : 1415036718919 : waiting for some more time....
consumer_two : 1415036718919 : Data available
consumer_two : 1415036718919 : waiting for some more time....
[producer] INFO org.example.hibernate.util.HibernateUtil - Session was closed...
consumer_one : 1415036720919 : Enough of waiting... now going to read
consumer_two : 1415036720920 : Enough of waiting... now going to read
Nov 03, 2014 11:15:20 PM com.mchange.v2.c3p0.stmt.GooGooStatementCache assimilateNewCheckedOutStatement
INFO: Multiply prepared statement! select user0_.id as id1_0_0_, user0_.name as name2_0_0_ from user user0_ where user0_.id=?
java.lang.IllegalStateException: consumer_one : This shouldn't be happening!!
at org.example.hibernate.Main$2$1.run(Main.java:79)
at org.example.hibernate.util.HibernateUtil.executeInSession(HibernateUtil.java:60)
at org.example.hibernate.Main$2.run(Main.java:61)
[consumer_one] INFO org.example.hibernate.util.HibernateUtil - Committing the transaction...
[consumer_one] INFO org.example.hibernate.util.HibernateUtil - Session was closed...
consumer_one : 1415036720931 : dying...
consumer_two : 1415036720940 : SUCCESS :: Read user :User{id='user-422', name='name-user-422'} org.example.hibernate.model.User#4666d804
consumer_two : 1415036720943 : dying...
[consumer_two] INFO org.example.hibernate.util.HibernateUtil - Committing the transaction...
[consumer_two] INFO org.example.hibernate.util.HibernateUtil - Session was closed...
main : 1415036720943 : Exiting....
Here is my hibernate config :
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration SYSTEM "classpath://org/hibernate/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.hbm2ddl.auto">update</property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/hib_ex</property>
<property name="connection.username">hibuser</property>
<property name="connection.password">hibpass</property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">10</property>
<property name="hibernate.c3p0.min_size">5</property>
<property name="hibernate.c3p0.max_size">20</property>
<property name="hibernate.c3p0.timeout">1800</property>
<property name="hibernate.c3p0.max_statements">50</property>
<property name="connection.provider_class"> org.hibernate.connection.C3P0ConnectionProvider</property>
<property name="hibernate.cache.use_second_level_cache">false</property>
<property name="hibernate.cache.use_query_cache">false</property>
<property name="hibernate.cache.use_minimal_puts">true</property>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.current_session_context_class">thread</property>
<property name="hibernate.cache.provider_class">org.hibernate.cache.internal.NoCachingRegionFactory</property>
<property name="show_sql">false</property>
<mapping class="org.example.hibernate.model.User" />
</session-factory>
The hibernate Utility
public enum HibernateUtil {
INSTANCE;
private final Logger LOG = LoggerFactory.getLogger(HibernateUtil.class);
private final String CONFIG_FILE = "hibernate.xml";
private final SessionFactory sessionFactory;
HibernateUtil(){
LOG.info("Initialising hibernate...");
URL configUrl = getClass().getClassLoader().getResource(CONFIG_FILE);
final Configuration configuration = new Configuration();
try {
configuration.configure(configUrl);
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
.applySettings(configuration.getProperties())
.build();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
LOG.info("Hibernate Initialised..");
} catch (Exception e){
throw new IllegalStateException("Could not init hibernate!");
}
}
public Session getSession(){
if(sessionFactory.getCurrentSession() != null
&& sessionFactory.getCurrentSession().isOpen()) {
return sessionFactory.getCurrentSession();
} else {
LOG.info("Opening a session");
return sessionFactory.openSession();
}
}
public void executeInSession(Runnable runnable){
Session session = getSession();
Transaction transaction = session.getTransaction();
if(!transaction.isActive()){
LOG.info("Starting the transaction...");
transaction.begin();
}
try {
runnable.run();
} catch (Exception e){
e.printStackTrace();
} finally {
if(transaction.isActive()) {
LOG.info("Committing the transaction...");
transaction.commit();
} else {
LOG.info("Transaction was committed...");
}
if(session.isOpen()){
LOG.info("Closing the session...");
session.close();
} else {
LOG.info("Session was closed...");
}
}
}
public static HibernateUtil getInstance(){
return INSTANCE;
}
}
Please help me understand :
- Why CONSUMER_1 thread's User.getById(userId) returns null even after the PRODUCER thread's transaction successfully completes?
- How CONSUMER_2 thread's User.getById(userId) is able to get the same object almost at the same time when CONSUMER_1 is getting null ?
To save your valuable time, get the complete code from github repo
My guess is that your database transaction isolation guarantees repeatable reads. Since the consumer 1 start by reading the entity and finds it is null, and then later executes the same query in the same transaction, the same result is returned: null. Transactions run in isolation, that's the I is ACID. Your transactions should be as short as possible. You shouldn't let the transaction and the session opened when you found that the expected entity is not available. So, instead of doing
open session and transaction
get entity
wait for entity to be available
get entity again
close the transaction and session
you should do
open session and transaction
get entity
close the transaction and session
wait for entity to be available
open session and transaction
get entity again
close the transaction and session