Tuesday, February 13, 2024

Yet another reason I learned to hate, but not fear, Windows Batch Scripting

 Consider the following batch script, and save it as "test.bat":

@ECHO OFF
SETLOCAL
:step-1
SET "TEST=0"
Echo TEST before: %TEST%
:step-2
FOR /F "tokens=* USEBACKQ" %%F IN (`ECHO 1`) DO (
  REM step-3
  SET "TEST=%%F"
  ECHO F in loop: %%F
  ECHO TEST in loop: %TEST%
)
:step-4
ECHO TEST after: %TEST%


High-level explanation of each labeled step above,


1. The script sets variable TEST to the value 0.

2. A FOR loop iterates over the value(s) output from the statement "ECHO 1", which predictably yields the single value, "1". We define the placeholder %%F, a special variable to hold each value as we iterate the loop.

3. Inside our FOR loop, we do the following:
   a. Assign the current value of %%F to TEST
   b. Output the current value of %%F
   c. Output the current value of TEST

4. After our FOR loop completes, output the current value of TEST.



Pop-quiz:
What do you expect the output of this batch script to be?

Wednesday, October 6, 2021

Leveraging Java 17 with Micronaut

What does Java 17 contribute to microservice development in Micronaut? The Oracle GraalVM team shared this video with details and a demo leveraging the new features of Java 17 with Micronaut.



Monday, May 10, 2021

What is a Testable Unit?

Last week I had a conversation with my good friend, John Hubb, about the various philosophies of TDD (Test Driven Development). This harkened me back to the halcyon days of my ACCA/ARGON lectures at Concordia where I met James Coplien, and his various musings about Unit Testing, TDD, BDD (Behavior Driven Development) and Agile methodologies.

The stand-out observation for me was the question, "What is a testable unit?" Modern developers, especially in object-oriented languages, tend to think every public class is the testable unit. Coplien challenges this notion, to think about functional units of code, with externally verifiable expectations, are the unit, and around which quality unit tests will congregate. Think business expectations, e.g. "Given X, when Y, then Z," and not "When I invoke this public function with A, it should return B."


https://rbcs-us.com/documents/Why-Most-Unit-Testing-is-Waste.pdf

"Tests should be designed with great care. Business people, rather than programmers, should design most functional tests. Unit tests should be limited to those that can be held up against some 'third-party' success criteria."

-- James Coplien, Why Most Unit Testing is Waste

Thursday, November 14, 2013

Adventures in marshalling multiple Java class objects from a custom database query in Spring JPA Hibernate

This was my "Eureka!", run-around-and-high-five-everyone moment of the day.

THE SETUP

I am using Hibernate via Spring JPA to marshal two database tables to POJOs in my Java application. 99% of the time it is perfectly sufficient to use the Spring JPA standard call to EntityManager#createQuery() to retrieve database records and marshal them to the appropriate List<Class> collection.

In this particular example I have the following two classes:
@Entity
public class Library {
    @Id
    private String id;
    @Column
    private String name;
    @OneToMany(fetch=javax.persistence.FetchType.LAZY)
    @JoinColumn(name="id")
    private List<Book> books;
    ....
}
@Entity
public class Book {
    @Id
    private String bookId;
    private String name;
    private String libraryId;
    ....
}

Then in the corresponding HQL query you can simply write:
SELECT DISTINCT library
FROM Library library
LEFT JOIN FETCH library.books

And results from createQuery().getResultsList() would be returned as a convenient java object collection like so:
List<Library> libraries = entityManager.createQuery(hql).getResultsList();

THE DILEMMA

We use Sybase. Sybase is great, as long as your table statistics are up-to-date. So if you can imagine with me a world where statistics haven't been updated in over 6 months, and you have a scenario where explicitly specifying an index can mean the difference of tens of thousands of physical or logical reads. RE: PERFORMANCE.

But hibernate doesn't let you specify index hints in HQL, nor add them to the Query object after the fact. I also wanted to avoid modifying the existing entity models, so no cheating and changing fetch.LAZY to fetch.EAGER for a single use-case.

To restate the desired end result, I want to execute the following SyBase-specific SQL statement with index hints:
SELECT library.*, book.*
FROM Library library (index idx_library_pk)
LEFT JOIN Book book (index idx_book_library_id)
ON (library.Id = book.libraryId)
And end up with a List<Library> collection with the class member collection library.books prefetched, that is to say a list of Libraries with a populated list of Books from a single database query.

POTENTIAL SOLUTIONS... (that didn't work)

In most cases where an index hint is needed I would fall back to Hibernate's createNativeQuery() to send sybase-aware SQL with index hints to the database. For example,
SELECT book.*
FROM Library library (index idx_library_pk)
LEFT JOIN Book book (index idx_book_library_id)
ON (library.Id = book.libraryId)
WHERE library.Id = ?

This would work fine to get a set of Books, and marshal them to a java List<Book>.

But what if I specifically need a List<Library> collection (each instance of Library should have a populated .books collection of the corresponding Book objects)? The closest I could get to this with createNativeQuery() was parsing a List<Object[]> where each object array contained a joined pair of Library and Book objects. Transforming this manually into a List<Library> collection of unique Library objects and then appending each corresponding Book object into the appropriate library.books would be a manual process, inefficient, and a nightmare to read and maintain.

THE SOLUTION

Lo and behold, the Session.createSQLQuery().

Spring JPA keeps a pointer to the Hibernate session in the entityManager object. So if you have an open entityManager object you can perform the following magic:
Session session = (Session)entityManager.getDelegate();

Next, you can create a SQLQuery, which is really just a subtype of a hibernate Query:
SQLQuery query = session.createSQLQuery(sql);


We want to modify this line a bit to tell the SQLQuery that we want to marshal the results to the Library class:
SQLQuery query = session.createSQLQuery(sql)                                          .addEntity( "library", Library.class );

This by itself would generate a List<Library> by directing hibernate to marshal the library alias to Library class objects. But we also have to explain that the book fields in our SQL query should map to the library.books collection in the mapped Library class:
SQLQuery query = session.createSQLQuery(sql)                                          .addEntity( "library", Library.class )                                          .addJoin( "book", "library.books" );


This implicitly tells hibernate that the book fields will be mapped to Book objects, but this by itself yields us yet again a List<Object[]>, the closest we got with the more familiar Spring/JPA Hibernate getNativeQuery(). Hibernate doesn't inherently know how to properly marshal the books member collection. But where we reached a dead end with the Query object, SQLQuery gives us one last trick:
SQLQuery query = session.createSQLQuery(sql)                                          .addEntity( "library", Library.class )                                          .addJoin( "book", "library.books" )                                          .addEntity( "library", Library.class )                                          .setResultTransformer( Criteria.DISTINCT_ROOT_ENTITY );

Due to a bug we oddly enough have to repeat the addEntity(), because setResultTransformer only works with the last entity referenced (we want a List<Library>, not List<Book>). But this does all the work for us of transforming a correlated list of libraries and books into a List<Library> with a populated member collection of Book objects.

So finally we execute the query, Hibernate transforms and marshals the results, and we can retrieve our POJO list:
List<Library> libraries = query.list();

Hurrah, we've done it!

SUMMARY

If you marshal database records to a Java class object (POJO) with Spring JPA and find the standard Hibernate EntityManager#createQuery() isn't sufficient, you have other options. Hibernate's EntityManager#createNativeQuery() works well when you are dealing with only a single entity. But for more complex relationships involving multiple entities mapped to multiple class objects Session#createSQLQuery() provides an interface to define object relationships and transform the query resultset into the specific class model you need with minimal code changes.

Friday, January 25, 2013

It's the little things that make me hate Solaris

It's the little differences in Solaris 10 that really make it hard for me to feel comfortable in my new production environment. So in every distro of Linux going back as far as I can remember, if you want to schedule a cronjob to run every 5 minutes you would format the crontab entry something like this:
*/5 * * * * ~/my-awesome-script.sh
But in Solaris, trying to load this into your crontab generates a syntax error. Instead, you are expected to do something like this:
0,5,10,15,20,25,30,35,40,45,50,55 * * * * ~/my-awesome-script.sh
Why? Because Solaris 10 is still running a cron that's older than dirt. It's the little things that are starting to make me really hate Solaris.

Friday, January 18, 2013

Reflections on "A Memory of Light", the last novel in The Wheel of Time

Yesterday I finished reading "A Memory of Light", the last book in The Wheel of Time series by Robert Jordan and +Brandon Sanderson

A little over fifteen years ago +Jason Christ introduced the epic WoT series to a skinny Freshman student who had just started working together at a small University tech department. Jason carried a hefty hardback, and the characteristic cover-art of Darrell K. Sweet caught my curiosity, as I had read and loved the Lord of the Rings series by J.R.R. Tolkien since my mother had read to me The Hobbit as a small child. While I was a bit skeptical that any other author could come close to the same depth of character and the scope of their world and plots, I took the recommendation and dove into the first 6 novels that were available at the time. There may have been a few hundred slow pages in the middle, but overall Robert Jordan's vision well met the expectations set by the truly great first volume, "The Eye of the World".

To avoid being too spoiler-ish, for now I will refrain from jumping into a critique of specific plot points in AMoL. But I can say that it was gripping, emotional, mostly satisfying, and sometimes intentionally exhausting end to the series. Brandon Sanderson did a great job of picking up where Robert Jordan left off, and crafted a balanced, well-paced and compelling conclusion to the struggles of characters that readers have seen grown from small-country-folk into complex personalities of nearly deistic powers. The conclusion of the Last Battle between the forces of good and evil felt genuinely climactic, while allowing for a good resolution to *most* of the plot lines that had been building across thousands of pages and in our imaginations for years.

I certainly looked forward to reading this last entry in the series, but to paraphrase Robert Jordan it did not feel as much like The Ending, but it was an ending, and I hope that The Wheel of Time might someday come back around to us again.

Thursday, November 1, 2012

New city, new focus

This month has been full of changes for my household.

First, I accepted and started my new job with BJC HealthCare in St. Louis, MO. My official title is "Senior Analyst", I am working on the Healthcare Informatics team at the Center for Clinical Excellence, and my primary function is Java development.

Second, my wife, Natalie, the two kids and I have moved from Chicago to St. Louis. We are currently staying with family in the area. We are happy to be closer to Natalie's family in the area, but will miss the other Bishops in northern Illinois that we leave behind.

Our condo in Chicago is (hopefully) in the final stages of selling, so Natalie and I have started the process of looking at apartments and house shopping in the neighborhoods around my new office.

At the time I am writing this, I am well into the third week at my new job, and am wrapping up my first project. It's nice to feel challenged and productive! BJC is a great organization, and the staff are very knowledgeable and professional, but maintain a flexible and friendly environment. So far the job has been great, and I feel a very good fit for me personally and professionally.

My posts on geekmode.com will likely take a turn to more enterprise development topics, and I already have some material to finish writing about some of the inherent benefits and nagging challenges of using hibernate as a data abstraction layer in Java.

So as usual, thanks for following geekmode.com, and stay tuned for more!

Thursday, September 27, 2012

Android 4.1 Jelly Bean coming to the ASUS Transformer Pad Infinity TF700 and Prime TF201


ASUS stated yesterday that the official Android 4.1 Jelly Bean update will be rolling out to two of the ASUS Transformer Pads over the next two days.

There are reports over at XDA that the update has been rolling out to the ASUS Transformer Pad Prime TF201 starting early this morning.

I very recently picked up the ASUS Transformer Infinity TF700, and I absolutely love it, but felt a bit snubbed that the TF300 received the Jelly Bean update before my newer model tablet. It really is a slick device, and nearly a laptop replacement with the attachable keyboard-dock. I will be very excited to get the Jelly Bean goodness of Google Now and Butter UI running on my Infinity when it rolls out tomorrow.

UPDATE: XDA is reporting that the Jelly Bean update started rolling out to TF700's this morning. I can confirm this, as I finished installing the update myself minutes ago. I will try to post my impressions of the update soon!

--
Original Press Release from: https://www.facebook.com/ASUS
Via: http://www.talkandroid.com/133753-asus-confirms-jelly-bean-for-transformer-prime-and-transformer-pad-infinity-coming-soon/

Thursday, August 2, 2012

"I am the 99%", the overlooked Android fragmentation story

When most people talk about OS fragmentation on Android devices, they usually are talking about vendors who are slow to push out updates to their older devices. More rarely I've heard mentioned the less-than-technical end-users who never bother to install available updates. I've personally been affected by both: Sprint is less than stellar at making their Sense UI updates to Google releases to my last two cellphones, and I am constantly reminding my friends and family of the advantages to getting updates to their phones that are available. For the latter, I've ended up being the guy actually installing the updates for them.

But I think there is an untold story here about a growing percentage of Android enthusiasts who are literally being forced to stay behind the curve. Insert "Help, help, I'm being repressed!" quote here.

I am a techie. I am an Android geek. I am an avid CyanogenMod follower, and simply must install the latest patch, update and sometimes even follow nightly builds when they are available (and arguably stable).

I am still running Gingerbread because Sprint (and now officially CyanogenMod) do not plan to release an ICS or Jellybean update for my device. 

I would spend my hard-earned cash and a lunch break to go buy a new Android smartphone today, but I am still months away from the end of my 2-year contract with Sprint. 

I realize there are some GSM providers that contact you to a SIM card instead of a single device, and that's great for them. But Sprint is largely a CDMA provider, so that isn't an option for me and anyone else who can't simply swap a SIM into a new phone and go. Sprint is literally making it impossible for large populations of their customers to benefit from the latest that Android has to offer by refusing to provide device updates, and making it cost-prohibitive to purchase new phones before the 2-year contractual waiting period is over.

I am one of the 99% not running the latest Android OS because I am contractually obligated to stay there until the end of 2012. Doesn't that seem like a bad business model for Sprint?

Friday, July 20, 2012

Programming is not Algebra... except when it is.

Programming is not Algebra: http://andy.wordpress.com/2012/05/30/programming-is-not-algebra/
... except when it is.

I loved reading the comments/discussion that followed this post.

Saturday, June 16, 2012

CyanogenMod 7.2 official release!

The official release of CyanogenMod 7.2 for Android is out!

http://www.cyanogenmod.com/blog/cyanogenmod-7-2

And yes, I already have it installed on my HTC EVO 4G.

Teaching myself Groovy and Grails

I've just finished up my first web introduction to Groovy and Grails. It's a very interesting development platform, and one that holds a lot of promise for future portal and web-app development at my day-job.

One of the variants I have seen is, what is the most efficient method to provide database connectivity between a Groovy/Grails app and a database server?

I've seen two examples:
1. Using a data model to define the database, and use the data model to pull data into your app as needed. I imagine this is the most-often used method(?)
2. Using a hard-coded db query in the control file to manually set exactly what data is being pulled for a single-task-centric application. For example, an app that is meant to only display a single piece of information about a user, like "Balance Due", "Meals left on your Board Plan", "Final grades from last semester".

I need to do some more investigation myself into the "best practice" method of implementing database queries, but I am open to hear any comments on benefit/costs of using either strategies above in Groovy/Grails.

Wednesday, June 13, 2012

Problem flashing CM7.2 nightlies since RC3

I have had to roll back the last three attempts to flash a CM7.2 nightly to my EVO, which is currently running the CyanogenMod 7.2 RC3 build.

The main symptoms are:
Applications appear removed from the system, any shortcuts to those applications are either missing or do not open.
The Google Play store indicates that I must link a Google account in order to open, prompts "Do you wish to add an account now, Y/N?" but if I click "YES", the dialog and app just closes.

Am I missing something that I should be doing when going from RC3 to one of the 3 later nightlies? (5/27, 6/3, 6/10).

It just recently occurred to me that I might need to re-flash the Google Apps add-on to my phone to fix the account problem, but I am just taking a stab in the dark at this point.

I do run Titanium Backup, but haven't tried doing a full data wipe and then restoring from TB, but unfortunately TB was one of the apps that appeared missing.

I would like to avoid having to do a full wipe if that is acceptable between RC3 and later nightlies. If this is just a pipe dream and a full wipe is manditory, I can bite the bullet and give it a try.

Monday, April 23, 2012

Android ICS on the EVO 4G

First, a disclaimer: This post is probably only relevant to EVO 4G users who enjoy custom ROMs, or ROM developers who are following their user's experiences.


So my contract on my EVO 4G isn't up until November April. And neither Sprint nor HTC are planning to release an official update to the next major version of the Android OS (version 4, AKA Ice Cream Sandwich). So if I am going to experience ICS at all in the foreseeable future, I am stuck rooting my phone and trying one of the hacked ports of ICS.

Now I've seen the list of ICS ports on the XDA forums, but I will admit I am hesitant to try many of the builds unless I have a lot of prior experience with the developers, and/or they have a reputation for putting together solid ROMs.

I have given the Android Open Kang Project a try (build 30) but experienced random lockups, reboots and a generally non-functioning camera.

Now, I realize that the camera issue is still a widespread problem for the EVO, because there isn't a solid ICS driver for the camera hardware yet (But come on, HTC, how difficult could it be to "leak" the drivers to the open source community? You would be doing yourself a favor).

So I will be running CyanogenMod 7.2 (whatever seems to be the latest stable RC/nightly) until either CM9 RC1 is released for the EVO 4G, or someone points me in the direction of a stable ICS port.


Tuesday, April 3, 2012

Sungard Summit 2012 conference summary

Here are my top takeaways from the Sungard Higher-Ed Summit conference, hosted this year in Las Vegas, NV.


Sungard + Datatel = ellucian

I really enjoyed hearing nine-time Grammy award winner John Legend speak at the general opening session on the importance of Education in addressing poverty and attaining societal goals. After his talk he also sang a few songs, and although R&B isn't my personal preference of musical styles, he exhibited both his musical talent and flexibility in entertaining a crowd of over 6,000 Higher-Ed professionals. Legend's message contributed to the most applicable general session I have attended at Summit.

At the end of the opening session was the announcement of the new name for the newly-merged company: ellucian. 

The investment merger of two of the biggest names in Higher Education caught my attention when the initial announcement went out, as we use Sungard's ERP solution, Banner, and I administer our web portal which runs on their Luminis technology (Sungard's repackaged take on uPortal). The implications of this merger have yet to be seen, but hey, apparently their marketing team and lawyers were able to pick a new name, so that's good, right?



I feel like Santa Claus,

Because I am making a list and checking it twice. It's been nearly two years since the news of the release Luminis version 5, the latest-and-greatest release of Sungard's portal solution for Higher Ed. But finally the patches and feature improvements have progressed to the point that I feel ready to take on the transition from our existing portal. This will require a lot of planning -- there is no upgrade from our current portal running Luminis 4, so we will be starting over from the hardware through content creation.

Technically speaking, this upgrade will bring us out of the late 90's of web portal technology into the modern world of Web 2.0. The underlying platform will change from uPortal to Liferay, meaning a proprietary content delivery system will be replaced with a standards-compliant Java portlet system. A proprietary SSO framework (CPIP, GCF) will be sidelined by the centralized, industry-standard CAS authentication. A central content management system (based on JackRabbit) and improved permissions delegation will mean I can off-load the responsibility of content management to the actual content owners much more deftly and to a more fine-tuned, targeted audience. AJAX, Flex and Springboard will provide the interactive experience that our online constituents are already used to and expect.

This will be a major step forward for my University to offer the services that our students, faculty and staff have come to rely on and expect from the institution, and I am excited about the opportunities ahead of us.



Vegas is overrated.

This is a pretty minor point, as I spent most of my time in sessions or participating in discussions with my colleagues at the convention, and the locale doesn't often play much of a role in a good conference experience. But I have been to conferences in other cities (New Orleans, Anaheim) that did have a huge positive influence. So here it is:

My most cultured experience in Las Vegas was a trip to the local In-N-Out burger.

The conference was hosted in the Mandalay Bay convention center and my hotel room was in the Excalibur, so it was pretty hard to avoid the lights, noise and smell of casino gambling. Even the shopping areas were superficial and sad, as most of their products were either branded with a casino logo, a Vegas show personality, or a tasteless slogan. None of it was really all that pleasant.

I have decided I generally dislike the Las Vegas strip. This is my own personal bias, as I am not a gambler. But if all of The Math states that when you gamble money in Vegas, you are going to leave with a lot less money than when you arrived: no thank you. So I did not waste spend any of my hard-earned money in any of the casinos, shops or other ridiculous "attractions" on the strip. And the Vegas strip really has little else to offer than temporary, expensive vices.

If there was an easy way for a convention attendee such as myself to get off-strip to see some actual culture in Las Vegas, I am still not aware of it.



Technology is the future of Higher Education.

Technology allows us to serve more effectively and efficiently, and reach people around the globe in ways that we hadn't even begun to imagine 5 years ago. 

Technology alone cannot educate, but when used properly can enable us to better educate. 

Concordia University Chicago launched it's first mobile application last month (you can find it right now in the Apple iTunes store and the Google Play Android market). Students can check their grades and look up their course schedule at a glance from their mobile devices; the community can stay connected to news, events and services on campus like they never have before. And we have only just begun to get our feet wet in what mobile services can do for our community.

Over the past 5 years we have implemented a web portal that integrates with our ERP; gives direct access to our online learning system; faculty can interact with their students and submit grades online; students can register for classes, apply for campus housing, pay their tuition electronically, and get their parking permits. Some of these functions may appear trivial at first glance, but as a part of a greater integrated solution it has changed the experience of our students, faculty and staff for the better.




Sungard Summit is not only a showcase of new technology, but also a forum for sharing ideas and successes, common struggles and creative solutions. If you were in Las Vegas this year at the very last Sungard Summit, I hope I had an opportunity to interact with you in one way or another. But if not, I look forward to seeing you next year at Ellucian Summit 2013 in Philadelphia!


Tuesday, January 24, 2012

Will Android ICS (Ice Cream Sandwich) be available on the EVO 4G?


I have become an avid CyanogenMod user since 7.1 resurrected my wife's Hero. Yes, the years-old HTC Hero running Gingerbread via CyanogenMod 7.1, and running great.

But more than resurrecting obsolete phones, the Cyanogen team is currently working on CyanogenMod 9, and that will bring an unofficial port of ICS to phones such as the EVO 4G, which would otherwise be abandoned by Sprint (and other carriers) from the official system update channels.

From what I have gathered, the biggest issue on the EVO 4G is finding a proper camera driver that works with ICS. Cyanogen has submitted a request to HTC to release an updated driver.

So with a little luck, the hard work of the Cyanogen team, and a lot of patience from CM fans such as myself, ICS will be on the EVO 4G in the foreseeable future.

Wednesday, January 18, 2012

GeekMode blackout

I am not posting anything to my blog today to show my solidarity in opposing SOPA, PIPA, and any legislation that would limit a free internet, free speech, and any other privileges granted to us under the U.S. Constitution and Bill of Rights.

Of course, this is a blog post itself, after a long stay of not posting anything to this blog, I hope you appreciate the inherent irony.

Please tell your Government officials: don't censor the Internet! http://www.google.com/takeaction/

Thursday, October 13, 2011

CyanogenMod 7.1 stable release

For all you android enthusiasts to like to root and flash new ROMs,
If you are like me, I've been waiting for months, and it's finally here!

CyanogenMod 7.1 is officially out!

Now, to be honest I've been running the 7.1 RC1 for several months already, and if I had more time I would be flashing the nightlies, too. But here are just a few key feature updates that you can expect to get going from the RC1 to the official release:

Android 2.3.7
Many UI tweaks and bug fixes
Improved support for WiMax
Update for the stock camera app


Find the download for your android device here: http://www.cyanogenmod.com/devices

Wednesday, May 18, 2011

The CDC warns citizens to prepare for the impending zombie apocalypse

From the CDC article:
Better Safe than Sorry
"So what do you need to do before zombies…or hurricanes or pandemics for example, actually happen?"
http://blogs.cdc.gov/publichealthmatters/2011/05/preparedness-101-zombie-apocalypse/

... And just a reminder to my readers, I still do freelance home-zombie-safety inspections.

Wednesday, April 27, 2011

The 50 books every child should read, via The Independent

I created a checklist of the books I have read from the article, "The 50 books every child should read", via The Independent. (Please check out their article for the full list.)

Alice's Adventures in Wonderland and Through the Looking Glass by Lewis Carroll

Pinocchio by Carlo Collodi

A Christmas Carol by Charles Dickens

Treasure Island by R.L. Stevenson

The Old Man and the Sea by Ernest Hemingway

The Secret Garden by Frances Hodgson-Burnett

The Hobbit and The Lord of the Rings by J.R.R. Tolkien
(Shouldn't these count as 4 books?)

The Adventures of Sherlock Holmes by Sir Arthur Conan Doyle

Little Women by Louisa May Alcott
(I read the entire series which includes Little Men and Jo's Boys)

Animal Farm by George Orwell
(I was a little surprised to see this on a children's book list, but I probably first read this when I was 12.)


---

So after reading their list, I decided that it was missing some very important classics that I look forward to my own daughter reading someday.

The Lion, The Witch and the Wardrobe, the series, by C.S. Lewis

Anne of Green Gables, the series, by Lucy Maud Montgomery

My Teacher is an Alien, the series, by Bruce Coville

Pilgrim's Progress by John Bunyan

The Swiss Family Robinson by Johann Rudolf Wyss

A Journey to the Center of the Earth by Jules Verne

The Adventures of Tom Sawyer and Adventures of Huckleberry Finn by Mark Twain


---

And really, that's just the tip of the iceberg.