/* * Copyright � 2008, 2010, Oracle and/or its affiliates. All rights reserved */ package com.sun.lwuit.browser; import com.sun.lwuit.io.Storage; import java.io.ByteArrayInputStream; import java.util.Hashtable; import java.util.Vector; /** * Handles saving of persistent data to the RMS. * This includes cookies, form data, history (visited links) and images/documents cache. * * The Hashtable format used for the cookies and form data is a Hashtable with a String key (representing cookie's domain or form action URL). * The value of the main Hastable is another Hashtable consisting of a String key (Cookie/Field name) and a String value (Cookie/Field value) * * For the history data the format is similar, but the value of the main hashtable is a vector holding strings of the visited links * * As for the cache, unlike all others the actual data is not saved in the memory 9due to its size). * An index containing all relevant links is kept in the memory in a simple vector (which is serialized to a separate record store). * The actual data is in a different record store where each record contains only the byte array representing the resource. * The record number of the index record store and the data record store match. * * NOTE: This class is not a scalabale implementation and can have some problems with memory or RMS sizes. * This is why the RMS can be disabled via the JAD properties - see RMS_ENABLED below. * * @author Ofir Leitner */ public class BrowserStorage { /** * Cookies */ public static final int TYPE_COOKIES = 0; /** * Form data */ public static final int TYPE_FORM_DATA = 1; /** * History */ public static final int TYPE_HISTORY = 2; /** * Cache */ public static final int TYPE_CACHE = 3; static Vector cacheIndex; static String CACHE_RMS_INDEX_NAME = "cacheidx"; static String CACHE_RMS_NAME = "cache"; /** * The names of the record stores */ static String[] RMS_NAMES = {"cookies","formdata","history",CACHE_RMS_NAME}; /** * Inidcating which data types to store to the RMS. * This can be modified via the JAD property rms_* (where * is one of the RMS_NAMES) */ static boolean[] RMS_ENABLED = {true,true,true,true}; static Hashtable[] data = new Hashtable[3]; /** * Returns a hashtable with all the cookies stored in the RMS * * @return a hashtable with all the cookies stored in the RMS */ public static Hashtable getCookies() { return getRMSData(TYPE_COOKIES); } /** * Returns a hashtable with all the form data stored in the RMS * * @return a hashtable with all the form data stored in the RMS */ public static Hashtable getFormData() { return getRMSData(TYPE_FORM_DATA); } /** * Commits the cookies into the RMS. Should be called when the midlet terminates. */ public static void commitCookies() { commitDataToRMS(TYPE_COOKIES); } /** * Commits the form data into the RMS. Should be called when the midlet terminates. */ public static void commitFormData() { commitDataToRMS(TYPE_FORM_DATA); } /** * Adds the given cookie to be stored in the RMS (Actual saving is done on commitCookies) * * @param domain The cookie's domain * @param name The cookie's name * @param value The cookie's value */ public static void addCookie(String domain,String name,String value) { addDataRecord(TYPE_COOKIES, domain, name, value); } /** * Adds the given form data record to be stored in the RMS (Actual saving is done on commitFormData) * * @param action The form's action URL * @param id The field's name * @param value The field's value */ public static void addFormData(String action,String id,String value) { addDataRecord(TYPE_FORM_DATA, action, id, value); } /** * Clears all persistent cookies data. */ public static void clearCookies() { clear(TYPE_COOKIES); } /** * Clears all persistent form data. */ public static void clearFormData() { clear(TYPE_FORM_DATA); } /** * Clears all persistent history data, and returns the new and empty Hashtable * (Since unlike cookies and form data, the browser does not wokr on a copy, but on the actual Hashtable written to the RMS) */ public static Hashtable clearHistory() { if (!RMS_ENABLED[TYPE_HISTORY]) { return new Hashtable(); } data[TYPE_HISTORY]=new Hashtable(); return data[TYPE_HISTORY]; } /** * Clears the files (html/images) cache on the RMS */ public static void clearCache() { Storage.getInstance().deleteStorageFile(CACHE_RMS_NAME); Storage.getInstance().deleteStorageFile(CACHE_RMS_INDEX_NAME); cacheIndex=null; } /** * Loads the index of the cache which includes all the saved resource names (Without their data) */ private static void loadCacheIndex() { if (!RMS_ENABLED[TYPE_CACHE]) { return; } cacheIndex = (Vector)Storage.getInstance().readObject(CACHE_RMS_INDEX_NAME); } /** * Returns a resource (a stream to an image or HTML document) from the cache or null if none exists * * @param url The URL to search for * @return a resource (a stream to an image or HTML document) from the cache or null if none exists */ public static ByteArrayInputStream getResourcefromCache(String url) { if (!RMS_ENABLED[TYPE_CACHE]) { return null; } if (cacheIndex==null) { loadCacheIndex(); if (cacheIndex==null) { //couldn't open indexes return null; } } int index=cacheIndex.indexOf(url); if (index==-1) { //Image not in cache //System.out.println("Resource not found in cache - "+url); return null; } return new ByteArrayInputStream((byte[])Storage.getInstance().readObject("img" + index)); } /** * Adds the given resource (image/HTML) into the cache * * @param url The URL this resource should be linked to (For future cache search) * @param buf The resource buffer * @param updateIfExists when true this will override current cache value for this url, when false if it exists the new value will be ignored */ public static void addResourceToCache(String url,byte[] buf,boolean updateIfExists) { if (!RMS_ENABLED[TYPE_CACHE]) { return; } if (cacheIndex==null) { loadCacheIndex(); if (cacheIndex==null) { //couldn't open indexes return; } } int index=cacheIndex.indexOf(url); if ((!updateIfExists) && (index!=-1)) { //System.out.println("Image already exists - ignoring "+url); return; } if(index == -1) { index = 0; } Storage.getInstance().writeObject("img" + index, buf); cacheIndex.addElement(url); Storage.getInstance().writeObject(CACHE_RMS_INDEX_NAME, cacheIndex); } /** * Returns a Hashtable with the history data from the RMS. * The returned Hashtable is the same table that is stored in the Storage class, and thus all elements added to that table will be automatically * in the Storage hashtable and will be written on commitHistory * * @return A Hashtable with the history data */ public static Hashtable getHistory() { if (!RMS_ENABLED[TYPE_HISTORY]) { return new Hashtable(); } data[TYPE_HISTORY]=new Hashtable(); Hashtable h = (Hashtable)Storage.getInstance().readObject(RMS_NAMES[TYPE_HISTORY]); if(h == null) { return new Hashtable(); } return h; } public static void addHistory(String domain,String url) { if (!RMS_ENABLED[TYPE_HISTORY]) { return; } Vector urlData=(Vector)data[TYPE_HISTORY].get(domain); if (urlData==null) { urlData=new Vector(); data[TYPE_HISTORY].put(domain,urlData); } urlData.addElement(url); } /** * Commits the history data to the RMS */ public static void commitHistory() { if (!RMS_ENABLED[TYPE_HISTORY]) { return; } if (data[TYPE_HISTORY]==null) { //System.out.println("History RMS not opened"); return; } Storage.getInstance().writeObject(RMS_NAMES[TYPE_HISTORY], data[TYPE_HISTORY]); } /** * Returns a Hashtable with the requested data from the RMS. * This both creates a hashtable used in this class, and creates another copy of the data that is returned. * The reason is that not all cookies should be saved (not all are persistent) so we have one copy for the session and another for saving. * * @param type One of TYPE_COOKIES or TYPE_FORM_DATA * @return A Hashtable with the requested data */ private static Hashtable getRMSData(int type) { if (!RMS_ENABLED[type]) { return new Hashtable(); } Hashtable h = (Hashtable)Storage.getInstance().readObject(RMS_NAMES[type]); if(h == null) { return new Hashtable(); } return h; } /** * Adds the given record to be saved in the RMS. Note that saving is not done immediately but only when commitDataToRMS is called * * @param type One of TYPE_COOKIES or TYPE_FORM_DATA * @param url The URL, either the domain of the cookie or the action URL of the form * @param id The cookie name or field name * @param value The cookie value or field value */ private static void addDataRecord(int type,String url,String id,String value) { if (!RMS_ENABLED[type]) { return; } Hashtable urlData=(Hashtable)data[type].get(url); if (urlData==null) { urlData=new Hashtable(); data[type].put(url,urlData); } urlData.put(id,value); } /** * Commits the data (either cookies or form data) to the RMS. This should be called when the midlet termiantes. * * @param type One of TYPE_COOKIES or TYPE_FORM_DATA */ private static void commitDataToRMS(int type) { if (!RMS_ENABLED[type]) { return; } if (data[type]==null) { //System.out.println("RMS was not opened"); return; } Storage.getInstance().writeObject(RMS_NAMES[type], data[type]); } /** * Clears the cookies/form data * * @param type One of TYPE_COOKIES or TYPE_FORM_DATA */ private static void clear(int type) { if (!RMS_ENABLED[type]) { return; } data[type]=new Hashtable(); } }