/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jackrabbit.core.persistence.pool;
import org.apache.jackrabbit.core.persistence.PMContext;
import org.apache.jackrabbit.core.persistence.db.DatabasePersistenceManager;
import org.apache.jackrabbit.core.util.db.ConnectionHelper;
import org.apache.jackrabbit.core.util.db.DerbyConnectionHelper;
import java.sql.SQLException;
import javax.sql.DataSource;
/**
* Extends the {@link BundleDbPersistenceManager} by derby specific code.
* <p>
* Configuration:<br>
* <ul>
* <li><param name="{@link #setBundleCacheSize(String) bundleCacheSize}" value="8"/>
* <li><param name="{@link #setConsistencyCheck(String) consistencyCheck}" value="false"/>
* <li><param name="{@link #setMinBlobSize(String) minBlobSize}" value="16384"/>
* <li><param name="{@link #setDriver(String) driver}" value="org.apache.derby.jdbc.EmbeddedDriver"/>
* <li><param name="{@link #setUrl(String) url}" value="jdbc:derby:${wsp.home}/db/itemState;create=true"/>
* <li><param name="{@link #setUser(String) user}" value=""/>
* <li><param name="{@link #setPassword(String) password}" value=""/>
* <li><param name="{@link #setSchema(String) schema}" value="derby"/>
* <li><param name="{@link #setSchemaObjectPrefix(String) schemaObjectPrefix}" value=""/>
* <li><param name="{@link #setErrorHandling(String) errorHandling}" value=""/>
* <li><param name="{@link #setDerbyStorageInitialPages(String) derbyStorageInitialPages}" value="16"/>
* <li><param name="{@link #setDerbyStorageMinimumRecordSize(String) derbyStorageMinimumRecordSize}" value="256"/>
* <li><param name="{@link #setDerbyStoragePageCacheSize(String) derbyStoragePageCacheSize}" value="1024"/>
* <li><param name="{@link #setDerbyStoragePageReservedSpace(String) derbyStoragePageReservedSpace}" value="20"/>
* <li><param name="{@link #setDerbyStoragePageSize(String) derbyStoragePageSize}" value="16384"/>
* </ul>
*/
public class DerbyPersistenceManager extends BundleDbPersistenceManager {
/** name of the embedded driver */
public static final String DERBY_EMBEDDED_DRIVER = "org.apache.derby.jdbc.EmbeddedDriver";
/** @see #setDerbyStorageInitialPages(String) */
private int derbyStorageInitialPages = 16;
/** @see #setDerbyStorageMinimumRecordSize(String) */
private int derbyStorageMinimumRecordSize = 512;
/** @see #setDerbyStoragePageCacheSize(String) */
private int derbyStoragePageCacheSize = 1024;
/** @see #setDerbyStoragePageReservedSpace(String) */
private int derbyStoragePageReservedSpace = 20;
/** @see #setDerbyStoragePageSize(String) */
private int derbyStoragePageSize = 16384;
/**
* @see #setDerbyStorageInitialPages
* @return the initial pages property
*/
public String getDerbyStorageInitialPages() {
return String.valueOf(derbyStorageInitialPages);
}
/**
* The on-disk size of a Derby table grows by one page at a time until eight
* pages of user data (or nine pages of total disk use, one is used for
* overhead) have been allocated. Then it will grow by eight pages at a time
* if possible.
* <p>
* A Derby table or index can be created with a number of pages already
* pre-allocated. To do so, specify the property prior to the CREATE TABLE
* or CREATE INDEX statement.
* <p>
* Define the number of user pages the table or index is to be created with.
* The purpose of this property is to preallocate a table or index of
* reasonable size if the user expects that a large amount of data will be
* inserted into the table or index. A table or index that has the
* pre-allocated pages will enjoy a small performance improvement over a
* table or index that has no pre-allocated pages when the data are loaded.
* <p>
* The total desired size of the table or index should be
* <p>
* <strong>(1+derby.storage.initialPages) * derby.storage.pageSize bytes.</strong>
* <p>
* When you create a table or an index after setting this property, Derby
* attempts to preallocate the requested number of user pages. However, the
* operations do not fail even if they are unable to preallocate the
* requested number of pages, as long as they allocate at least one page.
* <p>
* Default is <code>16</code>
*
* @param derbyStorageInitialPages the number of initial pages
*/
public void setDerbyStorageInitialPages(String derbyStorageInitialPages) {
this.derbyStorageInitialPages =
Integer.decode(derbyStorageInitialPages).intValue();
}
/**
* @see #setDerbyStorageMinimumRecordSize
* @return the minimum record size
*/
public String getDerbyStorageMinimumRecordSize() {
return String.valueOf(derbyStorageMinimumRecordSize);
}
/**
* Indicates the minimum user row size in bytes for on-disk database pages
* for tables when you are creating a table. This property ensures that
* there is enough room for a row to grow on a page when updated without
* having to overflow. This is generally most useful for VARCHAR and
* VARCHAR FOR BIT DATA data types and for tables that are updated a lot,
* in which the rows start small and grow due to updates. Reserving the
* space at the time of insertion minimizes row overflow due to updates,
* but it can result in wasted space. Set the property prior to issuing the
* CREATE TABLE statement.
* <p>
* Default is <code>256</code>
*
* @param derbyStorageMinimumRecordSize the minimum record size
*/
public void setDerbyStorageMinimumRecordSize(String derbyStorageMinimumRecordSize) {
this.derbyStorageMinimumRecordSize =
Integer.decode(derbyStorageMinimumRecordSize).intValue();
}
/**
* @see #setDerbyStoragePageCacheSize
* @return the page cache size
*/
public String getDerbyStoragePageCacheSize() {
return String.valueOf(derbyStoragePageCacheSize);
}
/**
* Defines the size, in number of pages, of the database's data page cache
* (data pages kept in memory). The actual amount of memory the page cache
* will use depends on the following:
* <ul>
* <li> the size of the cache (configured with {@link #setDerbyStoragePageCacheSize})
* <li> the size of the pages (configured with {@link #setDerbyStoragePageSize})
* <li> overhead (varies with JVMs)
* </ul>
* When increasing the size of the page cache, you typically have to allow
* more memory for the Java heap when starting the embedding application
* (taking into consideration, of course, the memory needs of the embedding
* application as well). For example, using the default page size of 4K, a
* page cache size of 2000 pages will require at least 8 MB of memory (and
* probably more, given the overhead).
* <p>
* The minimum value is 40 pages. If you specify a lower value, Derby uses
* the default value.
* <p>
* Default is <code>1024</code> (which gives about 16mb memory usage given
* the default of 16384 as page size).
*
* @param derbyStoragePageCacheSize the page cache size
*/
public void setDerbyStoragePageCacheSize(String derbyStoragePageCacheSize) {
this.derbyStoragePageCacheSize =
Integer.decode(derbyStoragePageCacheSize).intValue();
}
/**
* @see #setDerbyStoragePageReservedSpace
* @return the page reserved space
*/
public String getDerbyStoragePageReservedSpace() {
return String.valueOf(derbyStoragePageReservedSpace);
}
/**
* Defines the percentage of space reserved for updates on an on-disk
* database page for tables only (not indexes); indicates the percentage of
* space to keep free on a page when inserting. Leaving reserved space on a
* page can minimize row overflow (and the associated performance hit)
* during updates. Once a page has been filled up to the reserved-space
* threshold, no new rows are allowed on the page. This reserved space is
* used only for rows that increase in size when updated, not for new
* inserts. Set this property prior to issuing the CREATE TABLE statement.
* <p>
* Regardless of the value of derby.storage.pageReservedSpace, an empty page
* always accepts at least one row.
* <p>
* Default is <code>20%</code>
*
* @param derbyStoragePageReservedSpace the page reserved space
*/
public void setDerbyStoragePageReservedSpace(String derbyStoragePageReservedSpace) {
this.derbyStoragePageReservedSpace =
Integer.decode(derbyStoragePageReservedSpace).intValue();
}
/**
* @see #setDerbyStoragePageSize
* @return the page size
*/
public String getDerbyStoragePageSize() {
return String.valueOf(derbyStoragePageSize);
}
/**
* Defines the page size, in bytes, for on-disk database pages for tables or
* indexes used during table or index creation. Page size can only be one
* the following values: 4096, 8192, 16384, or 32768. Set this property
* prior to issuing the CREATE TABLE or CREATE INDEX statement. This value
* will be used for the lifetime of the newly created conglomerates.
* <p>
* Default is <code>16384</code>
*
* @param derbyStoragePageSize the storage page size
*/
public void setDerbyStoragePageSize(String derbyStoragePageSize) {
this.derbyStoragePageSize = Integer.decode(derbyStoragePageSize).intValue();
}
/**
* {@inheritDoc}
*/
public void init(PMContext context) throws Exception {
// init default values
if (getDriver() == null) {
setDriver(DERBY_EMBEDDED_DRIVER);
}
if (getDatabaseType() == null) {
setDatabaseType("derby");
}
if (getUrl() == null) {
setUrl("jdbc:derby:" + context.getHomeDir().getPath() + "/db/itemState;create=true");
}
if (getSchemaObjectPrefix() == null) {
setSchemaObjectPrefix("");
}
super.init(context);
// set properties
if (DERBY_EMBEDDED_DRIVER.equals(getDriver())) {
conHelper.exec("CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY "
+ "('derby.storage.initialPages', '" + derbyStorageInitialPages + "')");
conHelper.exec("CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY "
+ "('derby.storage.minimumRecordSize', '" + derbyStorageMinimumRecordSize + "')");
conHelper.exec("CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY "
+ "('derby.storage.pageCacheSize', '" + derbyStoragePageCacheSize + "')");
conHelper.exec("CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY "
+ "('derby.storage.pageReservedSpace', '" + derbyStoragePageReservedSpace + "')");
conHelper.exec("CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY " + "('derby.storage.pageSize', '"
+ derbyStoragePageSize + "')");
}
}
/**
* {@inheritDoc}
*/
@Override
protected ConnectionHelper createConnectionHelper(DataSource dataSrc) {
return new DerbyConnectionHelper(dataSrc, blockOnConnectionLoss);
}
/**
* {@inheritDoc}
*
* Since Derby cannot handle binary indexes, we use long-long keys.
*
* @return {@link BundleDbPersistenceManager#SM_LONGLONG_KEYS}
*/
public int getStorageModel() {
return BundleDbPersistenceManager.SM_LONGLONG_KEYS;
}
/**
* Closes the given connection by shutting down the embedded Derby
* database.
*
* @throws SQLException if an error occurs
*/
public void close() throws Exception {
super.close();
((DerbyConnectionHelper) conHelper).shutDown(getDriver());
}
}