Index: applications/editors/josm/plugins/openlayers/.classpath
===================================================================
--- applications/editors/josm/plugins/openlayers/.classpath	(revision 8748)
+++ applications/editors/josm/plugins/openlayers/.classpath	(revision 8748)
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="lib" path="lib/cobra.jar"/>
+	<classpathentry kind="lib" path="lib/js.jar"/>
+	<classpathentry kind="lib" path="lib/ehcache-1.4.1.jar"/>
+	<classpathentry kind="lib" path="lib/commons-logging-1.0.4.jar"/>
+	<classpathentry kind="lib" path="lib/backport-util-concurrent-3.0.jar"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/JOSM"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="output" path="build"/>
+</classpath>
Index: applications/editors/josm/plugins/openlayers/.project
===================================================================
--- applications/editors/josm/plugins/openlayers/.project	(revision 8748)
+++ applications/editors/josm/plugins/openlayers/.project	(revision 8748)
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>openLayers</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
Index: applications/editors/josm/plugins/openlayers/.settings/org.eclipse.jdt.core.prefs
===================================================================
--- applications/editors/josm/plugins/openlayers/.settings/org.eclipse.jdt.core.prefs	(revision 8748)
+++ applications/editors/josm/plugins/openlayers/.settings/org.eclipse.jdt.core.prefs	(revision 8748)
@@ -0,0 +1,78 @@
+#Thu Apr 17 13:20:38 CEST 2008
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=enabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=enabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=warning
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=enabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=warning
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=enabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=1.5
Index: applications/editors/josm/plugins/openlayers/README
===================================================================
--- applications/editors/josm/plugins/openlayers/README	(revision 8748)
+++ applications/editors/josm/plugins/openlayers/README	(revision 8748)
@@ -0,0 +1,19 @@
+A plugin for displaying OpenLayers images as a background
+
+This plugin is a proof of concept, and so it is at a very first stage.
+
+What it does: 
+    * It downloads a Yahoo satellite background image of the current view port (well, sort of)
+
+What it does NOT:
+    * Everything else :-)
+ 
+What needs to be done:
+    * Fix positions
+    * Fix refresh problems
+    * Patch JOSM to restrict zoom to discrete steps (needed by Yahoo)
+    * Get rid of ehcache. It's too bulky for our needs.
+    * Add configuration options.
+        * Allow the user to add any kind of OpenLayer layers
+        * Add options for cache (size, expiration time...)
+    * Find some clever way of distributing dependency libraries. A plugin of 2.1Mb seems too big.
Index: applications/editors/josm/plugins/openlayers/build.xml
===================================================================
--- applications/editors/josm/plugins/openlayers/build.xml	(revision 8748)
+++ applications/editors/josm/plugins/openlayers/build.xml	(revision 8748)
@@ -0,0 +1,83 @@
+<project name="openlayers" default="dist" basedir=".">
+
+  <!-- josm "user home" directory depends on the platform used (windows has a different place than unix/linux) -->
+  <property environment="env"/>
+  <condition property="josm.home.dir" value="${env.APPDATA}/JOSM" else="${user.home}/.josm">
+    <and>
+      <os family="windows"/>
+    </and>
+  </condition>
+
+  <!-- compilation properties -->
+  <property name="josm.build.dir"	value="../../core"/>
+  <property name="josm.plugins.dir" value="${josm.home.dir}/plugins"/>
+  <property name="josm"			    location="../../core/dist/josm-custom.jar" />
+  <property name="plugin.build.dir"	value="build"/>
+  <property name="plugin.dist.dir"	value="../../dist"/>
+  <property name="plugin.name"		value="${ant.project.name}"/>
+  <property name="plugin.jar"		value="../../dist/${plugin.name}.jar"/>
+
+  <property name="ant.build.javac.target" value="1.5"/>
+
+  <!-- All jar files needed -->
+  <fileset id="required_libs" dir="lib">
+    <include name="cobra.jar"/>
+    <include name="js.jar"/>
+    <include name="ehcache-1.4.1.jar"/>
+    <include name="backport-util-concurrent-3.0.jar"/> <!-- needed by ehcache -->
+    <include name="commons-logging-1.0.4.jar"/> <!--needed by ehcache -->
+  </fileset>
+
+  <target name="init">
+    <mkdir dir="build"></mkdir>
+    <mkdir dir="dist"></mkdir>
+  </target>
+	
+  <target name="compile" depends="init">
+    <echo message="creating ${plugin.jar}"/>
+    <javac srcdir="src" debug="true" destdir="build">
+        <classpath>
+            <path path="../../core/dist/josm-custom.jar"/>
+            <fileset refid="required_libs"/>
+        </classpath>
+    </javac>
+  </target>
+	
+  <target name="dist" depends="compile">
+	<!-- jars -->
+  	<!-- TODO: instead of adding library code to the plugin jar, JOSM should 
+  	     have some kind of library dir loaded in the classpath -->
+	<unjar dest="build">
+		<fileset refid="required_libs" />
+	</unjar>
+
+  	<!-- images -->
+    <copy todir="build/images">
+      <fileset dir="images" />
+    </copy>
+  	
+  	<!--resources -->
+    <copy todir="build/resources">
+      <fileset dir="resources" />
+    </copy>
+    
+    <!-- create josm-custom.jar -->
+    <jar destfile="${plugin.jar}" basedir="build">
+      <manifest>
+        <attribute name="Plugin-Class" value="org.openstreetmap.josm.plugins.openLayers.OpenLayersPlugin" />
+        <attribute name="Plugin-Description" value="Displays an OpenLayers background image" />
+        <attribute name="Class-Path" value="cobra.jar js.jar ehcache-1.4.1.jar commons-logging-1.0.4.jar backport-util-concurrent-3.0.jar" />
+      </manifest>
+    </jar>
+  </target>
+  
+  <target name="clean">
+    <delete dir="${plugin.build.dir}" />
+    <delete file="${plugin.jar}" />
+  </target>
+  
+  <target name="install" depends="dist">
+    <copy file="${plugin.jar}" todir="${josm.plugins.dir}"/>
+  </target>
+
+</project>
Index: applications/editors/josm/plugins/openlayers/resources/ehcache.xml
===================================================================
--- applications/editors/josm/plugins/openlayers/resources/ehcache.xml	(revision 8748)
+++ applications/editors/josm/plugins/openlayers/resources/ehcache.xml	(revision 8748)
@@ -0,0 +1,473 @@
+<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd">
+
+    <!--
+    CacheManager Configuration
+    ==========================
+    An ehcache.xml corresponds to a single CacheManager.
+    
+    See instructions below or the ehcache schema (ehcache.xsd) on how to configure.
+
+    DiskStore configuration
+    =======================
+
+    Sets the path to the directory where cache files are created.
+
+    If the path is a Java System Property it is replaced by its value in the
+    running VM.
+
+    The following properties are translated:
+    * user.home - User's home directory
+    * user.dir - User's current working directory
+    * java.io.tmpdir - Default temp file path
+
+    Subdirectories can be specified below the property e.g. java.io.tmpdir/one
+    -->
+    <diskStore path="java.io.tmpdir"/>
+
+    <!--
+    CacheManagerEventListener
+    =========================
+    Specifies a CacheManagerEventListenerFactory, be used to create a CacheManagerPeerProvider,
+    which is notified when Caches are added or removed from the CacheManager.
+
+    The attributes of CacheManagerEventListenerFactory are:
+    * class - a fully qualified factory class name
+    * properties - comma separated properties having meaning only to the factory.
+
+    Sets the fully qualified class name to be registered as the CacheManager event listener.
+
+    The events include:
+    * adding a Cache
+    * removing a Cache
+
+    Callbacks to listener methods are synchronous and unsynchronized. It is the responsibility
+    of the implementer to safely handle the potential performance and thread safety issues
+    depending on what their listener is doing.
+
+    If no class is specified, no listener is created. There is no default.
+    -->
+    <cacheManagerEventListenerFactory class="" properties=""/>
+
+
+    <!--
+    CacheManagerPeerProvider
+    ========================
+    (Enable for distributed operation)
+
+    Specifies a CacheManagerPeerProviderFactory which will be used to create a
+    CacheManagerPeerProvider, which discovers other CacheManagers in the cluster.
+
+    The attributes of cacheManagerPeerProviderFactory are:
+    * class - a fully qualified factory class name
+    * properties - comma separated properties having meaning only to the factory.
+
+    Ehcache comes with a built-in RMI-based distribution system with two means of discovery of
+    CacheManager peers participating in the cluster:
+    * automatic, using a multicast group. This one automatically discovers peers and detects
+      changes such as peers entering and leaving the group
+    * manual, using manual rmiURL configuration. A hardcoded list of peers is provided at
+      configuration time.
+
+    Configuring Automatic Discovery:
+    Automatic discovery is configured as per the following example:
+    <cacheManagerPeerProviderFactory
+                        class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
+                        properties="peerDiscovery=automatic, multicastGroupAddress=230.0.0.1,
+                                    multicastGroupPort=4446, timeToLive=32"/>
+
+    Valid properties are:
+    * peerDiscovery (mandatory) - specify "automatic"
+    * multicastGroupAddress (mandatory) - specify a valid multicast group address
+    * multicastGroupPort (mandatory) - specify a dedicated port for the multicast heartbeat
+      traffic
+    * timeToLive - specify a value between 0 and 255 which determines how far the packets will
+      propagate.
+
+      By convention, the restrictions are:
+      0   - the same host
+      1   - the same subnet
+      32  - the same site
+      64  - the same region
+      128 - the same continent
+      255 - unrestricted
+
+    Configuring Manual Discovery:
+    Manual discovery is configured as per the following example:
+    <cacheManagerPeerProviderFactory class=
+                          "net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
+                          properties="peerDiscovery=manual,
+                          rmiUrls=//server1:40000/sampleCache1|//server2:40000/sampleCache1
+                          | //server1:40000/sampleCache2|//server2:40000/sampleCache2"
+                          propertySeparator="," />
+
+    Valid properties are:
+    * peerDiscovery (mandatory) - specify "manual"
+    * rmiUrls (mandatory) - specify a pipe separated list of rmiUrls, in the form
+                            //hostname:port
+
+    The hostname is the hostname of the remote CacheManager peer. The port is the listening
+    port of the RMICacheManagerPeerListener of the remote CacheManager peer.
+
+    -->
+    <cacheManagerPeerProviderFactory
+            class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
+            properties="peerDiscovery=automatic,
+                        multicastGroupAddress=230.0.0.1,
+                        multicastGroupPort=4446, timeToLive=1"
+            propertySeparator=","
+            />
+
+
+    <!--
+    CacheManagerPeerListener
+    ========================
+    (Enable for distributed operation)
+
+    Specifies a CacheManagerPeerListenerFactory which will be used to create a
+    CacheManagerPeerListener, which
+    listens for messages from cache replicators participating in the cluster.
+
+    The attributes of cacheManagerPeerListenerFactory are:
+    class - a fully qualified factory class name
+    properties - comma separated properties having meaning only to the factory.
+
+    Ehcache comes with a built-in RMI-based distribution system. The listener component is
+    RMICacheManagerPeerListener which is configured using
+    RMICacheManagerPeerListenerFactory. It is configured as per the following example:
+
+    <cacheManagerPeerListenerFactory
+        class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
+        properties="hostName=fully_qualified_hostname_or_ip,
+                    port=40001,
+                    socketTimeoutMillis=120000"
+                    propertySeparator="," />
+
+    All properties are optional. They are:
+    * hostName - the hostName of the host the listener is running on. Specify
+      where the host is multihomed and you want to control the interface over which cluster
+      messages are received. Defaults to the host name of the default interface if not
+      specified.
+    * port - the port the listener listens on. This defaults to a free port if not specified.
+    * socketTimeoutMillis - the number of ms client sockets will stay open when sending
+      messages to the listener. This should be long enough for the slowest message.
+      If not specified it defaults 120000ms.
+
+    -->
+    <cacheManagerPeerListenerFactory
+            class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"/>
+
+
+    <!--
+    Cache configuration
+    ===================
+
+    The following attributes are required.
+
+    name:
+    Sets the name of the cache. This is used to identify the cache. It must be unique.
+
+    maxElementsInMemory:
+    Sets the maximum number of objects that will be created in memory
+
+	maxElementsOnDisk:
+    Sets the maximum number of objects that will be maintained in the DiskStore
+	The default value is zero, meaning unlimited.
+
+    eternal:
+    Sets whether elements are eternal. If eternal,  timeouts are ignored and the
+    element is never expired.
+
+    overflowToDisk:
+    Sets whether elements can overflow to disk when the memory store
+    has reached the maxInMemory limit.
+
+    The following attributes and elements are optional.
+
+    timeToIdleSeconds:
+    Sets the time to idle for an element before it expires.
+    i.e. The maximum amount of time between accesses before an element expires
+    Is only used if the element is not eternal.
+    Optional attribute. A value of 0 means that an Element can idle for infinity.
+    The default value is 0.
+
+    timeToLiveSeconds:
+    Sets the time to live for an element before it expires.
+    i.e. The maximum time between creation time and when an element expires.
+    Is only used if the element is not eternal.
+    Optional attribute. A value of 0 means that and Element can live for infinity.
+    The default value is 0.
+
+    diskPersistent:
+    Whether the disk store persists between restarts of the Virtual Machine.
+    The default value is false.
+
+    diskExpiryThreadIntervalSeconds:
+    The number of seconds between runs of the disk expiry thread. The default value
+    is 120 seconds.
+
+    diskSpoolBufferSizeMB:
+    This is the size to allocate the DiskStore for a spool buffer. Writes are made
+    to this area and then asynchronously written to disk. The default size is 30MB.
+    Each spool buffer is used only by its cache. If you get OutOfMemory errors consider
+    lowering this value. To improve DiskStore performance consider increasing it. Trace level
+    logging in the DiskStore will show if put back ups are occurring. 
+
+    memoryStoreEvictionPolicy:
+    Policy would be enforced upon reaching the maxElementsInMemory limit. Default
+    policy is Least Recently Used (specified as LRU). Other policies available -
+    First In First Out (specified as FIFO) and Less Frequently Used
+    (specified as LFU)
+
+    Cache elements can also contain sub elements which take the same format of a factory class
+    and properties. Defined sub-elements are:
+
+    * cacheEventListenerFactory - Enables registration of listeners for cache events, such as
+      put, remove, update, and expire.
+
+    * bootstrapCacheLoaderFactory - Specifies a BootstrapCacheLoader, which is called by a
+      cache on initialisation to prepopulate itself.
+
+    * cacheExtensionFactory - Specifies a CacheExtension, a generic mechansim to tie a class
+      which holds a reference to a cache to the cache lifecycle.
+
+    * cacheExceptionHandlerFactory - Specifies a CacheExceptionHandler, which is called when
+      cache exceptions occur.
+
+    * cacheLoaderFactory - Specifies a CacheLoader, which can be used both asynchronously and
+      synchronously to load objects into a cache.
+
+    RMI Cache Replication
+
+    Each cache that will be distributed needs to set a cache event listener which replicates
+    messages to the other CacheManager peers. For the built-in RMI implementation this is done
+    by adding a cacheEventListenerFactory element of type RMICacheReplicatorFactory to each
+    distributed cache's configuration as per the following example:
+
+    <cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+         properties="replicateAsynchronously=true,
+         replicatePuts=true,
+         replicateUpdates=true,
+         replicateUpdatesViaCopy=true,
+         replicateRemovals=true
+         asynchronousReplicationIntervalMillis=<number of milliseconds"
+         propertySeparator="," />
+
+    The RMICacheReplicatorFactory recognises the following properties:
+
+    * replicatePuts=true|false - whether new elements placed in a cache are
+      replicated to others. Defaults to true.
+
+    * replicateUpdates=true|false - whether new elements which override an
+      element already existing with the same key are replicated. Defaults to true.
+
+    * replicateRemovals=true - whether element removals are replicated. Defaults to true.
+
+    * replicateAsynchronously=true | false - whether replications are
+      asynchronous (true) or synchronous (false). Defaults to true.
+
+    * replicateUpdatesViaCopy=true | false - whether the new elements are
+      copied to other caches (true), or whether a remove message is sent. Defaults to true.
+
+    * asynchronousReplicationIntervalMillis=<number of milliseconds> - The asynchronous
+      replicator runs at a set interval of milliseconds. The default is 1000. The minimum
+      is 10. This property is only applicable if replicateAsynchronously=true
+
+
+    Cluster Bootstrapping
+
+    The RMIBootstrapCacheLoader bootstraps caches in clusters where RMICacheReplicators are
+    used. It is configured as per the following example:
+
+    <bootstrapCacheLoaderFactory
+        class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+        properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"
+        propertySeparator="," />
+
+    The RMIBootstrapCacheLoaderFactory recognises the following optional properties:
+
+    * bootstrapAsynchronously=true|false - whether the bootstrap happens in the background
+      after the cache has started. If false, bootstrapping must complete before the cache is
+      made available. The default value is true.
+
+    * maximumChunkSizeBytes=<integer> - Caches can potentially be very large, larger than the
+      memory limits of the VM. This property allows the bootstraper to fetched elements in
+      chunks. The default chunk size is 5000000 (5MB).
+
+
+    Cache Exception Handling
+
+    By default, most cache operations will propagate a runtime CacheException on failure. An
+    interceptor, using a dynamic proxy, may be configured so that a CacheExceptionHandler can
+    be configured to intercept Exceptions. Errors are not intercepted.
+
+    It is configured as per the following example:
+
+      <cacheExceptionHandlerFactory class="com.example.ExampleExceptionHandlerFactory"
+                                      properties="logLevel=FINE"/>
+
+    Caches with ExceptionHandling configured are not of type Cache, but are of type Ehcache only,
+    and are not available using CacheManager.getCache(), but using CacheManager.getEhcache().
+
+
+    Cache Loader
+
+    A default CacheLoader may be set which loads objects into the cache through asynchronous and
+    synchronous methods on Cache. This is different to the bootstrap cache loader, which is used
+    only in distributed caching.
+
+    It is configured as per the following example:
+
+        <cacheLoaderFactory class="com.example.ExampleCacheLoaderFactory"
+                                      properties="type=int,startCounter=10"/>
+
+    Cache Extension
+
+    CacheExtensions are a general purpose mechanism to allow generic extensions to a Cache.
+    CacheExtensions are tied into the Cache lifecycle.
+
+    CacheExtensions are created using the CacheExtensionFactory which has a
+    <code>createCacheCacheExtension()</code> method which takes as a parameter a
+    Cache and properties. It can thus call back into any public method on Cache, including, of
+    course, the load methods.
+
+    Extensions are added as per the following example:
+
+         <cacheExtensionFactory class="com.example.FileWatchingCacheRefresherExtensionFactory"
+                             properties="refreshIntervalMillis=18000, loaderTimeout=3000,
+                                         flushPeriod=whatever, someOtherProperty=someValue ..."/>
+
+    -->
+
+
+    <!--
+    Mandatory Default Cache configuration. These settings will be applied to caches
+    created programmtically using CacheManager.add(String cacheName).
+
+    The defaultCache has an implicit name "default" which is a reserved cache name.
+    -->
+    <defaultCache
+            maxElementsInMemory="10000"
+            eternal="false"
+            timeToIdleSeconds="120"
+            timeToLiveSeconds="120"
+            overflowToDisk="true"
+            diskSpoolBufferSizeMB="30"
+            maxElementsOnDisk="10000000"
+            diskPersistent="false"
+            diskExpiryThreadIntervalSeconds="120"
+            memoryStoreEvictionPolicy="LRU"
+            />
+
+    <!--
+    Sample caches. Following are some example caches. Remove these before use.
+    -->
+
+    <!--
+    Sample cache named sampleCache1
+    This cache contains a maximum in memory of 10000 elements, and will expire
+    an element if it is idle for more than 5 minutes and lives for more than
+    10 minutes.
+
+    If there are more than 10000 elements it will overflow to the
+    disk cache, which in this configuration will go to wherever java.io.tmp is
+    defined on your system. On a standard Linux system this will be /tmp"
+    -->
+    <cache name="sampleCache1"
+           maxElementsInMemory="10000"
+           maxElementsOnDisk="1000"
+           eternal="false"
+           overflowToDisk="true"
+           diskSpoolBufferSizeMB="20"
+           timeToIdleSeconds="300"
+           timeToLiveSeconds="600"
+           memoryStoreEvictionPolicy="LFU"
+            />
+
+
+    <!--
+    Sample cache named sampleCache2
+    This cache has a maximum of 1000 elements in memory. There is no overflow to disk, so 1000
+    is also the maximum cache size. Note that when a cache is eternal, timeToLive and
+    timeToIdle are not used and do not need to be specified.
+    -->
+    <cache name="sampleCache2"
+           maxElementsInMemory="1000"
+           eternal="true"
+           overflowToDisk="false"
+           memoryStoreEvictionPolicy="FIFO"
+            />
+
+
+    <!--
+    Sample cache named sampleCache3. This cache overflows to disk. The disk store is
+    persistent between cache and VM restarts. The disk expiry thread interval is set to 10
+    minutes, overriding the default of 2 minutes.
+    -->
+    <cache name="sampleCache3"
+           maxElementsInMemory="500"
+           eternal="false"
+           overflowToDisk="true"
+           timeToIdleSeconds="300"
+           timeToLiveSeconds="600"
+           diskPersistent="true"
+           diskExpiryThreadIntervalSeconds="1"
+           memoryStoreEvictionPolicy="LFU"
+            />
+
+
+    <!--
+    Sample distributed cache named sampleDistributedCache1.
+    This cache replicates using defaults.
+    It also bootstraps from the cluster, using default properties.
+    -->
+    <cache name="sampleDistributedCache1"
+           maxElementsInMemory="10"
+           eternal="false"
+           timeToIdleSeconds="100"
+           timeToLiveSeconds="100"
+           overflowToDisk="false">
+        <cacheEventListenerFactory
+                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"/>
+        <bootstrapCacheLoaderFactory
+                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"/>
+    </cache>
+
+
+    <!--
+    Sample distributed cache named sampleDistributedCache2.
+    This cache replicates using specific properties.
+    It only replicates updates and does so synchronously via copy
+    -->
+    <cache name="sampleDistributedCache2"
+           maxElementsInMemory="10"
+           eternal="false"
+           timeToIdleSeconds="100"
+           timeToLiveSeconds="100"
+           overflowToDisk="false">
+        <cacheEventListenerFactory
+                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+                properties="replicateAsynchronously=false, replicatePuts=false,
+                            replicateUpdates=true, replicateUpdatesViaCopy=true,
+                            replicateRemovals=false"/>
+    </cache>
+
+    <!--
+    Sample distributed cache named sampleDistributedCache3.
+    This cache replicates using defaults except that the asynchronous replication
+    interval is set to 200ms.
+    -->
+    <cache name="sampleDistributedCache3"
+           maxElementsInMemory="10"
+           eternal="false"
+           timeToIdleSeconds="100"
+           timeToLiveSeconds="100"
+           overflowToDisk="false">
+        <cacheEventListenerFactory
+                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+                properties="asynchronousReplicationIntervalMillis=200"/>
+    </cache>
+
+    
+
+
+</ehcache>
Index: applications/editors/josm/plugins/openlayers/resources/yahoo.html
===================================================================
--- applications/editors/josm/plugins/openlayers/resources/yahoo.html	(revision 8748)
+++ applications/editors/josm/plugins/openlayers/resources/yahoo.html	(revision 8748)
@@ -0,0 +1,86 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+   	<!-- The map can not be set to 100%. It hangs -->
+    <style type="text/css">
+        #map {
+            width: 1200px;
+            height: 900px;
+        }
+    </style>
+    <script type="text/javascript" src="http://api.maps.yahoo.com/ajaxymap?v=3.0&appid=euzuro-openlayers"></script>
+    <script type="text/javascript" src="http://www.openlayers.org/api/OpenLayers.js"></script>
+    <script type="text/javascript">
+    	// There seems to be a problem with this function and the Java JS engine, as the computed style 
+    	// does not have the function getPropertyValue.
+		OpenLayers.Element.getStyle =  function(element, style) {
+	        element = OpenLayers.Util.getElement(element);
+	        var value = element.style[OpenLayers.String.camelize(style)];
+	        if (!value) {
+	            if (document.defaultView && 
+	                document.defaultView.getComputedStyle && false) {
+	                
+	                var css = document.defaultView.getComputedStyle(element, null);
+	                value = css ? css.getPropertyValue(style) : null;
+	            } else if (element.currentStyle) {
+	                value = element.currentStyle[OpenLayers.String.camelize(style)];
+	            }
+	        }
+	    
+	        var positions = ['left', 'top', 'right', 'bottom'];
+	        if (window.opera &&
+	            (OpenLayers.Util.indexOf(positions,style) != -1) &&
+	            (OpenLayers.Element.getStyle(element, 'position') == 'static')) { 
+	            value = 'auto';
+	        }
+	    
+	        return value == 'auto' ? null : value;
+	    }
+    </script>
+    <script type="text/javascript">
+        var map, yahooLayer;
+
+        function init(){
+            var options = {
+                  projection: "WGS84",
+                  controls: []
+              };
+            map = new OpenLayers.Map('map', options );
+            yahooLayer = new OpenLayers.Layer.Yahoo( "Yahoo", {'type': YAHOO_MAP_SAT} );
+            map.addLayer(yahooLayer);
+            map.setCenter(new OpenLayers.LonLat(-3.6940, 40.4258), 12);
+        }
+    </script>
+    <script type="text/javascript">
+        function resizeMap(width, height)
+        {
+            var mapDiv = document.getElementById("map");
+            if( !mapDiv ) return;
+            
+            mapDiv.style.width = width;
+            mapDiv.style.height = height;
+            
+            if( map )
+                map.updateSize();
+        }
+        
+        function zoomMapToExtent(left, bottom, right, top)
+        {
+            if( !map ) return;
+            
+            map.zoomToExtent( new OpenLayers.Bounds(left, bottom, right, top) );
+            var extent = map.getExtent();
+            if( extent != null )
+            {
+                extent = extent.toArray();
+            }
+            return extent;
+        }
+    </script>
+  </head>
+  
+  <body onload="init()" style="overflow:hidden; margin: 0; padding: 0;">
+    <div id="overlay" style="background:white;opacity:0.5;z-index:10;">
+        <div id="map"></div>
+    </div>
+  </body>
+</html>
Index: applications/editors/josm/plugins/openlayers/src/org/openstreetmap/josm/plugins/openLayers/Browser.java
===================================================================
--- applications/editors/josm/plugins/openlayers/src/org/openstreetmap/josm/plugins/openLayers/Browser.java	(revision 8748)
+++ applications/editors/josm/plugins/openlayers/src/org/openstreetmap/josm/plugins/openLayers/Browser.java	(revision 8748)
@@ -0,0 +1,109 @@
+package org.openstreetmap.josm.plugins.openLayers;
+
+import java.awt.Dimension;
+import java.awt.EventQueue;
+import java.net.URL;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.lobobrowser.html.HtmlRendererContext;
+import org.lobobrowser.html.UserAgentContext;
+import org.lobobrowser.html.gui.HtmlBlockPanel;
+import org.lobobrowser.html.gui.HtmlPanel;
+import org.lobobrowser.html.js.Window;
+import org.lobobrowser.html.test.SimpleHtmlRendererContext;
+import org.mozilla.javascript.EcmaError;
+
+public class Browser extends HtmlPanel {
+
+    protected static final Logger logger = Logger.getLogger(Browser.class.getName());
+
+    static
+    {
+	Logger.getLogger("org.lobobrowser").setLevel(Level.WARNING);
+    }
+
+    private final SimpleHtmlRendererContext rcontext;
+
+    Dimension oldSize = null;
+
+    public Browser(String uri) {
+	super();
+
+	UserAgentContext ucontext = new CacheableUserAgentContext();
+	rcontext = new SimpleHtmlRendererContext(this, ucontext);
+	addNotify();
+
+	process( uri );
+    }
+
+    private void process(String uri) {
+	try {
+	    URL url;
+	    try {
+		url = new URL(uri);
+	    } catch (java.net.MalformedURLException mfu) {
+		int idx = uri.indexOf(':');
+		if (idx == -1 || idx == 1) {
+		    // try file
+		    url = new URL("file:" + uri);
+		} else {
+		    throw mfu;
+		}
+	    }
+	    // Call SimpleHtmlRendererContext.navigate()
+	    // which implements incremental rendering.
+	    this.rcontext.navigate(url, null);
+	} catch (Exception err) {
+	    err.printStackTrace();
+	}
+    }
+    
+    @Override
+    public void setSize(final Dimension newSize)
+    {
+	if (!newSize.equals(oldSize)) {
+	    oldSize = newSize;
+	    super.setSize(newSize);
+	    validate();
+
+	    executeAsyncScript("resizeMap(" + newSize.width + "," + newSize.height + ");");
+	}
+    }
+
+    public void executeAsyncScript(final String script)
+    {
+	EventQueue.invokeLater(new Runnable() {
+	    public void run() {
+		executeScript(script);
+	    }
+	});
+    }
+    
+    public Object executeScript(String script)
+    {
+	System.out.println("Executing script " + script);
+	try {
+	    Window window = Window.getWindow(rcontext);
+	    if( window.getDocumentNode() == null )
+		return null; // Document not loaded yet
+
+	    return window.eval(script);
+	} catch (EcmaError ecmaError) {
+	    logger.log(Level.WARNING, "Javascript error at " + ecmaError.sourceName() + ":" + ecmaError.lineNumber() + ": " + ecmaError.getMessage());
+	} catch (Throwable err) {
+	    logger.log(Level.WARNING, "Unable to evaluate Javascript code", err);
+	}
+	
+	return null;
+    }
+    
+    
+    /**
+     * Overrided to hide hardcoded scrollbars and insets
+     */
+    @Override
+    protected HtmlBlockPanel createHtmlBlockPanel(UserAgentContext ucontext, HtmlRendererContext rcontext) {
+	return new MyHtmlBlockPanel(java.awt.Color.WHITE, true, ucontext, rcontext, this);
+    }
+}
Index: applications/editors/josm/plugins/openlayers/src/org/openstreetmap/josm/plugins/openLayers/CacheableHttpRequest.java
===================================================================
--- applications/editors/josm/plugins/openlayers/src/org/openstreetmap/josm/plugins/openLayers/CacheableHttpRequest.java	(revision 8748)
+++ applications/editors/josm/plugins/openlayers/src/org/openstreetmap/josm/plugins/openLayers/CacheableHttpRequest.java	(revision 8748)
@@ -0,0 +1,324 @@
+package org.openstreetmap.josm.plugins.openLayers;
+
+import java.awt.Image;
+import java.awt.Toolkit;
+import java.io.*;
+import java.net.*;
+import java.util.EventObject;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.lobobrowser.html.*;
+import org.lobobrowser.util.*;
+import org.lobobrowser.util.io.IORoutines;
+import org.w3c.dom.Document;
+
+/**
+ * Cacheable HTTP request
+ * <p>
+ * This class will perform all HTTP request, caching the responses whenever possible
+ */
+public class CacheableHttpRequest implements HttpRequest {
+    private static final Logger logger = Logger.getLogger(CacheableHttpRequest.class.getName());
+
+    protected final UserAgentContext context;
+    protected final Proxy proxy;
+
+    protected boolean isAsync;
+    protected String requestMethod;
+    protected String requestUserName;
+    protected String requestPassword;
+
+    protected URL requestURL;
+    protected HttpResponse response;
+    protected int readyState;
+
+    /**
+     * The <code>URLConnection</code> is assigned to this field while it is
+     * ongoing.
+     */
+    protected URLConnection connection;
+
+    public CacheableHttpRequest(UserAgentContext context, Proxy proxy) {
+	this.context = context;
+	this.proxy = proxy;
+    }
+
+    public synchronized int getReadyState() {
+	return this.readyState;
+    }
+
+    public synchronized String getResponseText() {
+	if( response == null ) return null;
+	
+	byte[] bytes = this.response.responseBytes;
+	String encoding = this.response.encoding;
+	
+	try {
+	    return bytes == null ? null : new String(bytes, encoding);
+	} catch (UnsupportedEncodingException uee) {
+	    logger.log(Level.WARNING, "getResponseText(): Charset '" + encoding + "' did not work. Retrying with ISO-8859-1.", uee);
+	    try {
+		return new String(bytes, "ISO-8859-1");
+	    } catch (UnsupportedEncodingException uee2) {
+		// Ignore this time
+		return null;
+	    }
+	}
+    }
+
+    public synchronized Document getResponseXML() {
+	if( response == null ) return null;
+
+	byte[] bytes = this.response.responseBytes;
+	if (bytes == null) return null;
+
+	InputStream in = new ByteArrayInputStream(bytes);
+	try {
+	    return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(in);
+	} catch (Exception err) {
+	    logger.log(Level.WARNING, "Unable to parse response as XML.", err);
+	    return null;
+	}
+    }
+
+    public synchronized byte[] getResponseBytes() {
+	if( response == null ) return null;
+	return this.response.responseBytes;
+    }
+
+    public synchronized Image getResponseImage() {
+	if( response == null ) return null;
+
+	byte[] bytes = this.response.responseBytes;
+	if (bytes == null) return null;
+
+	return Toolkit.getDefaultToolkit().createImage(bytes);
+    }
+
+    public synchronized int getStatus() {
+	if( response == null ) return 0;
+	return this.response.status;
+    }
+
+    public synchronized String getStatusText() {
+	if( response == null ) return null;
+	return this.response.statusText;
+    }
+
+    public synchronized String getAllResponseHeaders() {
+	if( response == null ) return null;
+	return this.response.responseHeaders;
+    }
+
+    public synchronized String getResponseHeader(String headerName) {
+	if( response == null ) return null;
+	Map headers = this.response.responseHeadersMap;
+	return headers == null ? null : (String) headers.get(headerName);
+    }
+
+    public void open(String method, String url) throws IOException {
+	this.open(method, url, true);
+    }
+
+    public void open(String method, URL url) throws IOException {
+	this.open(method, url, true, null, null);
+    }
+
+    public void open(String method, URL url, boolean asyncFlag) throws IOException {
+	this.open(method, url, asyncFlag, null, null);
+    }
+
+    public void open(String method, String url, boolean asyncFlag) throws IOException {
+	URL urlObj = Urls.createURL(null, url);
+	this.open(method, urlObj, asyncFlag, null);
+    }
+
+    public void open(String method, URL url, boolean asyncFlag, String userName) throws IOException {
+	this.open(method, url, asyncFlag, userName, null);
+    }
+
+    public void abort() {
+	URLConnection c;
+	synchronized (this) {
+	    c = this.connection;
+	    response = null;
+	}
+	if (c instanceof HttpURLConnection) {
+	    ((HttpURLConnection) c).disconnect();
+	} else if (c != null) {
+	    try {
+		c.getInputStream().close();
+	    } catch (IOException ioe) {
+		ioe.printStackTrace();
+	    }
+	}
+    }
+
+    /**
+     * Opens the request. Call {@link #send(String)} to complete it.
+     * 
+     * @param method The request method.
+     * @param url The request URL.
+     * @param asyncFlag Whether the request should be asynchronous.
+     * @param userName The user name of the request (not supported.)
+     * @param password The password of the request (not supported.)
+     * @throws IOException If any error
+     */
+    public void open(final String method, final URL url, boolean asyncFlag, final String userName, final String password) throws IOException {
+	this.abort();
+
+	HttpResponse response = HttpResponse.lookup(url);
+	URLConnection c = null;
+	
+	if( response == null )
+	{
+	    c = proxy == null || proxy == Proxy.NO_PROXY ? url.openConnection() : url.openConnection(proxy);
+	    response = new HttpResponse();
+	}
+	    
+	synchronized (this) {
+	    this.connection = c;
+	    this.isAsync = asyncFlag;
+	    this.requestMethod = method;
+	    this.requestUserName = userName;
+	    this.requestPassword = password;
+	    this.requestURL = url;
+	    this.response = response;
+	    
+	    if( response.loaded )
+		changeState(HttpRequest.STATE_LOADING);
+	    else
+		changeState(HttpRequest.STATE_LOADING, 0, null, null);
+	}
+    }
+
+    /**
+     * Sends POST content, if any, and causes the request to proceed.
+     * <p>
+     * In the case of asynchronous requests, a new thread is created.
+     * 
+     * @param content POST content or <code>null</code> if there's no such
+     *        content.
+     */
+    public void send(final String content) throws IOException {
+	final URL url = this.requestURL;
+	if (url == null) {
+	    throw new IOException("No URL has been provided.");
+	}
+	if (this.isAsync) {
+	    // Should use a thread pool instead
+	    new Thread("SimpleHttpRequest-" + url.getHost()) {
+		@Override
+        public void run() {
+		    try {
+			sendSync(content);
+		    } catch (Throwable thrown) {
+			logger.log(Level.WARNING,"send(): Error in asynchronous request on " + url, thrown);
+		    }
+		}
+	    }.start();
+	} else {
+	    sendSync(content);
+	}
+    }
+
+    /**
+     * This is the charset used to post data provided to {@link #send(String)}.
+     * It returns "UTF-8" unless overridden.
+     */
+    protected String getPostCharset() {
+	return "UTF-8";
+    }
+
+    /**
+     * This is a synchronous implementation of {@link #send(String)} method
+     * functionality. It may be overridden to change the behavior of the class.
+     * 
+     * @param content POST content if any. It may be <code>null</code>.
+     * @throws IOException
+     */
+    protected void sendSync(String content) throws IOException {
+	if( response.loaded )
+	{
+	    // Response from cache
+	    changeState(HttpRequest.STATE_LOADED);
+	    changeState(HttpRequest.STATE_INTERACTIVE);
+	    changeState(HttpRequest.STATE_COMPLETE);
+	    return;
+	}
+	
+	try {
+	    URLConnection c;
+	    synchronized (this) {
+		c = this.connection;
+	    }
+	    c.setRequestProperty("User-Agent", this.context.getUserAgent());
+	    int istatus = 0;
+	    String istatusText = "";
+	    InputStream err = null;
+	    
+	    if (c instanceof HttpURLConnection) {
+		HttpURLConnection hc = (HttpURLConnection) c;
+		String method = requestMethod.toUpperCase();
+		hc.setRequestMethod(method);
+		if ("POST".equals(method) && content != null) {
+		    hc.setDoOutput(true);
+		    byte[] contentBytes = content.getBytes(this.getPostCharset());
+		    hc.setFixedLengthStreamingMode(contentBytes.length);
+		    OutputStream out = hc.getOutputStream();
+		    try {
+			out.write(contentBytes);
+		    } finally {
+			out.flush();
+		    }
+		}
+		istatus = hc.getResponseCode();
+		istatusText = hc.getResponseMessage();
+		err = hc.getErrorStream();
+	    }
+
+	    response.setConnectionInfo(c);
+	    changeState(HttpRequest.STATE_LOADED, istatus, istatusText, null);
+	    InputStream in = err == null ? c.getInputStream() : err;
+	    int contentLength = c.getContentLength();
+	    changeState(HttpRequest.STATE_INTERACTIVE, istatus, istatusText, null);
+	    byte[] bytes = IORoutines.load(in, contentLength == -1 ? 4096 : contentLength);
+	    changeState(HttpRequest.STATE_COMPLETE, istatus, istatusText, bytes);
+	    response.store(requestURL);
+	} finally {
+	    synchronized (this) {
+		this.connection = null;
+	    }
+	}
+    }
+
+    private final EventDispatch readyEvent = new EventDispatch();
+
+    public void addReadyStateChangeListener( final ReadyStateChangeListener listener) {
+	readyEvent.addListener(new GenericEventListener() {
+	    public void processEvent(EventObject event) {
+		listener.readyStateChanged();
+	    }
+	});
+    }
+    
+    protected void changeState(int readyState, int status, String statusMessage, byte[] bytes) {
+        synchronized (this) {
+            this.readyState = readyState;
+            this.response.changeState(status, statusMessage, bytes);
+        }
+        readyEvent.fireEvent(null);
+    }
+
+    protected void changeState(int readyState) {
+        synchronized (this) {
+            this.readyState = readyState;
+        }
+        readyEvent.fireEvent(null);
+    }
+
+}
Index: applications/editors/josm/plugins/openlayers/src/org/openstreetmap/josm/plugins/openLayers/CacheableUserAgentContext.java
===================================================================
--- applications/editors/josm/plugins/openlayers/src/org/openstreetmap/josm/plugins/openLayers/CacheableUserAgentContext.java	(revision 8748)
+++ applications/editors/josm/plugins/openlayers/src/org/openstreetmap/josm/plugins/openLayers/CacheableUserAgentContext.java	(revision 8748)
@@ -0,0 +1,18 @@
+package org.openstreetmap.josm.plugins.openLayers;
+
+import org.lobobrowser.html.HttpRequest;
+import org.lobobrowser.html.test.SimpleUserAgentContext;
+
+/**
+ * UserAgentContext that allows request caching
+ */
+public class CacheableUserAgentContext extends SimpleUserAgentContext {
+
+    /** 
+     * Returns a cache aware HttpRequest
+     */
+    @Override
+    public HttpRequest createHttpRequest() {
+	return new CacheableHttpRequest(this, this.getProxy());
+    }
+}
Index: applications/editors/josm/plugins/openlayers/src/org/openstreetmap/josm/plugins/openLayers/HttpResponse.java
===================================================================
--- applications/editors/josm/plugins/openlayers/src/org/openstreetmap/josm/plugins/openLayers/HttpResponse.java	(revision 8748)
+++ applications/editors/josm/plugins/openlayers/src/org/openstreetmap/josm/plugins/openLayers/HttpResponse.java	(revision 8748)
@@ -0,0 +1,103 @@
+/**
+ * 
+ */
+package org.openstreetmap.josm.plugins.openLayers;
+
+import java.io.Serializable;
+import java.net.*;
+import java.util.List;
+import java.util.Map;
+
+import org.lobobrowser.util.Urls;
+
+/**
+ * The response from a connection
+ */
+public class HttpResponse implements Serializable {
+    private static final long serialVersionUID = -8605486951415515445L;
+    
+    /** The status of the response */
+    protected int status;
+    /** The status text of the response */
+    protected String statusText;
+    /** The content of the response */
+    protected byte[] responseBytes;
+    /** The encoding of the response */
+    protected String encoding;
+    /** Whether this response has been already loaded */
+    protected boolean loaded = false;
+    
+    /** Response headers are set in this map after a response is received. */
+    protected Map<String, List<String>> responseHeadersMap;
+
+    /** Response headers are set in this string after a response is received. */
+    protected String responseHeaders;
+    
+    /**
+     * Sets the information about this response: headers and encoding
+     * @param c The connection
+     */
+    public synchronized void setConnectionInfo(URLConnection c)
+    {
+        encoding = Urls.getCharset(c);
+        if (encoding == null)
+            encoding = "ISO-8859-1";
+        
+        responseHeaders = getAllResponseHeaders(c);
+        responseHeadersMap = c.getHeaderFields();
+    }
+
+    /**
+     * Sets the state of this response
+     * 
+     * @param status The response status
+     * @param statusMessage The status message
+     * @param bytes The response bytes
+     */
+    public synchronized void changeState(int status, String statusMessage, byte[] bytes) {
+	this.status = status;
+        this.statusText = statusMessage;
+        this.responseBytes = bytes;
+    }
+        
+    /**
+     * Returns the headers of the connection as a String
+     * @param c The connection
+     * @return All the headers as a String
+     */
+    private String getAllResponseHeaders(URLConnection c) {
+        int idx = 0;
+	String value;
+	StringBuffer buf = new StringBuffer();
+	while((value = c.getHeaderField(idx)) != null) {
+	    String key = c.getHeaderFieldKey(idx);
+	    if( key != null )
+	    {
+		buf.append(key);
+		buf.append("=");
+	    }
+	    buf.append(value);
+	    buf.append("\n");
+	    idx++;
+	}
+	return buf.toString();
+    }
+
+    /**
+     * Stores this response in cache
+     * @param requestURL The URL requested
+     */
+    public void store(URL requestURL) {
+	loaded = true;
+	StorageManager.getInstance().put(requestURL, this);
+    }
+    
+    /**
+     * Looks up the requested URL in the cache
+     * @param requestURL The requested URL 
+     * @return The response, if available
+     */
+    public static HttpResponse lookup(URL requestURL) {
+	return StorageManager.getInstance().get(requestURL);
+    }
+}
Index: applications/editors/josm/plugins/openlayers/src/org/openstreetmap/josm/plugins/openLayers/MyHtmlBlockPanel.java
===================================================================
--- applications/editors/josm/plugins/openlayers/src/org/openstreetmap/josm/plugins/openLayers/MyHtmlBlockPanel.java	(revision 8748)
+++ applications/editors/josm/plugins/openlayers/src/org/openstreetmap/josm/plugins/openLayers/MyHtmlBlockPanel.java	(revision 8748)
@@ -0,0 +1,50 @@
+package org.openstreetmap.josm.plugins.openLayers;
+
+import java.awt.Color;
+import java.awt.Insets;
+
+import org.lobobrowser.html.HtmlRendererContext;
+import org.lobobrowser.html.UserAgentContext;
+import org.lobobrowser.html.domimpl.NodeImpl;
+import org.lobobrowser.html.gui.HtmlBlockPanel;
+import org.lobobrowser.html.renderer.FrameContext;
+import org.lobobrowser.html.renderer.RBlock;
+
+/**
+ * Overrided class to hide hardcoded scrollbars and insets
+ */
+class MyHtmlBlockPanel extends HtmlBlockPanel {
+
+    private static final long serialVersionUID = -4778865358510293592L;
+
+    /**
+     * Constructor
+     * @param background
+     * @param opaque
+     * @param pcontext
+     * @param rcontext
+     * @param frameContext
+     */
+    public MyHtmlBlockPanel(Color background, boolean opaque, UserAgentContext pcontext, HtmlRendererContext rcontext, FrameContext frameContext) {
+        super(background, opaque, pcontext, rcontext, frameContext);
+    }
+
+    /**
+     * Override to hide hardcoded scrollbars and insets
+     */
+    @Override
+    public void setRootNode(NodeImpl node) {
+        if (node != null) {
+            RBlock block = new RBlock(node, 0, this.ucontext, this.rcontext, this.frameContext, this, RBlock.OVERFLOW_HIDDEN);
+            block.setDefaultPaddingInsets( new Insets(0, 0, 0, 0) );
+            node.setUINode(block);
+            this.rblock = block;
+        } else {
+            this.rblock = null;
+        }
+        
+        this.invalidate();
+        this.validateAll();
+        this.repaint();
+    }
+}
Index: applications/editors/josm/plugins/openlayers/src/org/openstreetmap/josm/plugins/openLayers/OpenLayersLayer.java
===================================================================
--- applications/editors/josm/plugins/openlayers/src/org/openstreetmap/josm/plugins/openLayers/OpenLayersLayer.java	(revision 8748)
+++ applications/editors/josm/plugins/openlayers/src/org/openstreetmap/josm/plugins/openLayers/OpenLayersLayer.java	(revision 8748)
@@ -0,0 +1,152 @@
+package org.openstreetmap.josm.plugins.openLayers;
+
+import java.awt.*;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.*;
+
+import org.mozilla.javascript.NativeArray;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.RenameLayerAction;
+import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener;
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
+import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+/**
+ * Class that displays a OpenLayers layer.
+ * 
+ * @author Francisco R. Santos <frsantos@gmail.com>
+ * 
+ */
+public class OpenLayersLayer extends Layer implements PreferenceChangedListener, PropertyChangeListener {
+
+    private Browser browser;
+
+    /**
+     * Creates the layer
+     */
+    public OpenLayersLayer() {
+	super("OpenLayers");
+	
+	this.browser = new Browser(OpenLayersPlugin.pluginDir + "yahoo.html");
+        
+	if( Main.map != null )
+	{
+	    LatLon bottomLeft = Main.map.mapView.getLatLon(0,Main.map.mapView.getHeight());
+	    LatLon topRight = Main.map.mapView.getLatLon(Main.map.mapView.getWidth(), 0);
+	    browser.executeAsyncScript("zoomMapToExtent(" + bottomLeft.lon() + "," + bottomLeft.lat() + "," + topRight.lon() + "," + topRight.lat() + ")");
+	}
+    }
+
+    /**
+     * Draws current map onto the graphics
+     */
+    @Override
+    public void paint(Graphics g, MapView mv) {
+	setSize(Main.map.mapView.getSize());
+	browser.paint(g);
+    }
+
+    /**
+     * Sets the size of the layer
+     */
+    public void setSize(Dimension dim) {
+	browser.setSize(dim);
+    }
+    
+    @Override
+    public Icon getIcon() {
+	return ImageProvider.get("OpenLayers.png");
+    }
+
+    @Override
+    public Object getInfoComponent() {
+	return null;
+    }
+
+    @Override
+    public Component[] getMenuEntries() {
+	return new Component[] {
+		new JMenuItem(new LayerListDialog.ShowHideLayerAction(this)),
+		new JMenuItem(new LayerListDialog.DeleteLayerAction(this)),
+		new JSeparator(),
+		// color,
+		new JMenuItem(new RenameLayerAction(associatedFile, this)),
+		new JSeparator(),
+		new JMenuItem(new LayerListPopup.InfoAction(this)) };
+    }
+
+    @Override
+    public String getToolTipText() {
+	return null;
+    }
+
+    @Override
+    public boolean isMergable(Layer other) {
+	return false;
+    }
+
+    @Override
+    public void mergeFrom(Layer from) {
+    }
+
+    @Override
+    public void visitBoundingBox(BoundingXYVisitor v) {
+    }
+
+    @Override
+    public void destroy() {
+	Main.pref.listener.remove(this);
+
+	if( Main.map != null )
+	    Main.map.mapView.removePropertyChangeListener(this);
+	
+	OpenLayersPlugin.layer = null;
+	StorageManager.flush();
+    }
+
+    public void preferenceChanged(String key, String newValue) {
+    }
+
+    public void propertyChange(PropertyChangeEvent evt) {
+	if( !visible )
+	    return;
+	
+        String prop = evt.getPropertyName();
+	if ("center".equals(prop) || "scale".equals(prop)) {
+	    zoomToMapView();
+	}
+    }
+    
+    public void zoomToMapView()
+    {
+        LatLon bottomLeft = Main.map.mapView.getLatLon(0,Main.map.mapView.getHeight());
+        LatLon topRight = Main.map.mapView.getLatLon(Main.map.mapView.getWidth(), 0);
+        Object value = browser.executeScript("zoomMapToExtent(" + bottomLeft.lon() + "," + bottomLeft.lat() + "," + topRight.lon() + "," + topRight.lat() + ")");
+        if( value != null && false)
+        {
+            // TODO wrong calculations
+            
+            // Get actual extent from browser
+            NativeArray array = (NativeArray)value;
+            double left   = ((Double)array.get(0, null)).doubleValue();
+            double bottom = ((Double)array.get(1, null)).doubleValue();
+            double right  = ((Double)array.get(2, null)).doubleValue();
+            double top    = ((Double)array.get(3, null)).doubleValue();
+	    bottomLeft = new LatLon( bottom, left );
+	    topRight   = new LatLon( top, right);
+	    
+	    BoundingXYVisitor v = new BoundingXYVisitor();
+	    v.visit(Main.proj.latlon2eastNorth(bottomLeft));
+	    v.visit(Main.proj.latlon2eastNorth(topRight));
+	    System.out.println("Recalculating position (" + left + "," + bottom + "," + right + "," + top + ")");
+	    Main.map.mapView.recalculateCenterScale(v);
+        }
+    }
+}
Index: applications/editors/josm/plugins/openlayers/src/org/openstreetmap/josm/plugins/openLayers/OpenLayersPlugin.java
===================================================================
--- applications/editors/josm/plugins/openlayers/src/org/openstreetmap/josm/plugins/openLayers/OpenLayersPlugin.java	(revision 8748)
+++ applications/editors/josm/plugins/openlayers/src/org/openstreetmap/josm/plugins/openLayers/OpenLayersPlugin.java	(revision 8748)
@@ -0,0 +1,63 @@
+package org.openstreetmap.josm.plugins.openLayers;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import javax.swing.*;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
+import org.openstreetmap.josm.plugins.Plugin;
+
+/**
+ * Main class for the OpenLayers plugin.
+ * 
+ * @author Francisco R. Santos <frsantos@gmail.com>
+ * 
+ */
+public class OpenLayersPlugin extends Plugin {
+
+    static OpenLayersLayer layer;
+    static JMenu menu;
+    static String pluginDir;
+
+    public OpenLayersPlugin() {
+	pluginDir = getPluginDir();
+	try {
+	    copy("/resources/yahoo.html", "yahoo.html");
+	} catch (FileNotFoundException e) {
+	    // TODO Auto-generated catch block
+	    e.printStackTrace();
+	} catch (IOException e) {
+	    // TODO Auto-generated catch block
+	    e.printStackTrace();
+	}
+	StorageManager.initStorage( pluginDir );
+	refreshMenu();
+    }
+    
+    public static void refreshMenu() {
+	JMenuBar menuBar = Main.main.menu;
+	if (menu == null) {
+		menu = new JMenu(tr("OpenLayers"));
+		menuBar.add(menu, 5);
+	} else {
+		menu.removeAll();
+	}
+	
+	menu.add(new JMenuItem(new ShowOpenLayersAction("Yahoo")));
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.openstreetmap.josm.plugins.Plugin#getPreferenceSetting()
+     */
+    @Override
+    public PreferenceSetting getPreferenceSetting() {
+	return null;
+    }
+
+}
Index: applications/editors/josm/plugins/openlayers/src/org/openstreetmap/josm/plugins/openLayers/ShowOpenLayersAction.java
===================================================================
--- applications/editors/josm/plugins/openlayers/src/org/openstreetmap/josm/plugins/openLayers/ShowOpenLayersAction.java	(revision 8748)
+++ applications/editors/josm/plugins/openlayers/src/org/openstreetmap/josm/plugins/openLayers/ShowOpenLayersAction.java	(revision 8748)
@@ -0,0 +1,31 @@
+package org.openstreetmap.josm.plugins.openLayers;
+
+import java.awt.EventQueue;
+import java.awt.event.ActionEvent;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+
+public class ShowOpenLayersAction extends JosmAction {
+
+    public ShowOpenLayersAction(String name) {
+	super(name, "OpenLayers", "Show layer" + name, 0, 0, false);
+    }
+
+    public void actionPerformed(ActionEvent e) {
+	final OpenLayersLayer layer = OpenLayersPlugin.layer != null ? OpenLayersPlugin.layer : new OpenLayersLayer();
+	OpenLayersPlugin.layer = layer;
+	Main.main.addLayer(layer);
+	
+	EventQueue.invokeLater(new Runnable() {
+	    public void run() {
+		layer.setSize(Main.map.mapView.getSize());
+	    }
+	});
+	    
+	// Get notifications of scale and position
+	Main.map.mapView.addPropertyChangeListener("scale", layer);
+	Main.map.mapView.addPropertyChangeListener("center", layer);
+	
+    }
+};
Index: applications/editors/josm/plugins/openlayers/src/org/openstreetmap/josm/plugins/openLayers/StorageManager.java
===================================================================
--- applications/editors/josm/plugins/openlayers/src/org/openstreetmap/josm/plugins/openLayers/StorageManager.java	(revision 8748)
+++ applications/editors/josm/plugins/openlayers/src/org/openstreetmap/josm/plugins/openLayers/StorageManager.java	(revision 8748)
@@ -0,0 +1,69 @@
+package org.openstreetmap.josm.plugins.openLayers;
+
+import java.net.URL;
+
+import net.sf.ehcache.*;
+import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
+
+/**
+ * Currently, this storage uses a ehcache to store objects, but as ehcache is
+ * too big, with many jars, it should be replaced for a hand-made storage to
+ * disk.
+ * 
+ * @author frsantos
+ * 
+ */
+public class StorageManager {
+
+    private Cache cache;
+    
+    private static StorageManager storage;
+    
+    public static void initStorage(String basedir)
+    {
+	if( storage != null ) storage.dispose();
+	
+	storage = new StorageManager(basedir);
+    }
+    
+    protected StorageManager(String basedir)
+    {
+	System.setProperty("net.sf.ehcache.enableShutdownHook", "true"); 
+	cache = new Cache("OpenLayers", 500, MemoryStoreEvictionPolicy.LRU, true, basedir + "cache", false, 300*24*7, 300, true, 3600*24*7, null);
+	CacheManager.getInstance().addCache(cache);
+    }
+    
+    protected void dispose()
+    {
+	if( cache != null )
+	    cache.dispose();
+    }
+    
+    public static StorageManager getInstance()
+    {
+	return storage;
+    }
+    
+    public HttpResponse get(URL key)
+    {
+	Element element = cache.get(key);
+	if( element != null )
+	    return (HttpResponse)element.getObjectValue();
+	
+	return null;
+    }
+    
+    public void put(URL key, HttpResponse value)
+    {
+	Element element = new Element(key, value);
+	cache.put(element);
+    }
+
+    /**
+     * Flushes old data to disk
+     */
+    public static void flush() {
+	if( storage != null )
+	    storage.cache.flush();
+    }
+}
