5
5
import java .io .IOException ;
6
6
import java .io .InputStream ;
7
7
import java .io .OutputStream ;
8
+ import java .net .HttpURLConnection ;
8
9
import java .net .MalformedURLException ;
9
10
import java .net .URI ;
10
11
import java .net .URISyntaxException ;
11
12
import java .net .URL ;
12
13
13
14
import org .apache .commons .io .FilenameUtils ;
14
15
import org .apache .commons .io .IOUtils ;
16
+ import org .apache .commons .lang3 .SystemUtils ;
15
17
import org .gradle .api .DefaultTask ;
16
18
17
19
/**
20
22
*/
21
23
public abstract class AbstractPackageJsTask extends DefaultTask {
22
24
25
+ /**
26
+ * Describes the status of the JS packager server.
27
+ *
28
+ * @see #getPackagerStatus
29
+ */
30
+ private enum PackagerStatus {
31
+ /**
32
+ * Packager is up and running
33
+ */
34
+ RUNNING ,
35
+
36
+ /**
37
+ * Packager is not running, but could be started
38
+ */
39
+ NOT_RUNNING ,
40
+
41
+ /**
42
+ * Packager is not running and can't be started, something else is listening on the configured
43
+ * port
44
+ */
45
+ UNKNOWN
46
+ }
47
+
23
48
/**
24
49
* Get a bundle from packager and copy it to the appropriate folder.
25
50
*
26
51
* @param debug whether this is a debug build or not
27
52
*/
28
- protected void copyBundle (boolean debug ) throws URISyntaxException , IOException {
53
+ protected void copyBundle (boolean debug ) throws Exception {
29
54
ReactGradleExtension config = getConfig ();
30
55
PackagerParams packagerParams = getPackagerParams (config , debug );
31
56
@@ -35,15 +60,81 @@ protected void copyBundle(boolean debug) throws URISyntaxException, IOException
35
60
.separatorsToSystem ("build/intermediates/assets/" + (debug ? "debug" : "release" )));
36
61
assets .mkdirs ();
37
62
File bundle = new File (assets , config .getBundleFileName ());
38
- System .out .println ("Writing debug=" + debug + " bundle to " + bundle .getAbsolutePath ());
39
63
40
- URL packageUrl = getPackageUrl (config , packagerParams );
64
+ PackagerStatus packagerStatus = getPackagerStatus (config );
65
+ if (packagerStatus == PackagerStatus .NOT_RUNNING ) {
66
+ boolean started = startPackager ();
67
+ if (started ) {
68
+ packagerStatus = getPackagerStatus (config );
69
+ }
70
+ if (!started || packagerStatus != PackagerStatus .RUNNING ) {
71
+ throw new Exception (
72
+ "Could not start packager server. Please start it manually and try again." );
73
+ }
74
+ }
75
+ if (packagerStatus == PackagerStatus .RUNNING ) {
76
+ URL packageUrl = getPackageUrl (config , packagerParams );
77
+
78
+ InputStream packageStream = packageUrl .openStream ();
79
+ OutputStream bundleStream = new FileOutputStream (bundle );
80
+ IOUtils .copy (packageStream , bundleStream );
81
+ IOUtils .closeQuietly (packageStream );
82
+ IOUtils .closeQuietly (bundleStream );
83
+ } else if (packagerStatus == PackagerStatus .UNKNOWN ) {
84
+ throw new Exception ("Did not recognize the server at " + config .getPackagerHost () +
85
+ ". Please stop the service listening at this address and try again." );
86
+ }
87
+ }
41
88
42
- InputStream packageStream = packageUrl .openStream ();
43
- OutputStream bundleStream = new FileOutputStream (bundle );
44
- IOUtils .copy (packageStream , bundleStream );
45
- IOUtils .closeQuietly (packageStream );
46
- IOUtils .closeQuietly (bundleStream );
89
+ /**
90
+ * Tests if there is an HTTP server running at the configured address and if it is our packager.
91
+ * See {@link PackagerStatus} for the possible return values and their meaning.
92
+ *
93
+ * @param config the project config that contains packager address information
94
+ */
95
+ private PackagerStatus getPackagerStatus (ReactGradleExtension config ) throws URISyntaxException {
96
+ try {
97
+ URL statusUrl = new URI ("http" , config .getPackagerHost (), "/status" , null , null ).toURL ();
98
+ HttpURLConnection conn = (HttpURLConnection ) statusUrl .openConnection ();
99
+ if (conn .getResponseCode () != 200 ) {
100
+ // something else must be running on this port
101
+ return PackagerStatus .UNKNOWN ;
102
+ }
103
+ InputStream is = conn .getInputStream ();
104
+ String status = IOUtils .toString (is );
105
+ IOUtils .closeQuietly (is );
106
+ return status .contains ("packager-status:running" )
107
+ ? PackagerStatus .RUNNING
108
+ : PackagerStatus .UNKNOWN ;
109
+ } catch (IOException e ) {
110
+ // connect must have failed
111
+ return PackagerStatus .NOT_RUNNING ;
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Tries to spawn a process to run the packager server. Currently support OSX and Linux by running
117
+ * {@code open launchPackager.command} and {@code xterm -e bash launchPackager.command}
118
+ * respectively. Always waits 5 seconds for the server to finish initializing.
119
+ *
120
+ * @return {@code true} if the server process was started successfully, {@code false} otherwise.
121
+ */
122
+ private boolean startPackager () throws IOException , InterruptedException {
123
+ if (SystemUtils .IS_OS_MAC_OSX || SystemUtils .IS_OS_LINUX ) {
124
+ File jsRoot = new File (getProject ().getRootDir (), ".." ).getCanonicalFile ();
125
+ String launchPackagerScript =
126
+ "node_modules/react-native/packager/launchPackager.command" ;
127
+ if (SystemUtils .IS_OS_MAC_OSX ) {
128
+ Runtime .getRuntime ().exec (new String []{"open" , launchPackagerScript }, null , jsRoot );
129
+ } else if (SystemUtils .IS_OS_LINUX ) {
130
+ Runtime .getRuntime ()
131
+ .exec (new String []{"xterm" , "-e" , "bash" , launchPackagerScript }, null , jsRoot );
132
+ }
133
+ // wait for server to be ready
134
+ Thread .sleep (5000 );
135
+ return true ;
136
+ }
137
+ return false ;
47
138
}
48
139
49
140
/**
0 commit comments