My program appears to work to my liking. However, when I compile it I get this message:
Note: Program.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
What can I do to identify the unsafe operations with -Xlint, or what in program is causing this message? I'm thinking it has something to do with my Node class..?
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import javax.swing.JOptionPane;
/**
* An application that reads from a file, enters/deletes in queue and writes output to the file
*/
public class Program {
/**
* Driver code to test class
*
* #param arguments
* Commandline arguments. 1st argument is input file and 2nd argument is output file
* #throws IOException
*/
public static void main(String[] arguments) throws IOException {
//Queue Object
MyQueue<String> queue= (new MyQueue<String>());
String name;
//reading file
read(queue,arguments[0]);
String[] array = { "Offer Person", "Poll Person", "Peek person","Display Queue", "Exit Program"};
int choice = 0;
// display loop
while (choice != array.length-1) {
choice = JOptionPane.showOptionDialog(null, // put in center of screen
"Press a Button", // message to user
"Queue(Line) of People", // title of window
JOptionPane.YES_NO_CANCEL_OPTION, // type of option
JOptionPane.QUESTION_MESSAGE, // type of message
null, // icon
array, // array of strings
array[array.length - 1]); // default choice (last one)
if(choice==0)
{
//inserting the new name in queue
name=JOptionPane.showInputDialog(null,"Enter Person's name","Input");
queue.offer(name);
}
else if(choice==1){
//Display and remove the name which is at front of line
JOptionPane.showMessageDialog(null, queue.poll() + " is next in line");
}
else if(choice==2){
//Display name which is at front of line
JOptionPane.showMessageDialog(null, queue.peek() + " is front of the line");
}
else if(choice==3){
//Dispay all the list
JOptionPane.showMessageDialog(null, queue.toString());
}
//JOptionPane.showMessageDialog(null, "Your pressed button #" + choice);
}
//calling writing function
write(queue, arguments[1]);
}// end of main()
/**
* Reads a file
* #param queue
* #param file_name name of file
*/
public static void read(QueueInterface<String> queue, String file_name) throws IOException{
try
{
String name;
//creating a buffer reader to read
BufferedReader br= new BufferedReader(new FileReader(file_name));
while((name=br.readLine()) != null){
//putting in the queue
queue.offer(name);
}
//closing buffer reader
br.close();
}
catch(Exception ex)
{
System.err.println(ex.getMessage());
}
}
/**
* Writes the contents of LinkedQueue to the output file at the ned of program
* #param queue QueueInterface methods
* #param file_name name of file
*/
public static void write(QueueInterface<String> queue, String file_name) throws IOException{
try
{
String name;
//creating a buffer writer to write
BufferedWriter bw= new BufferedWriter(new FileWriter(file_name));
while((name=queue.poll()) != null){
//writin in file
bw.write(name);
bw.newLine();
}
//closing buffer
bw.close();
}
catch(Exception ex)
{
System.err.println(ex.getMessage());
}
}
}// end of class
/**
* Interface to be implemented by LinkedQueue
*/
interface QueueInterface<String>
{
public boolean empty();
public boolean offer(String element);
public String poll();
public String peek();
}
class Node<String>
{
private String data;
private Node nextNode;
public Node(String dataObject, Node nextNodeObject)
{
this.data=dataObject;
this.nextNode=nextNodeObject;
}
/**
* Gets the next node
* #return next node
*/
public Node getNext()
{
return nextNode;
}
/**
* Sets the next node of the current node
* #param nextNodeObject next node to be set as next to the current node
*/
public void setNext(Node nextNodeObject)
{
nextNode=nextNodeObject;
}
/**
* Sets data of the current node
* #param dataObject data to be inserted in new node
*/
public void setData(String dataObject)
{
this.data=dataObject;
}
/**
* Gets data of the current node
* #return data of the node
*/
public String getData()
{
return this.data;
}
}
class LinkedQueue implements QueueInterface<String>
{
protected Node<String> lastNode=null;
LinkedQueue() {
}
/**
* Checks if the queue is empty
* #return true if empty, false if not empty
*/
public boolean empty() {
if(lastNode==null)
{
return true;
}
else
return false;
}
/**
* Inserts new node in the queue
* #param element data to be inserted in new node
* #return true on success
*/
public boolean offer(String element)
{
Node<String> newLastNode = new Node<String>(element,null);
//If the LinkedQueue is empty, add the node to the last and point next to itself
if(empty())
{
newLastNode.setNext(newLastNode);
}
else
{
// Adding to the front of queue and updating next of the last node
newLastNode.setNext(lastNode.getNext());
lastNode.setNext(newLastNode);
}
lastNode=newLastNode;
return true;
}
/**
* Removes the first node and returns it
* #return data at first node
*/
public String poll()
{
// If queue is empty then return null
if(empty())
return null;
else
{
Node<String> frontNode = lastNode.getNext();
//Check if there will be no node left after polling this one
if (frontNode == lastNode)
{
lastNode = null;
}
else //Remove the first node and update next of the last node
{
lastNode.setNext(frontNode.getNext());
}
return frontNode.getData();
}
}
/**
* Returns data of the first node without removing it from the queue
* #return data at first node
*/
public String peek()
{
if (empty())
{
return null;
}
else
{
Node<String> frontNode = lastNode.getNext();
return frontNode.getData();
}
}
}
class MyQueue<String> extends LinkedQueue{
/**
* Constructor
*
*/
public MyQueue()
{
super();
}
/**
* Returns a string representation of the object
*
* #return a name on different lines
*/
public java.lang.String toString()
{
// create a variable to return
java.lang.String toReturn = "";
// Traversing the list
Node<String> frontNode = lastNode.getNext();
if(empty()) //If queue is empty
return "";
else if(frontNode==lastNode) //If only one elemtn
{
return frontNode.getData().toString();
}
else //More than one element in the queue
{
while(frontNode != lastNode)
{
toReturn=toReturn+frontNode.getData()+"\n";
frontNode=frontNode.getNext();
}
toReturn= toReturn+frontNode.getData()+"\n"; //Appending data of last node because it will be missed in the loop
}
return toReturn;
}
}
If you're compiling on the command line (i.e. javac Program.java), you just have to add the -Xlint:unchecked parameter to have it print out the warnings for you:
javac Program.java -Xlint:unchecked
That should point out the problem spots to you. But, as #DavidWallace mentioned in a comment, you should consider revising your use of generics to be a bit more clear -- that might reveal your issue to you even without using the -Xlint parameter.
If your classes should really only deal with Strings, then you don't need to include a type parameter at all (right now, in your code, <String> is a type parameter, representing the type you pass in when you use the class -- it doesn't signify that it has to be java.lang.String -- that's why #DavidWallace suggested you use T instead). Here's a good tutorial if you want to brush up on how to use Generics.
Assuming that using "String" as the type parameter is not what causes the actual message (or even if it does), with a quick glance I see:
Node<String> frontNode = lastNode.getNext();
but getNext() returns Node instead of Node<String> (should be Node<T> once you fix the type parameter name).
In jGRASP you can add flags for various commands using "Settings" > "Compiler Settings" > "Workspace" (or "Project..." or "File"). Just add "-Xlint" for "Flags or Args" for the "Compile" command for the Java language.
in jGrasp, the option is under the Settings menu item. Go to Settings-> File/Workspace/Project (pick whichever is applicable to you), and then on the Compiler tag, pick the Flags/Args tab, and then click the box next to Compile and enter -Xlint:unchecked into the text box. Click apply, and on your next build, you will get the messages on what was unchecked.
As others have pointed out, this warning is raised because you are using a raw form of a generic class rather than providing a type parameter to it. -Xlint:unchecked will show you where in your code you did that.
In the build.gradle add
allprojects {
gradle.projectsEvaluated {
tasks.withType(JavaCompile) {
options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
}
}
}
Related
Given a directory, my application traverses and loads .mdb MS Access dbs using the Jackcess API. Inside of each database, there is a table named GCMT_CMT_PROPERTIES with a column named cmt_data containing some text. I also have a Mapper object (which essentially resembles a Map<String,String> but allows duplicate keys) which I use as a dictionary when replacing a certain word from a string.
So for example if mapper contains fox -> dog then the sentence: "The fox jumps" becomes "The dog jumps".
The design I'm going with for this program is as follows:
1. Given a directory, traverse all subdirectories and load all .mdb files into a File[].
2. For each db file in File[], create a Task<Void> called "TaskMdbUpdater" and pass it the db file.
3. Dispatch and run each task as it is created (see 2. above).
TaskMdbUpdater is responsible for locating the appropriate table and column in the db file it was given and iteratively running a "find & replace" routine on each row of the table to detect words from the dictionary and replace them (as shown in example above) and finally updating that row before closing the db. Each instance of TaskMdbUpdater is a background thread with a Jackcess API DatabaseBuilder assigned to it, so it is able to manipulate the db.
In the current state, the code is running without throwing any exceptions whatsoever, however when I "manually" open the db through Access and inspect a given row, it appears to not have changed. I've tried to pin the source of the issue without any luck and would appreciate any support. If you need to see more code, let me know and I'll update my question accordingly.
public class TaskDatabaseTaskDispatcher extends Task<Void> {
private String parentDir;
private String dbFileFormat;
private Mapper mapper;
public TaskDatabaseTaskDispatcher(String parent, String dbFileFormat, Mapper mapper) {
this.parentDir = parent;
this.dbFileFormat = dbFileFormat;
this.mapper = mapper;
}
#Override
protected Void call() throws Exception {
File[] childDirs = getOnlyDirectories(getDirectoryChildFiles(new File(this.parentDir)));
DatabaseBuilder[] dbs = loadDatabasesInParent(childDirs);
Controller.dprint("TaskDatabaseTaskDispatcher", dbs.length + " databases were found in parent directory");
TaskMdbUpdater[] tasks = new TaskMdbUpdater[dbs.length];
Thread[] workers = new Thread[dbs.length];
for(int i=0; i<dbs.length; i++) {
// for each db, dispatch Task so a worker can update that db.
tasks[i] = new TaskMdbUpdater(dbs[i], mapper);
workers[i] = new Thread(tasks[i]);
workers[i].setDaemon(true);
workers[i].start();
}
return null;
}
private DatabaseBuilder[] loadDatabasesInParent(File[] childDirs) throws IOException {
DatabaseBuilder[] dbs = new DatabaseBuilder[childDirs.length];
// Traverse children and load dbs[]
for(int i=0; i<childDirs.length; i++) {
File dbFile = FileUtils.getFileInDirectory(
childDirs[i].getCanonicalFile(),
childDirs[i].getName() + this.dbFileFormat);
dbs[i] = new DatabaseBuilder(dbFile);
}
return dbs;
}
}
// StringUtils class, utility methods
public class StringUtils {
public static String findAndReplace(String str, Mapper mapper) {
String updatedStr = str;
for(int i=0; i<mapper.getMappings().size(); i++) {
updatedStr = updatedStr.replaceAll(mapper.getMappings().get(i).getKey(), mapper.getMappings().get(i).getValue());
}
return updatedStr;
}
}
// FileUtils class, utility methods:
public class FileUtils {
/**
* Returns only directories in given File[].
* #param list
* #return
*/
public static File[] getOnlyDirectories(File[] list) throws IOException, NullPointerException {
List<File> filteredList = new ArrayList<>();
for(int i=0; i<list.length; i++) {
if(list[i].isDirectory()) {
filteredList.add(list[i]);
}
}
File[] correctSizeFilteredList = new File[filteredList.size()];
for(int i=0; i<filteredList.size(); i++) {
correctSizeFilteredList[i] = filteredList.get(i);
}
return correctSizeFilteredList;
}
/**
* Returns a File[] containing all children under specified parent file.
* #param parent
* #return
*/
public static File[] getDirectoryChildFiles(File parent) {
return parent.listFiles();
}
}
public class Mapper {
private List<aMap> mappings;
public Mapper(List<aMap> mappings) {
this.mappings = mappings;
}
/**
* Returns mapping dictionary, typically used for extracting individual mappings.
* #return List of type aMap
*/
public List<aMap> getMappings() {
return mappings;
}
public void setMappings(List<aMap> mappings) {
this.mappings = mappings;
}
}
/**
* Represents a single String based K -> V mapping.
*/
public class aMap {
private String[] mapping; // [0] - key, [1] - value
public aMap(String[] mapping) {
this.mapping = mapping;
}
public String getKey() {
return mapping[0];
}
public String getValue() {
return mapping[1];
}
public String[] getMapping() {
return mapping;
}
public void setMapping(String[] mapping) {
this.mapping = mapping;
}
}
Update 1:
To verify my custom StringUtils.findAndReplace logic, I've performed the following unit test (in JUnit) which is passing:
#Test
public void simpleReplacementTest() {
// Construct a test mapper/dictionary
List<aMap> aMaps = new ArrayList<aMap>();
aMaps.add(new aMap(new String[] {"fox", "dog"})); // {K, V} = K -> V
Mapper mapper = new Mapper(aMaps);
// Perform replacement
String corpus = "The fox jumps";
String updatedCorpus = StringUtils.findAndReplace(corpus, mapper);
assertEquals("The dog jumps", updatedCorpus);
}
I'm including my TaskMdbUpdater class here separately with some logging code included, as I suspect point of failure lies somewhere in call:
/**
* Updates a given .mdb database according to specifications defined internally.
* #since 2.2
*/
public class TaskMdbUpdater extends Task<Void> {
private final String TABLE_NAME = "GCMT_CMT_PROPERTIES";
private final String COLUMN_NAME = "cmt_data";
private DatabaseBuilder dbPackage;
private Mapper mapper;
public TaskMdbUpdater(DatabaseBuilder dbPack, Mapper mapper) {
super();
this.dbPackage = dbPack;
this.mapper = mapper;
}
#Override
protected Void call() {
try {
// Controller.dprint("TaskMdbUpdater", "Worker: " + Thread.currentThread().getName() + " running");
// Open db and extract Table
Database db = this.dbPackage
.open();
Logger.debug("Opened database: {}", db.getFile().getName());
Table table = db.getTable(TABLE_NAME);
Logger.debug("Opening table: {}", table.getName());
Iterator<Row> tableRows = table.iterator();
// Controller.dprint("TaskMdbUpdater", "Updating database: " + db.getFile().getName());
int i=0;
try {
while( tableRows.hasNext() ) {
// Row is basically a<code> Map<Column_Name, Value> </code>
Row cRow = tableRows.next();
Logger.trace("Current row: {}", cRow);
// Controller.dprint(Thread.currentThread().getName(), "Database name: " + db.getFile().getName());
// Controller.dprint("TaskMdbUpdater", "existing row: " + cRow.toString());
String str = cRow.getString(COLUMN_NAME);
Logger.trace("Row {} column field contents (before find/replace): {}", i, str);
String newStr = performFindAndReplaceOnString(str);
Logger.trace("Row {} column field contents (after find/replace): {}", i, newStr);
cRow.put(COLUMN_NAME, newStr);
Logger.debug("Updating field in row {}", i);
Row newRow = table.updateRow(cRow); // <code>updateRow</code> returns the new, updated row. Ignoring this.
Logger.debug("Calling updateRow on table with modified row");
// Controller.dprint("TaskMdbUpdater", "new row: " + newRow.toString());
i++;
Logger.trace("i = {}", i);
}
} catch(NoSuchElementException e) {
// e.printStackTrace();
Logger.error("Thread has iterated past number of rows in table", e);
}
Logger.info("Iterated through {} rows in table {}", i, table.getName());
db.close();
Logger.debug("Closing database: {}", db.getFile().getName());
} catch (Exception e) {
// e.printStackTrace();
Logger.error("An error occurred while attempting to update row value", e);
}
return null;
}
/**
* #see javafx.concurrent.Task#failed()
*/
#Override
protected void failed() {
super.failed();
Logger.error("Task failed");
}
#Override
protected void succeeded() {
Logger.debug("Task succeeded");
}
private String performFindAndReplaceOnString(String str) {
// Logger.trace("OLD: [" + str + "]");
String updatedStr = null;
for(int i=0; i<mapper.getMappings().size(); i++) {
// loop through all parameter names in mapper to search for in str.
updatedStr = findAndReplace(str, this.mapper);
}
// Logger.trace("NEW: [" + updatedStr + "]");
return updatedStr;
}
}
Here's a small exerept from my log. As you can see, it doesn't seem to do anything after opening the table which has left me a bit perplexed:
INFO (16-02-2017 17:27:59) [Thread-9] NAMEMAP.logic.TaskDatabaseTaskDispatcher.call(): Located the following directories under specified MOIS parent which contains an .mdb file:
[01_Parent_All_Safe_Test[ RV_DMS_0041RV_DMS_0001RV_DMS_0003RV_DMS_0005RV_DMS_0007RV_DMS_0012RV_DMS_0013RV_DMS_0014RV_DMS_0016RV_DMS_0017RV_DMS_0018RV_DMS_0020RV_DMS_0023RV_DMS_0025RV_DMS_0028RV_DMS_0029RV_DMS_0031RV_DMS_0033RV_DMS_0034RV_DMS_0035RV_DMS_0036RV_DMS_0038RV_DMS_0039RV_DMS_0040 ]]
...
DEBUG (16-02-2017 17:27:59) [Thread-9] NAMEMAP.logic.TaskDatabaseTaskDispatcher.call(): Created new task: NAMEMAP.logic.TaskMdbUpdater#4cfe46fe
DEBUG (16-02-2017 17:27:59) [Thread-9] NAMEMAP.logic.TaskDatabaseTaskDispatcher.call(): Created new worker: Thread[Thread-22,5,main]
DEBUG (16-02-2017 17:27:59) [Thread-9] NAMEMAP.logic.TaskDatabaseTaskDispatcher.call(): Set worker Thread[Thread-22,5,main] as daemon
DEBUG (16-02-2017 17:27:59) [Thread-9] NAMEMAP.logic.TaskDatabaseTaskDispatcher.call(): Dispatching worker: Thread[Thread-22,5,main]
...
DEBUG (16-02-2017 17:28:00) [Thread-22] NAMEMAP.logic.TaskMdbUpdater.call(): Opened database: RV_DMS_0023.mdb
DEBUG (16-02-2017 17:28:00) [Thread-22] NAMEMAP.logic.TaskMdbUpdater.call(): Opening table: GCMT_CMT_PROPERTIES
After this point, there isn't any more entries entries in the log and the processor spikes at 100% load, remaining that way until I force kill the application. This could mean the program gets stuck in an infinite while loop - however if that were to be the case then shouldn't there be log entries in the file?
Update 2
Okay I've further narrowed the problem by printing log TRACE into stdio. It seems that my performFindAndReplaceOnString is super inefficient and it never gets past the first row of these dbs because it's just grinding away at the long string. Any suggestions on how I can efficiently perform a string replacement for this use case?
I have a pause mechanism for my events(Runnable) but I am not sure how to use/call it when I need it to be called.
In my class, I have a run method that submits an arraylist full of events to an executor. (see run() on GreenhouseControls). When the Events are submitted, Thread.sleep(eventTime); is called and afterwards the action() inside the event is called.
My program requirement needs a mechanism that pauses all the threads with a method(which will be called by a method when clicked) which can be resumed later on with another button.
Does anyone know how to implement this mechanism?
Here are the files:
GreenhouseControls.java
package control;
/**
* In this exercise we take a different design approach to GreenhouseControls
*
* Compiled/Tested using Eclipse Version: Luna Release (4.4.0)
* TME4 Folder is located in C:\COMP 308\
*/
import java.io.*;
import java.util.regex.*;
import java.util.logging.*;
import java.util.*;
import java.util.concurrent.*;
import java.lang.reflect.*;
import tme4.*;
/**
* GreenhouseControls consists of the Greenhouse's status and methods that
* control what actions will be performed inside the Greenhouse.
* #author Ray Masiclat
*
*/
public class GreenhouseControls extends Controller implements Serializable{
/**
* status is a Set of StatusPair objects which contains information about the
* GreenhouseControls' status. The Default States are initialized in the default constructor.
*/
private Set<StatusPair> status = new HashSet<StatusPair>();
private static String file = "src/error.log";
/**
* Default Constructor - initializes each state of the Greenhouse
*
*/
public GreenhouseControls(){
status.add(new StatusPair<String, Boolean>("light", false));
status.add(new StatusPair<String, Boolean>("water", false));
status.add(new StatusPair<String, Boolean>("fans", false));
status.add(new StatusPair<String, Boolean>("windowok", true));
status.add(new StatusPair<String, Boolean>("poweron", true));
status.add(new StatusPair<String, String>("thermostat", "Day"));
status.add(new StatusPair<String, Integer>("errorcode", 0));
status = Collections.synchronizedSet(status);
}
/**
* Prints out in the console how to use the program.
*/
public static void printUsage() {
System.out.println("Correct format: ");
System.out.println(" java GreenhouseControls -f <filename>, or");
System.out.println(" java GreenhouseControls -d dump.out");
}
/**
* Takes in an errorcode and returns the appropriate Fix that will fix the
* error that occured in the past.
* #param errorcode
* #return fix
*/
public Fixable getFixable(int errorcode){
Fixable fix = null;
switch(errorcode){
case 1:
fix = new FixWindow(this);
break;
case 2:
fix = new PowerOn(this);
break;
default:
System.out.println("No Error");
break;
}
return fix;
}
/**
* shutdown - method creates a Logger that creates an error log which consists of information about the
* reason of why the program was shut down. After logging the error information, it serializes
* the current state of the program in order for it to be fixed/restored in the future.
* #throws IOException
*/
public void shutdown() throws IOException{
System.err.println("System Shutting Down");
Logger logger = Logger.getLogger("ControllerException");
FileHandler fh;
try {
fh = new FileHandler("src/error.log");
logger.addHandler(fh);
SimpleFormatter formatter = new SimpleFormatter();
fh.setFormatter(formatter);
} catch (SecurityException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
//Check for errorcode using getError method.
int checkError = this.getError();
if(checkError == 1){
logger.info("Window Malfunction");
} else if (checkError == 2){
logger.info("Power Outage");
} else {
logger.info("No Error");
}
/**
* Serialize the current state and output it onto the src/ directory as dump.out
*/
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("src/dump.out"));
out.writeObject(this);
out.writeObject(getEventList());
out.close();
System.exit(0);
}
/**
* getStatus - returns the status Set which consists of information about the current state of the program.
* #return status
*/
public Set<StatusPair> getStatus(){
return status;
}
/**
* getError - Assigns an integer which will be used when logging the error.
* The errorNum is retrieved using the status Set
* #return errorNum
*/
public int getError(){
int errorNum = 0;
//Iterate through the status Set
for(StatusPair sp : status){
//Check for the name "errorcode" and initializes errorNum from its status
if(sp.getName().equals("errorcode")){
errorNum = (int) sp.getStatus();
}
}
return errorNum;
}
/**
* getEventList - returns a List of Events in the program.
* #return eventList
*/
public List<Event> getEventList(){
return eventList;
}
/**
* Create a method in GreenhouseControls called setVariable to
* handle updating to this collection. Use the synchronization feature
* in java to ensure that two Event classes are not trying to add to
* the structure at the same time.
* s - represents the name of the status
* o - represents the status it is going to be replaced with
* #param s
* #param o
*/
public void setVariable(String s, Object o){
for(StatusPair sp : getStatus()){
if(sp.getName().equals(s))
{
sp.setStatus(o);
}
}
}
/**
* addStatus - Adds a new status in the status Set and it is used
* if the program cannot find a name in the status Set.
* #param name
* #param status
*/
public void addStatus(String name, Object status){
getStatus().add(new StatusPair<String, Object>(name,status));
}
/**
* run - creates a thread array which will be used to run Events from
* the text file. Then a for loop is created to fill up the thread
* array with Events on each index. Then the thread is started once
* an index is initialized. After an event is added in to the thread
* array it is removed from the events list.
*/
public void run(){
ExecutorService exec = Executors.newCachedThreadPool();
if(eventList.size() == 1){
exec.submit(eventList.get(0));
eventList.remove(eventList.get(0));
} else {
for(Event e : eventList){
exec.submit(e);
}
}
exec.shutdown();
/*while(eventList.size() > 0){
for(int i = 0; i < eventList.size(); i++){
exec.submit(eventList.get(i));
}
}*/
/*Thread[] threads = new Thread[eventList.size()];
while(eventList.size() > 0)
for(int i = 0; i < eventList.size(); i++){
threads[i] = new Thread(eventList.get(i));
threads[i].start();
eventList.remove(i);
}*/
}
} ///:~
Event.java
/**
* Make Event implements Runnable so that each type of event provides
* its own timing. Each event file should be a class of its own. Change
* the rest of the design to simplify this model.
*
* Assignment: TME4
* #author Ray Masiclat
* #studentid 3231308
* #date July 27, 2015
*
*
* Compiled/Tested using Eclipse Version: Luna Release (4.4.0)
* TME4 Folder is located in C:\COMP 308\
*/
package tme4;
import java.io.*;
import java.util.logging.FileHandler;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import gui.*;
import control.*;
/**
* Event class that implements Runnable and Serializable
* #author Ray Masiclat
*
*/
public abstract class Event implements Runnable, Serializable{
/**
* eventTime - represents time it takes for the program to wait and then
* call its action method.
*/
protected long eventTime;
protected GreenhouseControls gcontrol;
protected boolean suspended = false;
/**
* Event class constructor which is used to initialize the Event's eventTime and
* GreenhouseControls object which is used for the Event to have access to its status
* variables.
* #param gc
* #param eventTime
*/
public Event(GreenhouseControls gc,long eventTime){
this.eventTime = eventTime;
this.gcontrol = gc;
}
/**
* getTime - returns the event's eventTime initialized from the constructor.
* #return eventTime
*/
public long getTime(){
return eventTime;
}
/**
* setTime - sets the eventTime
*/
public void setTime(long eventTime){
this.eventTime = eventTime;
}
/**
* run - Event class' run method is called when the Event is added in to the Thread
* and then "started". This method puts the Thread to sleep for however long the
* eventTime is. Afterwards, once it is done, it tries to run the Event's action
* method. If the action method throws an error, it is caught by the try-catch
* block which calls the GreenhouseControls object's shutdown which outputs an error.log
* file and serializes the current state of the GreenhouseControls object to a dump.out file.
*/
public void run(){
try {
synchronized(this){
while(suspended){
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Thread.sleep(eventTime);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
this.action();
} catch (ControllerException e) {
//Use shutdown to create error log
try {
gcontrol.shutdown();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
e.printStackTrace();
}
}
public void suspend(){
suspended = true;
}
public synchronized void resume(){
suspended = false;
notify();
}
/**
* abstract method used to call an Event's action method in the run method.
* #throws ControllerException
*/
public abstract void action() throws ControllerException;
} ///:~
(Pastebin: GreenhouseControls.java
and Event.java)
Something like this:
boolean stop = false;
You can use a suspending/stopping object, like so
public class Stopping {
public synchronized void checkStop() {
if (stopped) {
wait(); // wait until stop has been cleared
}
}
public synchronized void setStop(boolean val) {
stop = val;
if (val) {
notifyAll();
}
}
}
Create one of these and share it with all your threads, making sure to call checkStop() frequently. Alternatively, you could make the methods in this class static, and then your threads wouldn't need an object reference - they could just call Stopping.checkStop();
In my program, I am trying to get the last modified date for some items in my box folders, but I am unable to access that information.
According to the documentation for the box API and the javadoc for the java library, any of the following methods of BoxTypedObject should return the information I want:
getModifiedAt() should return a String in ISO 8601 format for the date.
getModifiedDate() should return a Date object for the date.
getValue("modified_at") should also return a String in ISO 8601 format for the date.
getExtraData("modified_at") is also a possible way, although I am not as sure of this one.
However, none of those methods has worked for me; they all just returh null.
The (vastly simplified) code that I am using to retrieve the dates is as follows, with one of the above methods substituted for the comment block:
private static void printAll(BoxFolder boxFolder){
for(BoxTypedObject file : boxFolder.getItemCollection().getEntries())
System.out.printf("[\"%1$s\" %2$s]%n",
file.getValue("name"), file./*[???]*/);
}
The other fields all return the correct values, only when I try to get the date does it fail on me.
How do I retrieve the modified date for the BoxTypedObjects?
EDIT: I have figured out one way to get it, but it is somewhat slow.
client.getFilesManager().getFile(file.getId(), null).getModifiedAt()
retrieves the date. I am still interested if there is a better way to do it, though.
Additional information (may or may not be relevant to the problem):
The authentication of the box client is handled by the following class:
import java.awt.Desktop;
import java.io.*;
import java.net.*;
import com.box.boxjavalibv2.BoxClient;
import com.box.boxjavalibv2.dao.BoxOAuthToken;
import com.box.boxjavalibv2.exceptions.*;
import com.box.boxjavalibv2.requests.requestobjects.BoxOAuthRequestObject;
import com.box.restclientv2.exceptions.BoxRestException;
/**
* This class handles the storage and use of authentication keys, to
* simplify the process of obtaining n authenticated client. This class
* will store refresh keys in a file, so that it can authenticate a client
* without needing for user intervention.
* <p>
* Copyright 2013 Mallick Mechanical, Inc.
*
* #author Anson Mansfield
*/
public class Authenticator {
/**
* Constructs an {#code Authenticator} for use obtaining
* authenticated {#Code BoxClient}s
*
* #param key The OAuth client id and application key.
* #param secret The OAuth client secret.
* #param authFile The file to be used for storing authentications
* for later use.
*/
public Authenticator(String key, String secret, File authFile){
this.key = key;
this.secret = secret;
this.authFile = authFile;
}
/**
* Constructs a new {#Code BoxClient} object, authenticates it,
* and returns it.
*/
public BoxClient getAuthenticatedClient(){
BoxClient client = new BoxClient(key,secret);
client.authenticate(getToken(client));
return client;
}
public final String host = "http://localhost";
public final int port = 4000;
public final String key, secret;
public final File authFile;
public final String url = "https://www.box.com/api/oauth2/authorize?response_type=code&client_id=";
/**
* Obtains a token that can be used to authenticate the box client,
* and stores its refresh value in a file, so it can be used later.
* #param client The client to obtain a token for.
* #return A token that can be used to authenticate the client, or
* {#code null} if one could not be obtained.
*/
private BoxOAuthToken getToken(BoxClient client){
BoxOAuthToken token = null;
try{
if((token = getOldToken(client)) != null) return token;
if((token = getNewToken(client)) != null) return token;
return token;
}finally{
writeNewToken(token);
}
}
/**
* Attempts to write a token's refresh token to a file.
* #param token The token whose refresh value is to be written.
*/
private void writeNewToken(BoxOAuthToken token) {
if(token != null)
try(BufferedWriter out = new BufferedWriter(new FileWriter(authFile))){
out.write(token.getRefreshToken());
}catch(IOException ex){
System.out.println("couldn't update new token");
}
}
/**
* Reads the last session's refresh token from a file and attempts
* to get a new authentication token with it.
* #param client The client for which the authentication token is for.
* #return The token obtained from the refresh, or {#code null} if one
* could not be obtained.
*/
private BoxOAuthToken getOldToken(BoxClient client) {
System.out.println("attempting to use old token");
BoxOAuthToken token = null;
try(BufferedReader in = new BufferedReader(new FileReader(authFile))){
token = client.getOAuthManager().refreshOAuth(
BoxOAuthRequestObject.refreshOAuthRequestObject(
in.readLine(), key, secret
));
System.out.println("refreshed old token");
}catch(IOException ex){
System.out.println("couldn't read old token");
} catch(BoxRestException | BoxServerException | AuthFatalFailureException ex){
System.out.println("couldn't refresh old token");
}
return token;
}
/**
* Connects to the OAuth server and gets a new authentication token.
* #param client The client to get a token for.
* #return The new token obtained from the server, or {#code null} if one could not be obtained.
*/
private BoxOAuthToken getNewToken(BoxClient client) {
System.out.println("attempting to get new token");
BoxOAuthToken token = null;
try {
Desktop.getDesktop().browse(java.net.URI.create(url + key));
token = client.getOAuthManager().createOAuth(
BoxOAuthRequestObject.createOAuthRequestObject(getCode(), key, secret, host + port)
);
} catch (BoxRestException | BoxServerException | AuthFatalFailureException | IOException ex) {
ex.printStackTrace();
return null;
}
return token;
}
/**
* This listens on the configured port for the code included in the callback.
* It also deploys a script on the receiving socket to close the browser tab navigating to it.
* #return The authentication code to generate a token with.
*/
private String getCode(){
try (ServerSocket serverSocket = new ServerSocket(port);
Socket socket = serverSocket.accept();
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream());){
out.println("<script type=\"text/javascript\">");
out.println( "window.open('', '_self', '');");
out.println( "window.close();");
out.println("</script>"); //Close the tab
while (true){
String code = "";
code = in.readLine ();
System.out.println(code);
String match = "code";
int loc = code.indexOf(match);
if( loc >0 ) {
int httpstr = code.indexOf("HTTP")-1;
code = code.substring(code.indexOf(match), httpstr);
String parts[] = code.split("=");
code=parts[1];
return code;
} else {
// It doesn't have a code
}
}
} catch (IOException | NullPointerException e) {
return "";
}
}
}
The actual class that will be getting the modified field (not yet finished, though):
import java.io.File;
import java.util.Scanner;
import com.box.boxjavalibv2.BoxClient;
import com.box.boxjavalibv2.dao.BoxFolder;
import com.box.boxjavalibv2.dao.BoxTypedObject;
/**
* Copyright 2013 Mallick Mechanical, Inc.
*
* #author Anson Mansfield
*/
public class BoxStuff {
static BoxClient client;
public void main(String ... args) throws Exception {
client = new Authenticator(args[0], args[1], new File(args[2]))
.getAuthenticatedClient();
userSelectFolder("Select the project folder");
}
private static BoxFolder userSelectFolder(String prompt) throws Exception{
Scanner kbd;
if(System.console()!=null)
kbd = new Scanner(System.console().reader());
else
kbd = new Scanner(System.in);
String line = "";
System.out.println();
System.out.println(prompt);
System.out.println("(leave prompt blank to select folder)");
BoxFolder current = client.getFoldersManager().getFolder("0", null);
printAll(current);
System.out.print("select>");
while(!(line = kbd.nextLine()).isEmpty()){
BoxFolder next = select(current, Integer.parseInt(line));
if(next != null) current = next;
printAll(current);
System.out.print("select>");
}
return current;
}
private static void printAll(BoxFolder boxFolder){
int idx=0;
System.out.println(" 0:[parent folder]");
for(BoxTypedObject file : boxFolder
.getItemCollection()
.getEntries()){
if(file.getType().equals("folder")){
System.out.printf("%1$3d:[%2$-32s %3$-6s %4$-9s]%n",
++idx, format((String) file.getValue("name"),30), file.getType(), file.getId());
} else {
System.out.printf(" [%1$-32s %2$-6s %3$-9s Edit:%4$s]%n",
format((String) file.getValue("name"),32), file.getType(), file.getId(), file.getExtraData("modified_at"));
}
}
}
private static String format(CharSequence source, int length){
StringBuilder b = new StringBuilder(length);
b.append('"');
if(source.length() > 30)
b.append(source.subSequence(0, 29)).append('~');
else
b.append(String.format("%1$-30s",source));
b.append('"');
return b.toString();
}
private static BoxFolder select(BoxFolder boxFolder, int i) throws Exception{
int idx=0;
for(BoxTypedObject file : boxFolder.getItemCollection().getEntries()){
if(file.getType().equals("folder") && ++idx == i){
return client.getFoldersManager().getFolder(file.getId(), null);
}
}
if(idx==0){
if(boxFolder.getParent() == null)
return client.getFoldersManager().getFolder("0", null);
else
return client.getFoldersManager().getFolder(boxFolder.getParent().getId(), null);
}else{
System.out.println("Selection is out of range!");
return boxFolder;
}
}
}
If anyone else wants to use these classes for something, just ask me. It is probably OK (they are a mechanical contractor, not a software company), I just need to clear it with my boss (This code does still belong to the company).
This is actually a little tricky. The api call to get folder items by default only return children items with some default fields, they don't include fields like modified_at. However if you supply extra fields parameters you should be able to get them.
Here is what you can do when using the getFolderItems method(this is also in the readme in github):
BoxFolderRequestObject requestObj =
BoxFolderRequestObject.getFolderItemsRequestObject(30, 20)
.addField(BoxFolder.FIELD_NAME)
.addField(BoxFolder.FIELD_MODIFIED_AT);
BoxCollection collection =
boxClient.getFoldersManager().getFolderItems(folderId, requestObj);
There is another tricky thing here though, after you supply these fields, the result children items will only contain the supplied fields(plus some basic fields), so make sure you add all the fields you want.
Here is one way I figured out to get it (that actually works):
client.getFilesManager().getFile(file.getId(), null).getModifiedAt()
This, however, is somewhat slow, so I would greatly appreciate it if someone else knows a faster solution.
In text fields I can check how many characters entered by the user, for example:
Field field;
field.addValidator(
new StringLengthValidator(
"WARNING MESSAGE HERE", 6, 20, false));
If the number is not within the range, then warning message is throw. For numeric fields I can check the type:
field.addValidator(new Validator() {
public boolean isValid(Object value) {
try {
float f = Float.parseFloat((String) value);
return true;
} catch (Exception e) {
field.getWindow().showNotification("WARNING MESSAGE HERE");
field.setValue(0);
return false;
}
}
public void validate(Object value)
throws InvalidValueException {
}
});
For the combobox I specify the following:
final ComboBox combobox = new ComboBox("...");
if("someProperty".equals(propertyId)) {
field = combobox;
}
field.setRequired(true);
field.setRequiredError("WARNING MESSAGE HERE");
If I leave it blank, then no warning displayed and form sent to the server. What validator needed for ComboBox?
I would be very grateful for the information. Thanks to all.
What you are looking for is the immediate callback to the server after the user changes anything.
// Fire value changes immediately when the field loses focus
combobox.setImmediate(true);
For that users don't have to wait to get a validation until they commit or do anything else what needs to interact with the server.
I suppose you should explicitly call validate() method:
/**
* Checks the validity of the Validatable by validating the field with all
* attached validators except when the field is empty. An empty field is
* invalid if it is required and valid otherwise.
*
* The "required" validation is a built-in validation feature. If the field
* is required, but empty, validation will throw an EmptyValueException with
* the error message set with setRequiredError().
*
* #see com.vaadin.data.Validatable#validate()
*/
public void validate() throws Validator.InvalidValueException {
if (isEmpty()) {
if (isRequired()) {
throw new Validator.EmptyValueException(requiredError);
} else {
return;
}
}
// If there is no validator, there can not be any errors
if (validators == null) {
return;
}
...
By default Form performs validation:
/**
* Checks the validity of the validatable.
*
* #see com.vaadin.data.Validatable#validate()
*/
#Override
public void validate() throws InvalidValueException {
super.validate();
for (final Iterator<Object> i = propertyIds.iterator(); i.hasNext();) {
(fields.get(i.next())).validate();
}
}
I'm doing below procedure for LinkedIn login but unfortunately while loading the LinkedIn for login window of "Grant Yokeapp access to your LinkedIn Account"
It's not showing anything and fires error.
I'm using version of LWuit-1.5 in Eclipse pulsar with S60 SDk 5th installed.
public class Login {
Form form = new Form();
String authorizeUrl = "";
public Form Login() {
Display.init(this);
HttpRequestHandler handler = new HttpRequestHandler();
HTMLComponent htmlC = new HTMLComponent(handler);
user = new LinkedInUser(Const.consumerKey, Const.consumerSecret);
user.fetchNewRequestToken();
if (user.requestToken != null) {
authorizeUrl = "https://www.linkedin.com/uas/oauth/authorize?oauth_token="
+ user.requestToken.getToken();
htmlC.setPage(authorizeUrl);
FlowLayout flow = new FlowLayout(Component.TOP);
form.setLayout(flow);
form.addComponent(htmlC);
}
return form;
}
}
and i'm calling this method in my MIDlet class startApp() in following way
Login login=new Login();
login.Login().show();
I'm getting following errors
Uncaught exception!
java.lang.NullPointerException
at com.sun.lwuit.html.CSSEngine.applyStyleToUIElement(Unknown Source)
at com.sun.lwuit.html.CSSEngine.applyStyle(Unknown Source)
at com.sun.lwuit.html.CSSEngine.checkSelector(Unknown Source)
at com.sun.lwuit.html.CSSEngine.applyCSS(Unknown Source)
at com.sun.lwuit.html.CSSEngine.applyCSS(Unknown Source)
at com.sun.lwuit.html.CSSEngine.applyCSS(Unknown Source)
at com.sun.lwuit.html.CSSEngine.applyCSS(Unknown Source)
at com.sun.lwuit.html.CSSEngine.applyCSS(Unknown Source)
at com.sun.lwuit.html.CSSEngine.applyCSS(Unknown Source)
at com.sun.lwuit.html.CSSEngine.applyCSS(Unknown Source)
at com.sun.lwuit.html.CSSEngine.applyCSS(Unknown Source)
at com.sun.lwuit.html.HTMLComponent.applyAllCSS(Unknown Source)
at com.sun.lwuit.html.ResourceThreadQueue.threadFinished(Unknown Source)
at com.sun.lwuit.html.ResourceThreadQueue$ResourceThread.streamReady(Unknown Source)
at com.sun.lwuit.html.ResourceThreadQueue$ResourceThread.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
-VM verbose connection exited
The HttpRequestHandler file code is
/*
* Copyright � 2008, 2010, Oracle and/or its affiliates. All rights reserved
*/
package com.yoke.symbian;
import com.sun.lwuit.html.DocumentInfo;
import com.sun.lwuit.html.DocumentRequestHandler;
import com.yoke.helper.Storage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import javax.microedition.io.Connector;
import javax.microedition.io.HttpConnection;
/**
* An implementation of DocumentRequestHandler that handles fetching HTML documents both from HTTP and from the JAR.
* This request handler takes care of cookies, redirects and handles both GET and POST requests
*
* #author Ofir Leitner
*/
public class HttpRequestHandler implements DocumentRequestHandler {
//Hashtable connections = new Hashtable();
/**
* A hastable containing all cookies - the table keys are domain names, while the value is another hashtbale containing a pair of cookie name and value.
*/
static Hashtable cookies = Storage.getCookies();
/**
* A hastable containing all history - the table keys are domain names, while the value is a vector containing the visited links.
*/
static Hashtable visitedLinks = Storage.getHistory();
/**
* If true will cache HTML pages, this also means that they will be buffered and read fully and only then passed to HTMLComponent - this can have memory implications.
* Also note that for the cached HTMLs to be written Storage.RMS_ENABLED[TYPE_CACHE] should be true
*/
static boolean CACHE_HTML=false;
/**
* If true will cache images, this also means that they will be buffered and read fully and only then passed to HTMLComponent - this can have memory implications.
* Also note that for the cached images to be written Storage.RMS_ENABLED[TYPE_CACHE] should be true
*/
static boolean CACHE_IMAGES=true;
/**
* If true will cache CSS files, this also means that they will be buffered and read fully and only then passed to HTMLComponent - this can have memory implications.
* Also note that for the cached CSS files to be written Storage.RMS_ENABLED[TYPE_CACHE] should be true
*/
static boolean CACHE_CSS=false;
/**
* Returns the domain string we use to identify visited link.
* Note that this may be different than the domain name returned by HttpConnection.getHost
*
* #param url The link URL
* #return The link's domain
*/
static String getDomainForLinks(String url) {
String domain=null;
if (url.startsWith("file:")) {
return "localhost"; // Just a common name to store local files under
}
int index=-1;
if (url.startsWith("http://")) {
index=7;
} else if (url.startsWith("https://")) {
index=8;
}
if (index!=-1) {
domain=url.substring(index);
index=domain.indexOf('/');
if (index!=-1) {
domain=domain.substring(0,index);
}
}
return domain;
}
/**
* {#inheritDoc}
*/
public InputStream resourceRequested(DocumentInfo docInfo) {
InputStream is=null;
String url=docInfo.getUrl();
String linkDomain=getDomainForLinks(url);
// Visited links
if (docInfo.getExpectedContentType()==DocumentInfo.TYPE_HTML) { // Only mark base documents as visited links
if (linkDomain!=null) {
Vector hostVisitedLinks=(Vector)visitedLinks.get(linkDomain);
if (hostVisitedLinks==null) {
hostVisitedLinks=new Vector();
visitedLinks.put(linkDomain,hostVisitedLinks);
}
if (!hostVisitedLinks.contains(url)) {
hostVisitedLinks.addElement(url);
Storage.addHistory(linkDomain, url);
}
} else {
System.out.println("Link domain null for "+url);
}
}
String params=docInfo.getParams();
if ((!docInfo.isPostRequest()) && (params !=null) && (!params.equals(""))) {
url=url+"?"+params;
}
// See if page/image is in the cache
// caching will be used only if there are no parameters and no cookies (Since if they are this is probably dynamic content)
boolean useCache=false;
if (((docInfo.getExpectedContentType()==DocumentInfo.TYPE_HTML) && (CACHE_HTML) && ((params==null) || (params.equals(""))) && (!cookiesExistForDomain(linkDomain) )) ||
((docInfo.getExpectedContentType()==DocumentInfo.TYPE_IMAGE) && (CACHE_IMAGES)) ||
((docInfo.getExpectedContentType()==DocumentInfo.TYPE_CSS) && (CACHE_CSS)))
{
useCache=true;
InputStream imageIS=Storage.getResourcefromCache(url);
if (imageIS!=null) {
return imageIS;
}
}
// Handle the file protocol
if (url.startsWith("file://")) {
return getFileStream(docInfo);
}
try {
HttpConnection hc = (HttpConnection)Connector.open(url);
String encoding=null;
if (docInfo.isPostRequest()) {
encoding="application/x-www-form-urlencoded";
}
if (!docInfo.getEncoding().equals(DocumentInfo.ENCODING_ISO)) {
encoding=docInfo.getEncoding();
}
//hc.setRequestProperty("Accept_Language","en-US");
//String domain=hc.getHost(); // sub.domain.com / sub.domain.co.il
String domain=linkDomain; // will return one of the following formats: sub.domain.com / sub.domain.co.il
sendCookies(domain, hc);
domain=domain.substring(domain.indexOf('.')); // .domain.com / .domain.co.il
if (domain.indexOf('.',1)!=-1) { // Make sure that we didn't get just .com - TODO - however note that if the domain was domain.co.il - it can be here .co.il
sendCookies(domain, hc);
}
if (encoding!=null) {
hc.setRequestProperty("Content-Type", encoding);
}
if (docInfo.isPostRequest()) {
hc.setRequestMethod(HttpConnection.POST);
if (params==null) {
params="";
}
byte[] paramBuf=params.getBytes();
hc.setRequestProperty("Content-Length", ""+paramBuf.length);
OutputStream os=hc.openOutputStream();
os.write(paramBuf);
os.close();
//os.flush(); // flush is said to be problematic in some devices, uncomment if it is necessary for your device
}
String contentTypeStr=hc.getHeaderField("content-type");
if (contentTypeStr!=null) {
contentTypeStr=contentTypeStr.toLowerCase();
if (docInfo.getExpectedContentType()==DocumentInfo.TYPE_HTML) { //We perform these checks only for text (i.e. main page), for images/css we just send what the server sends
and "hope for the best"
if (contentTypeStr!=null) {
if ((contentTypeStr.startsWith("text/")) || (contentTypeStr.startsWith("application/xhtml")) || (contentTypeStr.startsWith("application/vnd.wap"))) {
docInfo.setExpectedContentType(DocumentInfo.TYPE_HTML);
} else if (contentTypeStr.startsWith("image/")) {
docInfo.setExpectedContentType(DocumentInfo.TYPE_IMAGE);
hc.close();
return getStream("<img src=\""+url+"\">",null);
} else {
hc.close();
return getStream("Content type "+contentTypeStr+" is not supported.","Error");
}
}
}
if ((docInfo.getExpectedContentType()==DocumentInfo.TYPE_HTML) ||
(docInfo.getExpectedContentType()==DocumentInfo.TYPE_CSS)) { // Charset is relevant for HTML and CSS only
int charsetIndex = contentTypeStr.indexOf("charset=");
if (charsetIndex!=-1) {
String charset=contentTypeStr.substring(charsetIndex+8);
docInfo.setEncoding(charset.trim());
// if ((charset.startsWith("utf-8")) || (charset.startsWith("utf8"))) { //startwith to allow trailing white spaces
// docInfo.setEncoding(DocumentInfo.ENCODING_UTF8);
// }
}
}
}
int i=0;
while (hc.getHeaderFieldKey(i)!=null) {
if (hc.getHeaderFieldKey(i).equalsIgnoreCase("set-cookie")) {
addCookie(hc.getHeaderField(i), url);
}
i++;
}
int response=hc.getResponseCode();
if (response/100==3) { // 30x code is redirect
String newURL=hc.getHeaderField("Location");
if (newURL!=null) {
hc.close();
docInfo.setUrl(newURL);
if ((response==302) || (response==303)) { // The "302 Found" and "303 See Other" change the request method to GET
docInfo.setPostRequest(false);
docInfo.setParams(null); //reset params
}
return resourceRequested(docInfo);
}
}
is = hc.openInputStream();
if (useCache) {
byte[] buf=getBuffer(is);
Storage.addResourceToCache(url, buf,false);
ByteArrayInputStream bais=new ByteArrayInputStream(buf);
is.close();
hc.close(); //all the data is in the buffer
return bais;
}
} catch (SecurityException e) {
return getStream("Network access was disallowed for this session. Only local and cached pages can be viewed.<br><br> To browse external sites please exit the application and when asked for network access allow it.", "Security error");
} catch (IOException e) {
System.out.println("HttpRequestHandler->IOException: "+e.getMessage());
return getStream("The page could not be loaded due to an I/O error.", "Error");
} catch (IllegalArgumentException e) { // For malformed URL
System.out.println("HttpRequestHandler->IllegalArgumentException: "+e.getMessage());
return getStream("The requested URL is not valid.", "Malformed URL");
}
return is;
}
/**
* Checks if there are cookies stored on the client for the specified domain
*
* #param domain The domain to check for cookies
* #return true if cookies for the specified domain exists, false otherwise
*/
private boolean cookiesExistForDomain(String domain) {
Object obj=cookies.get(domain);
//System.out.println("Cookies for domain "+domain+": "+obj);
if (obj==null) {
int index=domain.indexOf('.');
if (index!=-1) {
domain=domain.substring(index); // .domain.com / .domain.co.il
if (domain.indexOf('.',1)!=-1) { // Make sure that we didn't get just .com - TODO - however note that if the domain was domain.co.il - it can be here .co.il
obj=cookies.get(domain);
//System.out.println("Cookies for domain "+domain+": "+obj);
}
}
}
return (obj!=null);
}
/**
* Sends the avaiable cookies for the given domain
*
* #param domain The cookies domain
* #param hc The HTTPConnection
* #throws IOException
*/
private void sendCookies(String domain,HttpConnection hc) throws IOException {
//System.out.println("Sending cookies for "+domain);
Hashtable hostCookies=(Hashtable)cookies.get(domain);
String cookieStr="";
if (hostCookies!=null) {
for (Enumeration e=hostCookies.keys();e.hasMoreElements();) {
String name = (String)e.nextElement();
String value = (String)hostCookies.get(name);
String cookie=name+"="+value;
if (cookieStr.length()!=0) {
cookieStr+="; ";
}
cookieStr+=cookie;
}
}
if (cookieStr.length()!=0) {
//System.out.println("Cookies for domain "+domain+": "+cookieStr);
hc.setRequestProperty("cookie", cookieStr);
}
}
/**
* Returns an Inputstream of the specified HTML text
*
* #param htmlText The text to get the stream from
* #param title The page's title
* #return an Inputstream of the specified HTML text
*/
private InputStream getStream(String htmlText,String title) {
String titleStr="";
if (title!=null) {
titleStr="<head><title>"+title+"</title></head>";
}
htmlText="<html>"+titleStr+"<body>"+htmlText+"</body></html>";
ByteArrayInputStream bais = new ByteArrayInputStream(htmlText.getBytes());
return bais;
}
/**
* Adds the given cookie to the cookie collection
*
* #param setCookie The cookie to add
* #param hc The HttpConnection
*/
private void addCookie(String setCookie,String url/*HttpConnection hc*/) {
//System.out.println("Adding cookie: "+setCookie);
String urlDomain=getDomainForLinks(url);
// Determine cookie domain
String domain=null;
int index=setCookie.indexOf("domain=");
if (index!=-1) {
domain=setCookie.substring(index+7);
index=domain.indexOf(';');
if (index!=-1) {
domain=domain.substring(0, index);
}
if (!urlDomain.endsWith(domain)) { //if (!hc.getHost().endsWith(domain)) {
System.out.println("Warning: Cookie tried to set to another domain");
domain=null;
}
}
if (domain==null) {
domain=urlDomain; //domain=hc.getHost();
}
// Check cookie expiry
boolean save=false;
index=setCookie.indexOf("expires=");
if (index!=-1) { // Cookies without the expires= property are valid only for the current session and as such are not saved to RMS
String expire=setCookie.substring(index+8);
index=expire.indexOf(';');
if (index!=-1) {
expire=expire.substring(0, index);
}
save=true;
}
// Get cookie name and value
index=setCookie.indexOf(';');
if (index!=-1) {
setCookie=setCookie.substring(0, index);
}
index=setCookie.indexOf('=');
String name=setCookie;
String value="";
if (index!=-1) {
name=setCookie.substring(0, index);
value=setCookie.substring(index+1);
}
Hashtable hostCookies=(Hashtable)cookies.get(domain);
if (hostCookies==null) {
hostCookies=new Hashtable();
cookies.put(domain,hostCookies);
}
hostCookies.put(name,value);
if (save) { // Note that we save all cookies with expiry specified, while not checking the specific expiry date
Storage.addCookie(domain, name, value);
}
}
/**
* This method is used when the requested document is a file in the JAR
*
* #param url The URL of the file
* #return An InputStream of the specified file
*/
private InputStream getFileStream(DocumentInfo docInfo) {
String url=docInfo.getUrl();
// If a from was submitted on a local file, just display the parameters
if ((docInfo.getParams()!=null) && (!docInfo.getParams().equals(""))) {
String method="GET";
if (docInfo.isPostRequest()) {
method="POST";
}
String params=docInfo.getParams();
String newParams="";
if (params!=null) {
for(int i=0;i<params.length();i++) {
char c=params.charAt(i);
if (c=='&') {
newParams+=", ";
} else {
newParams+=c;
}
}
}
return getStream("<h2>Form submitted locally.</h2><b>Method:</b> "+method+"<br><br><b>Parameters:</b><br>"+newParams+"<hr>Continue to local URL","Form Results");
}
url=url.substring(7); // Cut the file://
int hash=url.indexOf('#'); //trim anchors
if (hash!=-1) {
url=url.substring(0,hash);
}
int param=url.indexOf('?'); //trim parameters, not relvant for files
if (param!=-1) {
url=url.substring(0, param);
}
// Use the following commented segment for loading HTML files saved with the UTF8 header added by some utils - 0xEF, 0xBB, 0xBF
// This is a simple code to skip automatically 3 chars on a certain file suffix (.htm isntead of .html)
// A better solution is to detect these bytes, but that requires buffering of the stream (to "unread" if these are not the right chars)
/*
if (url.endsWith(".htm")) {
System.out.println("Notepad UTF - Skipping 3 chars");
docInfo.setEncoding(DocumentInfo.ENCODING_UTF8);
// If the UTF8 encoding string doesn't work on your device, try the following instead of the line above:
//docInfo.setEncoding("UTF-8");
InputStream is= getClass().getResourceAsStream(url);
try {
is.read();
is.read();
is.read();
return is;
} catch (IOException ex) {
ex.printStackTrace();
}
}
*/
return getClass().getResourceAsStream(url);
}
/**
* Reads an inputstream completely and places it into a buffer
*
* #param is The InputStream to read
* #return A buffer containing the stream's contents
* #throws IOException
*/
static byte[] getBuffer(InputStream is) throws IOException {
int chunk = 50000;
byte[] buf = new byte[chunk];
int i=0;
int b = is.read();
while (b!=-1) {
if (i>=buf.length) {
byte[] tempbuf=new byte[buf.length+chunk];
for (int j=0;j<buf.length;j++) {
tempbuf[j]=buf[j];
}
buf=tempbuf;
}
buf[i]=(byte)b;
i++;
b = is.read();
}
byte[] tempbuf=new byte[i];
for (int j=0;j<tempbuf.length;j++) {
tempbuf[j]=buf[j];
There are numerous OAuth related bugs in LWUIT which we fixed for Codename One. Unfortunately there are so many its hard for me to tell which one you are triggering, however you can look at our issue tracker to see if you can find something specific. We still have some that are open but OAuth works MUCH better.