Need Deadlock in web application - servlets

for some reason I want to check how the deadlock occurred in web application , so that's why I used the code below , but when I deploy the web application and test it , I did not get in the deadlock situation !! any help
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
import java.util.ArrayList;
import java.io.IOException;
import java.io.PrintWriter;
public class DeadLockServlet extends HttpServlet
{
public static ArrayList student = new ArrayList();
public static ArrayList employee = new ArrayList();
PrintWriter out;
#Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
String lsAction = request.getParameter("action");
String lsValue = request.getParameter("data");
out = response.getWriter();
String msg = "";
if (lsAction != null)
{
if (lsAction.equals("addStudent"))
{
addStudent(lsValue);
msg = "Student added: "+lsValue;
}
else if (lsAction.equals("addEmployee"))
{
addEmployee(lsValue);
msg = "Employee added: "+lsValue;
}
}
else
{
msg = "Invalid Request";
}
request.setAttribute("msg", msg);
request.setAttribute("student", student);
request.setAttribute("employee", employee);
request.getRequestDispatcher("index.jsp").forward(request, response);
}
public void addStudent(String lsValue)
{
synchronized (employee)
{
synchronized (student)
{
if (lsValue != null && !lsValue.equals(""))
{
student.add(lsValue);
}
}
}
}
public void addEmployee(String lsValue)
{
synchronized (student)
{
synchronized (employee)
{
if (lsValue != null && !lsValue.equals(""))
{
employee.add(lsValue);
}
}
}
}
}

Although your lock ordering will cause a deadlock, it requires exact timing. I don't think it's possible for you to do that without running that web server in the debugger and stepping through the code.
If you don't want to or can't do that, add a sufficiently long sleep between the two synchronized blocks in both addStudent and addEmployee. Say, 1 minute each. Then hit the servlet from two clients at around the same time, one doing addStudent, the other doing addEmployee.
The 'student' request handling thread will get the 'employee' lock, then sit and wait a bit. The 'employee' request handling thread will get the 'student' lock, then sit and wait. Then one of them will wake up, and try to grab the other lock, and the other thread will do the reverse, and presto: deadlock.
The add*** methods should be changed to something like this:
public void addStudent(String lsValue)
{
synchronized (employee)
{
Thread.sleep(60 * 1000);
synchronized (student)
{
if (lsValue != null && !lsValue.equals(""))
{
student.add(lsValue);
}
}
}
}
public void addEmployee(String lsValue)
{
synchronized (student)
{
Thread.sleep(60 * 1000);
synchronized (employee)
{
if (lsValue != null && !lsValue.equals(""))
{
employee.add(lsValue);
}
}
}
}
Obviously deadlocks are to be avoided, but hey, that's a whole nother question!

Related

Quarkus and reactive datasources - Error Multiple matching properties for name "datasource.url"

I have a problem connecting to the postgres database using PgPool and ResulSet, then the Statement of sql. Here is my class of service.
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.servlet.http.HttpSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.context.annotation.ApplicationScope;
//import io.vertx.reactivex.pgclient.PgPool;
import io.vertx.axle.pgclient.PgPool;
import ml.kalansow.domain.StudentFees;
import ml.kalansow.service.KalansowService;
#Service
#ApplicationScope
public class StudentFeesService implements KalansowService {
private static final Logger LOG = LoggerFactory.getLogger(StudentFeesService.class);
PgPool client;
// ----Constructor------------------------------------------
public StudentFeesService() {
// TODO Auto-generated constructor stub
};
// ------------------------------Method---------------------------------
#Override
public String getServiceName() {
return this.getClass().getName();
}
public void processGetFeesDetails(HttpSession session) {
String strStudentId = (String) session.getAttribute("StudentId");
StudentFees studentFees = new StudentFees();
if (strStudentId != null) {
// This is mandatory before calling the next method
studentFees.setStudentId(strStudentId);
populateFeesInfo(studentFees);
session.setAttribute("studentFees", studentFees);
} else {
LOG.error("Student Id is null");
}
}
private void populateFeesInfo(StudentFees studentFees) {
String strStudentI = studentFees.getStudentId();
io.vertx.sqlclient.impl.Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
StringBuffer sbQuery = new StringBuffer();
sbQuery.append("SELECT * FROM STUDENT_FEES WHERE STUDENT_I=");
sbQuery.append("" + strStudentI + "''");
if (strStudentI != null) {
//connection = DatabaseService.getDBConnection();
connection=(io.vertx.sqlclient.impl.Connection) client.getConnection();
try {
statement = ((Connection) connection).createStatement();
resultSet = statement.executeQuery(sbQuery.toString());
resultSet.next();
studentFees.setJanAcad(resultSet.getString("JAN_ACAD"));
studentFees.setFebAcad(resultSet.getString("FEB_ACAD"));
studentFees.setMarAcad(resultSet.getString("MAR_ACAD"));
studentFees.setAprAcad(resultSet.getString("APR_ACAD"));
studentFees.setMayAcad(resultSet.getString("MAY_ACAD"));
studentFees.setJunAcad(resultSet.getString("JUN_ACAD"));
studentFees.setJulAcad(resultSet.getString("JUL_ACAD"));
studentFees.setAugAcad(resultSet.getString("AUG_ACAD"));
studentFees.setSepAcad(resultSet.getString("SEP_ACAD"));
studentFees.setOctAcad(resultSet.getString("OCT_ACAD"));
studentFees.setNovAcad(resultSet.getString("NOV_ACAD"));
studentFees.setDecAcad(resultSet.getString("DEC_ACAD"));
} catch (SQLException e) {
LOG.error(e.getMessage());
} finally {
/*DatabaseService.closeDBConnection(statement, resultSet);
DatabaseService.realeaseDBConnection();*/
client.close();
}
} else {
LOG.error("Student id is null");
}
}
}
My application properties file contain datasource properties
quarkus.datasource.driver=org.postgresql.Driver
quarkus.reactive-datasource.url=vertx-reactive:postgresql://localhost:5432/test
quarkus.reactive-datasource.username=test
quarkus.reactive-datasource.password=test
And console is here
ERROR [io.qua.dev.DevModeMain] Failed to start Quarkus: java.lang.IllegalArgumentException: Multiple matching properties for name "datasource.url" property was matched by both public java.util.Optional io.quarkus.agroal.runtime.DataSourceRuntimeConfig.url and public java.util.Optional io.quarkus.reactive.pg.client.runtime.DataSourceConfig.url. This is likely because you have an incompatible combination of extensions that both define the same properties (e.g. including both reactive and blocking database extensions)
at io.quarkus.deployment.configuration.matching.PatternMapBuilder.addMember(PatternMapBuilder.java:71)
at io.quarkus.deployment.configuration.matching.PatternMapBuilder.addGroup(PatternMapBuilder.java:60)
at io.quarkus.deployment.configuration.matching.PatternMapBuilder.addMember(PatternMapBuilder.java:85)
at io.quarkus.deployment.configuration.matching.PatternMapBuilder.addGroup(PatternMapBuilder.java:60)
at io.quarkus.deployment.configuration.matching.PatternMapBuilder.makePatterns(PatternMapBuilder.java:35)
at io.quarkus.deployment.configuration.BuildTimeConfigurationReader.(BuildTimeConfigurationReader.java:107)
at io.quarkus.deployment.ExtensionLoader.loadStepsFrom(ExtensionLoader.java:174)
at io.quarkus.deployment.QuarkusAugmentor.run(QuarkusAugmentor.java:85)
at io.quarkus.runner.RuntimeRunner.run(RuntimeRunner.java:114)
at io.quarkus.dev.DevModeMain.doStart(DevModeMain.java:178)
at io.quarkus.dev.DevModeMain.start(DevModeMain.java:96)
You cannot use both the Agroal extension and the Reactive datasources together for the time being.
We are discussing possible ways to fix that here: https://groups.google.com/d/msg/quarkus-dev/3r0lquVsUsc/DVxX7SvQAQAJ .
But for now, your only choice is to use either one or the other.

RocksDB: how to use ttl in java?

I am testing rocksdb for java api. I put a key-value entry into the map, and then wait for 20s, then get from map. Why the entry is not deleted?
this is my code:
import org.rocksdb.Options;
import org.rocksdb.RocksDBException;
import org.rocksdb.TtlDB;
public class Test2 {
public static void main(String[] args) throws Exception {
TtlDB.loadLibrary();
Options options = new Options()
.setCreateIfMissing(true);
TtlDB db = TtlDB.open(options, args[0],20,false);
if(args.length > 3 && "put".equals(args[1])) {
db.put(args[2].getBytes(), args[3].getBytes());
}
byte[] arr = db.get(args[2].getBytes());
if(arr != null) {
System.out.println(new String(arr));
} else {
System.out.println(arr);
}
System.out.println(db.get(args[2].getBytes()));
Thread.sleep(21000);
System.out.println(db.get(args[2].getBytes()));
db.close();
}
}
Expired TTL values are deleted in compaction only:(Timestamp + ttl < time_now)
https://github.com/facebook/rocksdb/wiki/Time-to-Live

Can Storm's HdfsBolt flush data after a timeout as well?

We are using Storm to process streaming data and store into HDFS. We have got everything to work but have one issue. I understand that we can specify the number of tuples after which the data gets flushed to HDFS using SyncPolicy, something like this below:
SyncPolicy syncPolicy = new CountSyncPolicy(Integer.parseInt(args[3]));
The question I have is can the data also be flushed after a timeout? For e.g. we have set the SyncPolicy above to 1000 tuples. If for whatever reason we get 995 tuples and then the data stops coming in for a while is there any way that storm can flush the 995 records to HDFS after a specified timeout (5 seconds)?
Thanks in advance for any help on this!
Shay
Yes, if you send a tick tuple to the HDFS bolt, it will cause the bolt to try to sync to the HDFS file system. All this happens in the HDFS bolt's execute function.
To configure tick tuples for your topology, in your topology config. In Java, to set that to every 300 seconds the code would look like:
Config topologyConfig = new Config();
topologyConfig.put(Config.TOPOLOGY_TICK_TUPLE_FREQ_SECS, 300);
StormSubmitter.submitTopology("mytopology", topologyConfig, builder.createTopology());
You'll have to adjust that last line depending on your circumstances.
There is an alternative solution for this problem,
First, lets clarify about sync policy, If your sync policy is 1000 ,then HdfsBolt only sync the data from 1000 tuple by calling hsync() method in execute() means it only clears the buffer by pushing data to disk, but for faster write disk may uses its cache and not writing to file directly.
The data is written to the file only when the size of data matches your rotation policy that need to specify at the time of bolt creation.
FileRotationPolicy rotationPolicy = new FileSizeRotationPolicy(100.0f, Units.KB);
So for flushing the record the to file after timeout, Seperate your tick tuple from normal tuples in excecute method and calculate the time difference of both tuple, If the diff is greater than timeout period then write the data to file.
By handling tick tuple differently you can also avoid the tick tuple frequency written to your file.
See the below code for better understanding:
public class CustomHdfsBolt1 extends AbstractHdfsBolt {
private static final Logger LOG = LoggerFactory.getLogger(CustomHdfsBolt1.class);
private transient FSDataOutputStream out;
private RecordFormat format;
private long offset = 0L;
private int tickTupleCount = 0;
private String type;
private long normalTupleTime;
private long tickTupleTime;
public CustomHdfsBolt1() {
}
public CustomHdfsBolt1(String type) {
this.type = type;
}
public CustomHdfsBolt1 withFsUrl(String fsUrl) {
this.fsUrl = fsUrl;
return this;
}
public CustomHdfsBolt1 withConfigKey(String configKey) {
this.configKey = configKey;
return this;
}
public CustomHdfsBolt1 withFileNameFormat(FileNameFormat fileNameFormat) {
this.fileNameFormat = fileNameFormat;
return this;
}
public CustomHdfsBolt1 withRecordFormat(RecordFormat format) {
this.format = format;
return this;
}
public CustomHdfsBolt1 withSyncPolicy(SyncPolicy syncPolicy) {
this.syncPolicy = syncPolicy;
return this;
}
public CustomHdfsBolt1 withRotationPolicy(FileRotationPolicy rotationPolicy) {
this.rotationPolicy = rotationPolicy;
return this;
}
public CustomHdfsBolt1 addRotationAction(RotationAction action) {
this.rotationActions.add(action);
return this;
}
protected static boolean isTickTuple(Tuple tuple) {
return tuple.getSourceComponent().equals(Constants.SYSTEM_COMPONENT_ID)
&& tuple.getSourceStreamId().equals(Constants.SYSTEM_TICK_STREAM_ID);
}
public void execute(Tuple tuple) {
try {
if (isTickTuple(tuple)) {
tickTupleTime = Calendar.getInstance().getTimeInMillis();
long timeDiff = normalTupleTime - tickTupleTime;
long diffInSeconds = TimeUnit.MILLISECONDS.toSeconds(timeDiff);
if (diffInSeconds > 5) { // specify the value you want.
this.rotateWithOutFileSize(tuple);
}
} else {
normalTupleTime = Calendar.getInstance().getTimeInMillis();
this.rotateWithFileSize(tuple);
}
} catch (IOException var6) {
LOG.warn("write/sync failed.", var6);
this.collector.fail(tuple);
}
}
public void rotateWithFileSize(Tuple tuple) throws IOException {
syncHdfs(tuple);
this.collector.ack(tuple);
if (this.rotationPolicy.mark(tuple, this.offset)) {
this.rotateOutputFile();
this.offset = 0L;
this.rotationPolicy.reset();
}
}
public void rotateWithOutFileSize(Tuple tuple) throws IOException {
syncHdfs(tuple);
this.collector.ack(tuple);
this.rotateOutputFile();
this.offset = 0L;
this.rotationPolicy.reset();
}
public void syncHdfs(Tuple tuple) throws IOException {
byte[] e = this.format.format(tuple);
synchronized (this.writeLock) {
this.out.write(e);
this.offset += (long) e.length;
if (this.syncPolicy.mark(tuple, this.offset)) {
if (this.out instanceof HdfsDataOutputStream) {
((HdfsDataOutputStream) this.out).hsync(EnumSet.of(SyncFlag.UPDATE_LENGTH));
} else {
this.out.hsync();
}
this.syncPolicy.reset();
}
}
}
public void closeOutputFile() throws IOException {
this.out.close();
}
public void doPrepare(Map conf, TopologyContext topologyContext, OutputCollector collector) throws IOException {
LOG.info("Preparing HDFS Bolt...");
this.fs = FileSystem.get(URI.create(this.fsUrl), this.hdfsConfig);
this.tickTupleCount = 0;
this.normalTupleTime = 0;
this.tickTupleTime = 0;
}
public Path createOutputFile() throws IOException {
Path path = new Path(this.fileNameFormat.getPath(),
this.fileNameFormat.getName((long) this.rotation, System.currentTimeMillis()));
this.out = this.fs.create(path);
return path;
}
}
You can directly use this class in your project.
Thanks,

SyncAdapter onPerformSync get current location

When onPerformSync occurs I need the current location but I do not want to set up a separate service that is constantly active requesting location because my SyncAdapter period exponentially backs off such that the periods between syncs could be many hours apart. It would be wasteful to have location requests running between each sync.
I am planning on using a GoogleApiClient and LocationServices.FusedLocationApi.requestLocationUpdates then Thread.sleep(###) the onPerformSync thread until a location is found.
However I have read that requestLocationUpdates needs to be called on the main looper and that it makes callbacks on that thread in which case I expect will it fail to return location results because I am sleeping on the thread which called it.
Will I need to start my own looper thread?
Is there another/better way to get current location from onPerformSync?
Turns out my fears were not justified, my method does work without error. I have put together a handy example class below in case anyone else wants to do this:
public class cSyncLocation implements ConnectionCallbacks, OnConnectionFailedListener, LocationListener
{
// =======================================================
// private vars
// =======================================================
private GoogleApiClient moGoogleApiClient;
private LocationRequest moLocationRequest;
private Location moCurrentLocation;
private static final int kTIMEOUT_MILLISECONDS = 2500;
// =======================================================
// public static vars
// =======================================================
// =======================================================
// public methods
// =======================================================
public void Start(Context oContext)
{
if (moGoogleApiClient == null)
{
moGoogleApiClient = new GoogleApiClient.Builder(oContext)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
}
if (moLocationRequest == null)
{
moLocationRequest = new LocationRequest();
moLocationRequest.setInterval(1);
moLocationRequest.setFastestInterval(1);
moLocationRequest.setInterval(1);
moLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}
// Start the connection
if (moGoogleApiClient != null)
{
if (!moGoogleApiClient.isConnecting() && !moGoogleApiClient.isConnected())
moGoogleApiClient.connect();
else if (moCurrentLocation == null)
LocationServices.FusedLocationApi.requestLocationUpdates(moGoogleApiClient, moLocationRequest, this);
}
}
public void Stop()
{
if (moGoogleApiClient != null && moGoogleApiClient.isConnected())
LocationServices.FusedLocationApi.removeLocationUpdates(moGoogleApiClient, this);
if (moGoogleApiClient != null)
moGoogleApiClient.disconnect();
}
public Location GetLocationBlocking(Context oContext)
{
if (moCurrentLocation == null)
{
intTimeout = kTIMEOUT_MILLISECONDS;
Start(oContext);
while(intTimeout > 0 && aFrmLocationActivity.IsLastLocationExpired(oContext))
{
Thread.sleep(100);
intTimeout -= 100;
}
Stop();
}
return moCurrentLocation;
}
// =======================================================
// Location API Events
// =======================================================
#Override
public void onLocationChanged(Location oLocation)
{
if (oLocation != null)
{
moCurrentLocation = oLocation;
}
}
// =======================================================
// Google API Connection Events
// =======================================================
#Override
public void onConnected(Bundle connectionHint)
{
// Connected to Google Play services! The good stuff goes here.
if (moGoogleApiClient != null)
{
Location oLocation = LocationServices.FusedLocationApi.getLastLocation(moGoogleApiClient);
if (oLocation != null)
moCurrentLocation = oLocation;
else
LocationServices.FusedLocationApi.requestLocationUpdates(moGoogleApiClient, moLocationRequest, this);
}
}
#Override
public void onConnectionSuspended(int cause)
{
//...
}
#Override
public void onConnectionFailed(ConnectionResult result)
{
//...
}
}
How to use it, in your onPerformSync method call it like this
cSyncLocation oSyncLocation = new cSyncLocation();
Location oLocation = oSyncLocation.GetLocationBlocking(getContext());
Obviously you will want to add some exception handling and deal with null location result.

When the SQLiteOpenHelper onCreate method is called?

I tried to create an SQLite database and do some stuff with it. But I found that my onCreate method is not even invoked!!
I am sending a message to LogCat on the begining of the onCreate method.
My assumption is, the (super) constructor will invoke onCreate method. Is that right?
My Code:
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase;
import android.content.Context;
import android.database.Cursor;
import android.content.ContentValues;
import android.util.Log;
public class DatabaseHandler extends SQLiteOpenHelper {
// Static Constants
/*** Database details ***/
// Database version
private static final int DATABASE_VERSION = 1;
// Database name
private static final String DATABASE_NAME = "database_name";
/*** Database Tables ***/
/** Events **/
// Event table
private static final String TABLE_EVENT = "event";
// Event table columns
private static final String COLUMN_EVENT_EID = "_eid";
private static final String COLUMN_EVENT_CREATION_DATE = "creation_date";
private static final String COLUMN_EVENT_TITLE = "title";
private static final String COLUMN_EVENT_ICON = "icon";
public DatabaseHandler(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
Log.e("MyApp", "onCreate invoked");
// Tables creation queries
String CREATE_EVENT_TABLE = "create table " + TABLE_EVENT + "(" + COLUMN_EVENT_EID + " integer primary key, "
+ COLUMN_EVENT_CREATION_DATE + " text, "
+ COLUMN_EVENT_TITLE + " text, "
+ COLUMN_EVENT_ICON + " text)";
// Creating tables
db.execSQL(CREATE_EVENT_TABLE);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.e("MyApp", "onUpgrade invoked");
db.execSQL("DROP TABLE IF EXISTS " + TABLE_EVENT);
}
}
MainActivity Code:
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DatabaseHandler db = new DatabaseHandler(this);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
The documentation says:
The database is not actually created or opened until one of getWritableDatabase() or getReadableDatabase() is called.
Let me clear the on the logic flow. Here is Lazy-initialization concept.
The (super) constructor on DatabaseHandler will not invoke onCreate method. Calling DatabaseHandler constructor will initializes: context, database name, factory that creates the database, database version, and database error handler.
getWritableDatabase() > getDatabaseLocked() > - SQLiteDatabase.create()
OR
getReadableDatabase() > getDatabaseLocked() > - SQLiteDatabase.create()
Answer: After your database gets created successfully, your configurations changes, next time again getReadableDatabase() or getWritableDatabase() calls getDatabaseLocked() and there onCreate(db) method inside getDatabaseLocked() gets executed.
Explanation:
The above SQLiteDatabase.create() method is responsible to create SQLiteDatabase in the disk.
But the process in lazy-initialization (mean, it doesn't make everything ready. It creates those objects on the runtime if you need them. For this it used a lot of if..else statements).
If you see the full body of getDatabaseLocked(), this is below. [You can search onCreate() method inside the body of getDatabaseLocked()]
private SQLiteDatabase getDatabaseLocked(boolean writable) {
if (mDatabase != null) {
if (!mDatabase.isOpen()) {
// Darn! The user closed the database by calling mDatabase.close().
mDatabase = null;
} else if (!writable || !mDatabase.isReadOnly()) {
// The database is already open for business.
return mDatabase;
}
}
if (mIsInitializing) {
throw new IllegalStateException("getDatabase called recursively");
}
SQLiteDatabase db = mDatabase;
try {
mIsInitializing = true;
if (db != null) {
if (writable && db.isReadOnly()) {
db.reopenReadWrite();
}
} else if (mName == null) {
db = SQLiteDatabase.create(null);
} else {
try {
if (DEBUG_STRICT_READONLY && !writable) {
final String path = mContext.getDatabasePath(mName).getPath();
db = SQLiteDatabase.openDatabase(path, mFactory,
SQLiteDatabase.OPEN_READONLY, mErrorHandler);
} else {
db = mContext.openOrCreateDatabase(mName, mEnableWriteAheadLogging ?
Context.MODE_ENABLE_WRITE_AHEAD_LOGGING : 0,
mFactory, mErrorHandler);
}
} catch (SQLiteException ex) {
if (writable) {
throw ex;
}
Log.e(TAG, "Couldn't open " + mName
+ " for writing (will try read-only):", ex);
final String path = mContext.getDatabasePath(mName).getPath();
db = SQLiteDatabase.openDatabase(path, mFactory,
SQLiteDatabase.OPEN_READONLY, mErrorHandler);
}
}
onConfigure(db);
final int version = db.getVersion();
if (version != mNewVersion) {
if (db.isReadOnly()) {
throw new SQLiteException("Can't upgrade read-only database from version " +
db.getVersion() + " to " + mNewVersion + ": " + mName);
}
db.beginTransaction();
try {
if (version == 0) {
onCreate(db);
} else {
if (version > mNewVersion) {
onDowngrade(db, version, mNewVersion);
} else {
onUpgrade(db, version, mNewVersion);
}
}
db.setVersion(mNewVersion);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
onOpen(db);
if (db.isReadOnly()) {
Log.w(TAG, "Opened " + mName + " in read-only mode");
}
mDatabase = db;
return db;
} finally {
mIsInitializing = false;
if (db != null && db != mDatabase) {
db.close();
}
}
}
Please note, inside the body of getDatabaseLocked() method, there are so many if.. else cases. These if.. else cases determines your current environment (configuration), and based on your current environment they call appropriate methods to initialize/configure whatever needed.
Also, note: All the callbacks methods in your DatabaseHandler (class that implemented SQLiteOpenHelper) are called inside the getDatabaseLocked() body.
Source code SQLiteOpenHelper.java:
https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/database/sqlite/SQLiteOpenHelper.java
Source code SQLiteDatabase.java:
https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/database/sqlite/SQLiteDatabase.java
Sample to follow: https://github.com/uddhavgautam/SQLiteBasicSample
Your are right, the (super) constructor will invoke onCreate method, BUT only if the actual database does not exits.
From http://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper.html#onCreate%28android.database.sqlite.SQLiteDatabase%29
A helper class to manage database creation and version management.
You create a subclass implementing onCreate(SQLiteDatabase),
onUpgrade(SQLiteDatabase, int, int) and optionally
onOpen(SQLiteDatabase), and this class takes care of opening the
database if it exists, creating it if it does not, and upgrading it as
necessary.
As the official documents says, "getWritableDatabase () Create and/or open a database that will be used for reading and writing. The first time this is called, the database will be opened and onCreate(SQLiteDatabase), onUpgrade(SQLiteDatabase, int, int) and/or onOpen(SQLiteDatabase) will be called."
Once opened successfully, the database is cached, so you can call this method every time you need to write to the database. (Make sure to call close() when you no longer need the database.) Errors such as bad permissions or a full disk may cause this method to fail, but future attempts may succeed if the problem is fixed.
http://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper.html#getWritableDatabase()

Resources