summaryrefslogtreecommitdiff
path: root/java/src/ServiceLoader.java
blob: c9b201c72a8225e3c7c5a01d81e7b4044c4cfc5d (plain)
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
235
236
237
238
239
240
241
242
/*************************************************
 *
 * = PACKAGE
 *    JACE.ServiceConfigurator
 *
 * = FILENAME
 *    ServiceLoader.java
 * 
 * Implementation of a ClassLoader  
 *
 *@author Prashant Jain, Everett Anderson
 *
 *************************************************/
package JACE.ServiceConfigurator;

import java.io.*;
import java.util.*;
import java.net.*;
import java.lang.*;
import JACE.OS.*;
import JACE.Misc.*;

public class ServiceLoader extends ClassLoader
{
  /**
   * Constructor
   */
  public ServiceLoader ()
  {
    super ();
    this.getClassPath ();
  }

  /**
   * Searches the class path for a given file
   *
   *@param filename     File name to look for
   *@return             Returns the absolute path to the file
   *                    (useful for package name)
   */
  public String findFileInClasspath (String filename) 
  {
    // Checks for the given name across the classpath
    StringTokenizer tokens = new StringTokenizer (this.classPath_,
						  this.pathSeparator_);

    while (tokens.hasMoreTokens()) 
      {
	String fn = tokens.nextToken() + this.fileSeparator_ + filename;

	File f = new File (fn);

	if (f.exists() && f.isFile() && f.canRead())
	  return new String(f.getAbsolutePath());
      }

    return null;
  }

  /**
   * Load a class from across the network 
   */
  public Class loadClass (URL url, boolean resolve) throws ClassNotFoundException
  {
    Class newClass = null;
    // Try to load it the class by reading in the bytes.
    // Note that we are not catching ClassNotFoundException here
    // since our caller will catch it.
    try
      {
	URLConnection urlConnection = url.openConnection ();

	// Get the input stream associated with the URL connection and
	// pipe it to a newly created DataInputStream
	DataInputStream i = new DataInputStream (urlConnection.getInputStream ());

	// Allocate a buffer big enough to hold the contents of the
	// data we are about to read
	byte [] buf = new byte [urlConnection.getContentLength ()];

	// Now read all the data into the buffer
	i.readFully (buf);

	newClass = defineClass (buf, 0, buf.length);
	//	    ACE.DEBUG ("Loaded class: "+ name);
	    
	// Check if we need to load other classes referred to by this class.
	if (resolve)
	  resolveClass (newClass);
      }
    catch (IOException e)
      {
	throw new ClassNotFoundException (e.toString ());
      }
    return newClass;
  }


  /**
   * Load a class file:
   *
   * @param fn       A file name WITHOUT the .class extension
   * @param resolve  Standard resolve flag -- user should set to true
   *
   * @return   A Class file capable of creating quasi-useful instances
   *           of the loaded class.  They can't exist in the normal
   *           Java space, though, so it's impossible to cast them
   *           to something useful.  Use a wrapper and reflection
   *           as in ServiceRecords.
   */
  public Class loadClass (String fn, boolean resolve) throws ClassNotFoundException
  {
    Class newClass;

    // Load built-in java classes with the system loader
    if (fn.startsWith("java")) {
      newClass = findSystemClass(fn);
      if (newClass == null)
	throw (new ClassNotFoundException());
      else
	return newClass;
    }

    // If given a dot separated qualified name, put it in OS path format.
    // This assumes the file separator is one char
    String str = new String(fn);
    if (str.indexOf('.') >= 0)
      str = str.replace('.', this.fileSeparator_.charAt(0));
    str = str + ".class";
    
    // Search the class path for the given file name
    String fullname = this.findFileInClasspath(str);
    if (fullname == null) {
      
      // If we can't find the class file, see if the
      // system can
      if ((newClass = findSystemClass(fn)) != null) {
	return newClass;
      } else 
	throw (new ClassNotFoundException());
    }

    try
      {
	// Try to load it the class by reading in the bytes.
	// Note that we are not catching ClassNotFoundException here
	// since our caller will catch it.
	try
	  {
	    byte[] buf = bytesForClass (fullname);

	    // ***** Note *****
	    // This looks inside the class file and digs up the true
	    // fully qualified class name.  You need this to
	    // load the class!
	    String className = ClassReader.getClassName(fullname);

	    if (className != null) {
	      ACE.DEBUG("Defining class with name: " + className);
	      newClass = defineClass (className, buf, 0, buf.length);
	    } else {
	      // Try it anyway
	      newClass = defineClass (null, buf, 0, buf.length);
	     
	      ACE.ERROR("Unknown class name");
	    }  

	    // Check if we need to load other classes referred to by this class.
	    if (resolve)
	      resolveClass (newClass);
	  	  
	  } catch (ClassNotFoundException e) {
	  
	    ACE.DEBUG ("Using default loader for class: " + fn);

	    if ((newClass = findSystemClass (fn)) != null)
	      return newClass;
	    else 
	      throw (e);   // Rethrow the exception
	  }
      }
    catch (IOException e)
      {
	throw new ClassNotFoundException (e.toString ());
      }

    return newClass;
  }

  /**
   * Get system properties for later use
   */
  private void getClassPath ()
  {
    // Cache system properties that are needed when trying to find a
    // class file
    this.classPath_     = System.getProperty ("java.class.path", ".");
    this.pathSeparator_ = System.getProperty ("path.separator", ":");
    this.fileSeparator_ = System.getProperty ("file.separator", "/");
  }

  /**
   * Read file into a byte array
   */
  private byte[] bytesForClass (String name) throws IOException, ClassNotFoundException
  {
    // Set up the stream
    FileInputStream in = new FileInputStream (name);

    // Get byte count
    int length = in.available ();
	    
    if (length == 0)
      throw new ClassNotFoundException (name);

    // Create an array of bytes to read the file in
    byte[] buf = new byte[length];

    // Read the file
    in.read (buf);

    // Return byte array
    return buf;
  }

  private String classPath_;
  // Class path that is loaded at construction

  private String pathSeparator_;
  // Platform-dependent path separator (e.g., : or ;)

  private String fileSeparator_;
  // Platform-dependent file separator (e.g., / or \)

  private String context_ = null;
}