File: jinit.R

package info (click to toggle)
rjava 1.0-11-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 3,184 kB
  • sloc: java: 13,223; ansic: 5,479; sh: 3,776; xml: 325; makefile: 250; perl: 33
file content (234 lines) | stat: -rw-r--r-- 10,450 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
## This file is part of the rJava package - low-level R/Java interface
## (C)2006 Simon Urbanek <simon.urbanek@r-project.org>
## For license terms see DESCRIPTION and/or LICENSE
##
## $Id$

.check.JVM <- function() 
    .Call(RJava_checkJVM)
.need.init <- function()
    .Call(RJava_needs_init)

## initialization
.jinit <- function(classpath=NULL, parameters=getOption("java.parameters"), ..., silent=FALSE, force.init=FALSE) {
  running.classpath <- character()
  if (!.need.init()) {
    running.classpath <- .jclassPath()
    if (!force.init) {
      if (length(classpath)) {
        cpc <- unique(unlist(strsplit(classpath, .Platform$path.sep)))
        if (length(cpc)) .jaddClassPath(cpc)
      }
      return(0)
    }
  }

  ## determine path separator
  path.sep <- .Platform$path.sep

  if (!is.null(classpath)) {
    classpath <- as.character(classpath)
    if (length(classpath))
      classpath <- paste(classpath,collapse=path.sep)
  }
  
  # merge CLASSPATH environment variable if present
  cp<-Sys.getenv("CLASSPATH")
  if (!is.null(cp)) {
    if (is.null(classpath))
      classpath<-cp
    else
      classpath<-paste(classpath,cp,sep=path.sep)
  }
  
  # set rJava/java/boot for boostrap (so we can get RJavaClassLoader)
  boot.classpath <- file.path(.rJava.base.path,"java","boot")

  # if running in a sub-arch, append -Dr.arch in case someone gets the idea to start JRI
  if (is.character(.Platform$r_arch) && nzchar(.Platform$r_arch) && length(grep("-Dr.arch", parameters, fixed=TRUE)) == 0L)
    parameters <- c(paste("-Dr.arch=/", .Platform$r_arch, sep=''), as.character(parameters))

  ## unfortunately Sys/setlocale()/Sys.getlocale() have incompatible interfaces so there
  ## is no good way to get/set locales -- so we have to hack around it ...
  locale.list <- c("LC_COLLATE", "LC_CTYPE", "LC_MONETARY", "LC_NUMERIC", "LC_TIME", "LC_MESSAGES", "LC_PAPER", "LC_MEASUREMENT")
  ## RStudio on Windows crashes due to missing error handlers if locales
  ## are queried that Windows does not support so we remove those to work around it
  if (identical(.Platform$OS.type, "windows"))
      locale.list <- locale.list[-(6:8)]
  locales <- sapply(locale.list, Sys.getlocale)
  loc.sig <- Sys.getlocale()

  #cat(">> init CLASSPATH =",classpath,"\n")
  #cat(">> boot class path: ", boot.classpath,"\n")
  # call the corresponding C routine to initialize JVM
  xr <- .External(RinitJVM, boot.classpath, parameters)

  ## we have to re-set the locales right away
  suppressWarnings(try(if (!identical(Sys.getlocale(), loc.sig)) for (i in names(locales)) try(Sys.setlocale(i, locales[i]), silent=TRUE),
      silent=TRUE))

  if (xr==-1) stop("Unable to initialize JVM.")
  if (xr==-2) stop("Another VM is already running and rJava was unable to attach to that VM.")
  # we'll handle xr==1 later because we need fully initialized rJava for that

  # this should remove any lingering .jclass objects from the global env
  # left there by previous versions of rJava
  pj <- grep("^\\.jclass",ls(1,all.names=TRUE),value=TRUE)
  if (length(pj)>0) { 
    rm(list=pj,pos=1)
    if (exists(".jniInitialized",1)) rm(list=".jniInitialized",pos=1)
    if (!silent) warning("rJava found hidden Java objects in your workspace. Internal objects from previous versions of rJava were deleted. Please note that Java objects cannot be saved in the workspace.")
  }

  ##--- HACK-WARNING: we're operating directly on the namespace environment
  ##                  this could be dangerous.
  for (x in .delayed.variables) unlockBinding(x, .env)
  assign(".jniInitialized", TRUE, .env)
  # get cached class objects for reflection
  assign(".jclassObject", .jcall("java/lang/Class","Ljava/lang/Class;","forName","java.lang.Object"), .env)
  assign(".jclassClass", .jcall("java/lang/Class","Ljava/lang/Class;","forName","java.lang.Class"), .env)
  assign(".jclassString", .jcall("java/lang/Class","Ljava/lang/Class;","forName","java.lang.String"), .env)

  assign(".jclass.int", .jfield("java/lang/Integer", "Ljava/lang/Class;", "TYPE"), .env)
  assign(".jclass.double", .jfield("java/lang/Double", "Ljava/lang/Class;", "TYPE"), .env)
  assign(".jclass.float", .jfield("java/lang/Float", "Ljava/lang/Class;", "TYPE"), .env)
  assign(".jclass.boolean", .jfield("java/lang/Boolean", "Ljava/lang/Class;", "TYPE"), .env)
  assign(".jclass.void", .jfield("java/lang/Void", "Ljava/lang/Class;", "TYPE"), .env)

  ## if NOAWT is set, set AWT to headless
  if (nzchar(Sys.getenv("NOAWT"))) .jcall("java/lang/System","S","setProperty","java.awt.headless","true")

  lib <- "libs"
  if (nchar(.Platform$r_arch)) lib <- file.path("libs", .Platform$r_arch)

  rjcl <- NULL
  if (xr==1) { # && nchar(classpath)>0) {
    # ok, so we're attached to some other JVM - now we need to make sure that
    # we can load our class loader. If we can't then we have to use our bad hack
    # to be able to squeeze our loader in

    # first, see if this is actually JRIBootstrap so we have a loader already
    rjcl <- .Call(RJava_primary_class_loader)
    if (is.null(rjcl) || .jidenticalRef(rjcl,.jzeroRef)) rjcl <- NULL
    else rjcl <- new("jobjRef", jobj=rjcl, jclass="RJavaClassLoader")
    if (is.jnull(rjcl))
      rjcl <- .jnew("RJavaClassLoader", .rJava.base.path,
                                      file.path(.rJava.base.path, lib), check=FALSE)
    .jcheck(silent=TRUE)
    if (is.jnull(rjcl)) {
      ## it's a hack, so we run it in try(..) in case BadThings(TM) happen ...
      cpr <- try(.jmergeClassPath(boot.classpath), silent=TRUE)
      if (inherits(cpr, "try-error")) {
        .jcheck(silent=TRUE)
        if (!silent) warning("Another VM is running already and the VM did not allow me to append paths to the class path.")
        assign(".jinit.merge.error", cpr, .env)
      }
      if (length(parameters)>0 && any(parameters!=getOption("java.parameters")) && !silent)
        warning("Cannot set VM parameters, because VM is running already.")
    }
  }

  if (is.jnull(rjcl))
    rjcl <- .jnew("RJavaClassLoader", .rJava.base.path,
                  file.path(.rJava.base.path, lib), check=FALSE )

  if (!is.jnull(rjcl)) {
    ## init class loader
    assign(".rJava.class.loader", rjcl, .env)

    ##-- set the class for native code
    .Call(RJava_set_class_loader, .env$.rJava.class.loader@jobj)

    ## now it's time to add any additional class paths
    cpc <- unique(strsplit(classpath, .Platform$path.sep)[[1]])
    if (length(cpc)) .jaddClassPath(cpc)
  } else stop("Unable to create a Java class loader.")
  
  ##.Call(RJava_new_class_loader, .rJava.base.path, file.path(.rJava.base.path, lib))

  ## lock namespace bindings
  for (x in .delayed.variables) lockBinding(x, .env)
  
  ## now we need to update the attached namespace (package env)  as well
  m <- match(paste("package", getNamespaceName(.env), sep = ":"), search())[1]
  if (!is.na(m)) { ## only is it is attached
    pe <- as.environment(m)
    for (x in .delayed.export.variables) {
      unlockBinding(x, pe)
      pe[[x]] <- .env[[x]]
      lockBinding(x, pe)
    }
  }
  
  # FIXME: is this the best place or should this be done 
  #        internally right after the RJavaClassLoader is instantiated
  # init the cached RJavaTools class in the jni side
  .Call(initRJavaTools)
  
  # not yet
  # import( c( "java.lang", "java.util") )
  
  invisible(xr)
}

# FIXME: this is not always true: osgi, eclipse etc use a different
#        class loader strategy, we should add some sort of hook to let people
#        define how they want this to be done
.jmergeClassPath <- function(cp) {
  ccp <- .jcall("java/lang/System","S","getProperty","java.class.path")
  ccpc <- strsplit(ccp, .Platform$path.sep)[[1]]
  cpc <- strsplit(cp, .Platform$path.sep)[[1]]
  rcp <- unique(cpc[!(cpc %in% ccpc)])
  if (length(rcp) > 0) {
    # the loader requires directories to include trailing slash
    # Windows: need / or \ ? (untested)
    dirs <- which(file.info(rcp)$isdir)
    for (i in dirs)
      if (substr(rcp[i],nchar(rcp[i]),nchar(rcp[i]))!=.Platform$file.sep)
        rcp[i]<-paste(rcp[i], .Platform$file.sep, sep='')

    ## this is a hack, really, that exploits the fact that the system class loader
    ## is in fact a subclass of URLClassLoader and it also subverts protection
    ## of the addURL class using reflection - yes, bad hack, but we use it
    ## only if the boot class path doesn't contain our own class loader so
    ## we cannot replace the system loader with our own (this will happen when we
    ## need to attach to an existing VM)
    ## The original discussion and code for this hack was at:
    ## http://forum.java.sun.com/thread.jspa?threadID=300557&start=15&tstart=0

    ## it should probably be run in try(..) because chances are that it will
    ## break if Sun changes something...
    cl <- .jcall("java/lang/ClassLoader", "Ljava/lang/ClassLoader;", "getSystemClassLoader")
    urlc <- .jcall("java/lang/Class", "Ljava/lang/Class;", "forName", "java.net.URL")
    clc <- .jcall("java/lang/Class", "Ljava/lang/Class;", "forName", "java.net.URLClassLoader")
    ar <- .jcall("java/lang/reflect/Array", "Ljava/lang/Object;",
                         "newInstance", .jclassClass, 1:1)
    .jcall("java/lang/reflect/Array", "V", "set",
                  .jcast(ar, "java/lang/Object"), 0:0,
                  .jcast(urlc, "java/lang/Object"))
    m<-.jcall(clc, "Ljava/lang/reflect/Method;", "getDeclaredMethod", "addURL", .jcast(ar,"[Ljava/lang/Class;"))
    .jcall(m, "V", "setAccessible", TRUE)

    ar <- .jcall("java/lang/reflect/Array", "Ljava/lang/Object;",
                 "newInstance", .jclassObject, 1:1)
    
    for (fn in rcp) {
      f <- .jnew("java/io/File", fn)
      url <- .jcall(f, "Ljava/net/URL;", "toURL")
      .jcall("java/lang/reflect/Array", "V", "set",
             .jcast(ar, "java/lang/Object"), 0:0,
             .jcast(url, "java/lang/Object"))
      .jcall(m, "Ljava/lang/Object;", "invoke",
             .jcast(cl, "java/lang/Object"), .jcast(ar, "[Ljava/lang/Object;"))
    }

    # also adjust the java.class.path property to not confuse others
    if (length(ccp)>1 || (length(ccp)==1 && nchar(ccp[1])>0))
      rcp <- c(ccp, rcp)
    acp <- paste(rcp, collapse=.Platform$path.sep)
    .jcall("java/lang/System","S","setProperty","java.class.path",as.character(acp))
  } # if #rcp>0
  invisible(.jcall("java/lang/System","S","getProperty","java.class.path"))
}

.jvmState <- function() .Call(RgetJVMstate)