Below, You see a part of Application Programming Notes Java Card 3 Platform Classic Edition about object deletion management :
void updateBuffer(byte requiredSize)
{
try
{
if(buffer != null && buffer.length == requiredSize)
{
//we already have a buffer of required size
return;
}
JCSystem.beginTransaction();
byte[] oldBuffer = buffer;
buffer = new byte[requiredSize];
if (oldBuffer != null)
JCSystem.requestObjectDeletion();
JCSystem.commitTransaction();
}
catch(Exception e)
{
JCSystem.abortTransaction();
}
}
The question is :
When I call JCSystem.requestObjectDeletion(); in the ifexpression, How it recognize which one the buffer or oldBuffer object must be delete?
Object deletion is typically performed during start up. So basically the system can sweep the memory just like a normal Java Garbage Collector at that time.
If no references are found to a specific object, then the space occupied by that object can be collected. As the oldBuffer reference is out of scope by that time there will be no reference to the old array left. The exact memory management is implementation dependent.
Related
I need to attach custom data into new fields added to INTranCost when the PO Receipt occurs.
Following the breadcrumbs, it seems that POReceiptEntry -> Release Action eventually calls INDocumentRelease.ReleaseDoc that eventually creates INTranCost. I tried extending both POReceiptEntry and INDocumentRelease to add an event for INTranCost_RowInserted to publish a PXTrace message, but the trace doesn't appear, telling me that I'm not hitting the event that I expected. (Which explains why the real business logic I need included didn't fire.)
protected virtual void _(Events.RowInserted<INTranCost> e)
{
PXTrace.WriteInformation("This is it!");
}
Of course, I want to put real code in this spot, but I am just trying to make sure I'm hitting the event properly. This works on pretty much everything else I've done, including attaching similar data to INTranExt fields. I cannot get it to work for INTranCost so that I can add to INTranCostExt. At this point, I can't determine if it is location (which graph extension) or a special methodology required for this special case.
I also tried overriding events and putting a breakpoint on the code, but it's like I'm not even on the same process. (Yes, I checked that I am connected to the right Acumatica instance and that I have no errors.)
What event in which graph is required to capture the creation in INTranCost for a PO Receipt to update custom fields in INTranCostExt?
Using Request Profiler, I was able to determine that I was close but not deep enough. While the INTranCost object to insert was built in INDocumentRelease FILE, the actual insert was processed in INReleaseProcess graph in that same file.
I only need to execute this "push" from the data captured on the POLine when the INTranCost record is created, and LineNbr is a key field and therefore never updated after it is set. I need to be sure that I have enough data to make the connection back, and the primary key links me back to the INTran easily. That subsequently gets back to the POReceiptLine to the POLine where the data is maintained that needs the "current value" to be captured when the transaction is posted. Since I need to update the DAC Extension, I need to use an event that will allow an existing DAC.Update to apply my values. Therefore, I added an event handler on INTranCost_LineNbr_FieldUpdated since that value should not be "updated" after it is set initially.
Code that accomplished the task:
public class INReleaseProcess_Extension : PXGraphExtension<INReleaseProcess>
{
public override void Initialize()
{
base.Initialize();
}
protected virtual void _(Events.FieldUpdated<INTranCost.lineNbr> e)
{
INTranCost row = (INTranCost) e.Row;
INTran tran = PXSelect<INTran,
Where<INTran.docType, Equal<Required<INTran.docType>>,
And<INTran.refNbr, Equal<Required<INTran.refNbr>>,
And<INTran.lineNbr, Equal<Required<INTran.lineNbr>>
>>>>
.SelectSingleBound(Base, null, row.DocType, row.RefNbr, (int?) e.NewValue);
if (tran?.POReceiptType != null && tran?.POReceiptNbr != null)
{
PXResultset<POReceiptLine> Results = PXSelectJoin<POReceiptLine,
InnerJoin<POLine, On<POLine.orderType, Equal<POReceiptLine.pOType>,
And<POLine.orderNbr, Equal<POReceiptLine.pONbr>,
And<POLine.lineNbr, Equal<POReceiptLine.pOLineNbr>>>>,
InnerJoin<POOrder, On<POOrder.orderType, Equal<POLine.orderType>,
And<POOrder.orderNbr, Equal<POLine.orderNbr>>>>>,
Where<POReceiptLine.receiptType, Equal<Required<POReceiptLine.receiptType>>,
And<POReceiptLine.receiptNbr, Equal<Required<POReceiptLine.receiptNbr>>,
And<POReceiptLine.lineNbr, Equal<Required<POReceiptLine.lineNbr>>>>>>.
SelectSingleBound(Base, null, tran.POReceiptType, tran.POReceiptNbr, tran.POReceiptLineNbr);
if (Results != null)
{
foreach (PXResult<POReceiptLine, POLine, POOrder> result in Results)
{
POReceiptLine receipt = result;
POLine line = result;
POOrder order = result;
POLineExt pOLineExt = PXCache<POLine>.GetExtension<POLineExt>(line);
INTranCostExt iNTranCostExt = PXCache<INTranCost>.GetExtension<INTranCostExt>(row);
if (pOLineExt != null && iNTranCostExt != null)
{
Base.Caches[typeof(INTranCost)].SetValueExt<INTranCostExt.usrField>(row, pOLineExt.UsrField);
}
}
}
}
}
}
I have read an FAQ about using Redemption from multiple threads, but it is not clear to me what is "creatable Redemption object"?
We are using Redemption to get proper sent date and internet message id from sent items.
I initialize a global object from the main thread and set MAPISESSION from main thread also, this way:
public void Init()
{
_rdoSession.Logon();
_rdoSession.MAPIOBJECT = ThisAddIn.Instance.Session.MAPIOBJECT;
_storeId = ThisAddIn.Instance.Session.DefaultStore.StoreID;
}
Then I get items that way:
public RDOMail GetEmailFromServer(string entryID, string storeID = null)
{
try
{
if (storeID == null)
{
storeID = _storeId;
}
return _rdoSession.GetMessageFromID(entryID, storeID, MAPI_NO_CACHE | MAPI_BEST_ACCESS);
}
catch (Exception e)
{
Log.Error(e, "Error getting message from server with RDO");
}
return null;
}
public Task<RDOMail> GetEmailFromServerAsync(string entryID, string storeID = null)
{
return Task.Run(() => GetEmailFromServer(entryID, storeID));
}
It seems to work, but does it use shared session in this case?
And as I understand RDOMail is a COM object that gets passed to another thread (and cleared there with Marshal.ReleaseComObject). Is that ok or we better do everything in a thread where we get it?
And maybe there is an easier way to get internet message id and sent date from the server? Does it download whole e-mail (with attachments) in our implementation or not?
Firstly, there is no reason to call Logon and set the MAPIOBJECT property. Just set the MAPIOBJECT properly.
Secondly, since your code is running in a COM addin, there is no need to create a global RDOSession (unless you are really using it) - Outlook initializes the MAPI system for you anyway on the main thread.
Thirdly, the code running in a separate thread (Task.Run), needs its own RDOSession to initialize MAPI on that thread. For that, you will need to store ThisAddIn.Instance.Session.MAPIOBJECT in a separate variable in your Init() method called on the main thread. The code running in a separate thread will then need to create its own RDOSession and set is MAPIOBJECT property to a variable set on the main thread - this way you will not be marshalling any OOM objects between threads (which Outlook really does no like).
I tried to customize the queries executed by Orchard.ContentManagement.DefaultContentManager but the following peace of code *1 render my efforts useless:
class DefaultContentManager
{
...
public virtual ContentItem Get(int id, VersionOptions options, QueryHints hints) {
...
// implemention of the query comes here
...
*1 -> // no record means content item is not in db
if (versionRecord == null) {
// check in memory
var record = _contentItemRepository.Get(id);
if (record == null) {
return null;
}
versionRecord = GetVersionRecord(options, record);
if (versionRecord == null) {
return null;
}
}
The query is executed correctly and it does not return any data (which was my goal) but afterwards a second attempt *1 is executed to still get the content item.
Why is this part of code there? What is its purpose? Also why does the comment state check in memory and then the repository (DB table) is queried.
It's already been verified at this point that the item doesn't exist in the database, but it may have just been created from code during the same request. In that case, the nHibernate session has the item, but the database doesn't have it yet. The repository hits the session, not the db directly, so if it's there, it'll retrieve it, but that'll happen in memory.
I use a function to get access to a configuration document:
private Document lookupDoc(String key1) {
try {
Session sess = ExtLibUtil.getCurrentSession();
Database wDb = sess.getDatabase(sess.getServerName(), this.dbname1);
View wView = wDb.getView(this.viewname1);
Document wDoc = wView.getDocumentByKey(key1, true);
this.debug("Got a doc for key: [" + key1 + "]");
return wDoc;
} catch (NotesException ne) {
if (this.DispLookupErrors)
ne.printStackTrace();
this.lastErrorMsg = ne.text;
this.debug(this.lastErrorMsg, "error");
}
return null;
}
In another method I use this function to get the document:
Document wDoc = this.lookupDoc(key1);
if (wdoc != null) {
// do things with the document
wdoc.recycle();
}
Should I be recycling the Database and View objects when I recycle the Document object? Or should those be recycled before the function returns the Document?
The best practice is to recycle all Domino objects during the scope within which they are created. However, recycling any object automatically recycles all objects "beneath" it. Hence, in your example method, you can't recycle wDb, because that would cause wDoc to be recycled as well, so you'd be returning a recycled Document handle.
So if you want to make sure that you're not leaking memory, it's best to recycle objects in reverse order (e.g., document first, then view, then database). This tends to require structuring your methods such that you do whatever you need to/with a Domino object inside whatever method obtains the handle on it.
For instance, I'm assuming the reason you defined a method to get a configuration document is so that you can pull the value of configuration settings from it. So, instead of a method to return the document, perhaps it would be better to define a method to return an item value:
private Object lookupItemValue(String configKey, itemName) {
Object result = null;
Database wDb = null;
View wView = null;
Document wDoc = null;
try {
Session sess = ExtLibUtil.getCurrentSession();
wDb = sess.getDatabase(sess.getServerName(), this.dbname1);
wView = wDb.getView(this.viewname1);
wDoc = wView.getDocumentByKey(configKey, true);
this.debug("Got a doc for key: [" + configKey + "]");
result = wDoc.getItemValue(itemName);
} catch (NotesException ne) {
if (this.DispLookupErrors)
ne.printStackTrace();
this.lastErrorMsg = ne.text;
this.debug(this.lastErrorMsg, "error");
} finally {
incinerate(wDoc, wView, wDb);
}
return result;
}
There are a few things about the above that merit an explanation:
Normally in Java, we declare variables at first use, not Table of Contents style. But with Domino objects, it's best to revert to TOC so that, whether or not an exception was thrown, we can try to recycle them when we're done... hence the use of finally.
The return Object (which should be an item value, not the document itself) is also declared in the TOC, so we can return that Object at the end of the method - again, whether or not an exception was encountered (if there was an exception, presumably it will still be null).
This example calls a utility method that allows us to pass all Domino objects to a single method call for recycling.
Here's the code of that utility method:
private void incinerate(Object... dominoObjects) {
for (Object dominoObject : dominoObjects) {
if (null != dominoObject) {
if (dominoObject instanceof Base) {
try {
((Base)dominoObject).recycle();
} catch (NotesException recycleSucks) {
// optionally log exception
}
}
}
}
}
It's private, as I'm assuming you'll just define it in the same bean, but lately I tend to define this as a public static method of a Util class, allowing me to follow this same pattern from pretty much anywhere.
One final note: if you'll be retrieving numerous item values from a config document, obviously it would be expensive to establish a new database, view, and document handle for every item value you want to return. So I'd recommend overriding this method to accept a List<String> (or String[ ]) of item names and return a Map<String, Object> of the resulting values. That way you can establish a single handle for the database, view, and document, retrieve all the values you need, then recycle the Domino objects before actually making use of the item values returned.
Here's an idea i'm experimenting with. Tim's answer is excellent however for me I really needed the document for other purposes so I've tried this..
Document doc = null;
View view = null;
try {
Database database = ExtLibUtil.getCurrentSessionAsSigner().getCurrentDatabase();
view = database.getView("LocationsByLocationCode");
doc = view.getDocumentByKey(code, true);
//need to get this via the db object directly so we can safely recycle the view
String id = doc.getNoteID();
doc.recycle();
doc = database.getDocumentByID(id);
} catch (Exception e) {
log.error(e);
} finally {
JSFUtils.incinerate(view);
}
return doc;
You then have to make sure you're recycling the doc object safely in whatever method calls this one
I have some temporary documents which exist for a while as config docs then no longer needed, so get deleted. This is kind of enforced by an existing Notes client application: they have to exist to keep that happy.
I've written a class which has a HashMap of Java Date, String and Doubles with the item name as the key. So now I have a serializable representation of the document, plus the original doc noteID so that it can be found quickly and amended/deleted when it's not needed anymore.
That means the config doc can be collected, a standard routine creates a map of all items for the Java representation, taking into account the item type. The doc object can then be recycled right away.
The return object is the Java class representation of the document, with getValue(String name) and setValue(String name, val) where val can be Double String or Date. NB: this structure has no need for rich text or attachments, so it's kept to simple field values.
It works well although if the config doc has lots of Items, it could mean holding a lot of info in memory unnecessarily. Not so in my particular case though.
The point is: the Java object is now serializable so it can remain in memory, and as Tim's brilliant reply suggests, the document can be recycled right away.
Here's another one for releasing objects created by reflection:
We're working with a reporting tool (Active Reports 6) which creates a dll for each report.
We have lots of clients that use similar but still unique reports.
Reports are read through a web interface.
We run multiple sites, one for each client.
Our choices are:
1) Put all the reports in one big project which will be called by all the sites.
Cost: It will need to be recompiled every time we make a small change to any one report, potentially creating problems for all sites.
2) Create a whole bunch of similar little projects, with one for each site - let's say for sake of space that this creates problems, too.
3) Create a "Report Factory" which will use reflection to wire-up report dlls as needed.
We chose "3".
Problem: The final product works fine except for one thing: It won't release the report dll when done.
There is not currently a problem with the operation within a test environment, but if you try to do anything in the folder with the report dlls, you get the following error message: "This action can't be completed because the folder or a file in it is open in another program"
After research on this site and others, we realized that we needed an AppDomain for each call which can be cleanly unloaded.
After still having problems, we realized that the AppDomainSetup object needed to have a setting that allowed it to optimize for multiple users (LoaderOptimization.MultiDomain)
That didn't work.
Unfortunately, the base object (Active 6 report) can not be serialized, so we can't make a deep copy and chuck the original object.
After doing all of this, we're still experiencing problems.
Here is the code (C#):
private object WireUpReport(ReportArgs args)
{
//The parameter 'args' is a custom type (ReportArgs) which merely contains a
name/value pair collection.
object myReport = null;
string sPath = String.Empty;
string sFriendlyName = String.Empty;
sFriendlyName = System.Guid.NewGuid().ToString();
Assembly asmReport = null;
AppDomainSetup ads = null;
AppDomain adWireUp = null;
ConstructorInfo ci = null;
Type myReportType = null;
Type[] parametypes = null;
object[] paramarray = null;
object retObject = null;
try
{
//Get Report Object
sPath = GetWireUpPath(args); //Gets the path to the required dll; kept in a config file
//This parameter is used in an overloaded constructor further down
ads = new AppDomainSetup();
ads.ApplicationBase = Path.GetDirectoryName(sPath);
ads.LoaderOptimization = LoaderOptimization.MultiDomain;
adWireUp = AppDomain.CreateDomain(sFriendlyName, AppDomain.CurrentDomain.Evidence, ads);
asmReport = adWireUp.GetAssemblies()[0];
asmReport = Assembly.LoadFrom(sPath);
//Create parameters for wireup
myReportType = asmReport.GetExportedTypes()[0];
parametypes = new Type[1];
parametypes[0] = typeof(ReportArgs);
ci = myReportType.GetConstructor(parametypes);
paramarray = new object[1];
paramarray[0] = args;
//Instantiate object
myReport = ci.Invoke(paramarray);
return myReport;
}
catch (Exception ex)
{
throw ex;
}
finally
{
//Make sure Assembly object is released.
if (adWireUp != null)
{
AppDomain.Unload(adWireUp);
}
if (asmReport != null)
{
asmReport = null;
}
if (ads != null)
{
ads = null;
}
if (adWireUp != null)
{
adWireUp = null;
}
if (ci != null)
{
ci = null;
}
if (myReportType != null)
{
myReportType = null;
}
if (parametypes != null)
{
parametypes = null;
}
if (paramarray != null)
{
paramarray = null;
}
}
}
The object which is returned from this code is cast as type ActiveReports and then passed around our application.
Any help would be deeply appreciated. Thanks
Your code looks like you are seriously misunderstanding how to interact with a separate AppDomain.
Think of communicating with an AppDomain like talking to someone who's currently in another country. You know where they are, but you can't just walk over and talk to them. If you want them to do something for you, you have to open up a line of communication and tell them what you need.
The way that you open that line of communication is by defining a proxy object that can be created inside the other AppDomain and then cross the boundary back to your current AppDomain. Being able to cross the boundary requires that your object either be marked as [Serializable] or inherit from MarshalByRefObject. Because we actually want to talk to a reference in the other AppDomain and not just have a copy of it, we need the proxy to do the latter.
private class CrossDomainQuery : MarshalByRefObject
{
public void LoadDataFromAssembly(string assemblyPath)
{
var assembly = Assembly.LoadFrom(assemblyPath);
//TODO: Do something with your assembly
}
}
There is a method on the AppDomain called CreateInstanceAndUnwrap() that will create an instance of that communication object inside the other AppDomain and then hand you back a __TransparentProxy object that can be cast to the proxy type.
var crossDomainQuery = (CrossDomainQuery)adWireUp.CreateInstanceAndUnwrap(
typeof(CrossDomainQuery).Assembly.FullName,
typeof(CrossDomainQuery).FullName);
Once you have that proxy object, you can call methods on it and they will be invoked in the other AppDomain.
crossDomainQuery.LoadDataFromAssembly(assemblyPath);
So how is this different from what your current example code is doing?
Your current code does not actually execute anything useful inside the other AppDomain.
adWireUp = AppDomain.CreateDomain(sFriendlyName, AppDomain.CurrentDomain.Evidence, ads);
asmReport = adWireUp.GetAssemblies()[0];
asmReport = Assembly.LoadFrom(sPath);
This creates a new AppDomain, but then it loads all of the assemblies from that AppDomain into your current AppDomain. Additionally, it explicitly loads your report assembly into your current AppDomain.
Creating an AppDomain and calling methods on it doesn't mean that your code is executing inside of it any more than reading about another country means that you're now talking to someone inside it.
Even if you do create a proxy object and execute code inside that other AppDomain, there are a few things to be aware of.
1) Both AppDomains must be able to see the type used for the proxy, and you may have to handle AssemblyResolve events for either AppDomain manually (at least temporarily) to help resolve that.
2) AppDomains are fairly expensive to create. Generally, they are not used in situations where you need to spin something up really quickly, take some action and disappear. You should plan on either keeping them around as long as you can or be prepared to take the performance hit on every call.
3) You've said that the report type that you're instantiating is not serializable, and being able to serialize the object is a requirement for passing that type back from the other AppDomain. Defining a serializable class that can transport relevant data across the boundary and using that to pass the report data might be an option, but you'll have to determine if that works for your particular situation.
Also, as an aside, unless you have logic that depends on variables being set to null, setting everything to null in your finally does nothing useful and complicates your code.