1 package com.atlassian.maven.plugins.amps.product;
2
3 import static com.atlassian.core.util.FileUtils.createZipFile;
4 import static com.atlassian.maven.plugins.amps.util.ConfigFileUtils.replace;
5 import static com.atlassian.maven.plugins.amps.util.ZipUtils.unzip;
6
7 import java.io.File;
8 import java.io.IOException;
9 import java.net.Socket;
10 import java.util.ArrayList;
11 import java.util.Arrays;
12 import java.util.Collection;
13 import java.util.Collections;
14 import java.util.List;
15 import java.util.Map;
16
17 import org.apache.commons.io.FileUtils;
18 import org.apache.maven.plugin.MojoExecutionException;
19 import org.apache.maven.plugin.logging.Log;
20 import org.apache.maven.project.MavenProject;
21 import org.apache.tools.ant.taskdefs.Java;
22 import org.apache.tools.ant.types.Path;
23
24 import com.atlassian.maven.plugins.amps.MavenGoals;
25 import com.atlassian.maven.plugins.amps.Product;
26 import com.atlassian.maven.plugins.amps.ProductArtifact;
27 import com.atlassian.maven.plugins.amps.util.ant.AntJavaExecutorThread;
28 import com.atlassian.maven.plugins.amps.util.ant.JavaTaskFactory;
29
30 import static com.atlassian.maven.plugins.amps.util.ant.JavaTaskFactory.*;
31
32 public class FeCruProductHandler extends AbstractProductHandler
33 {
34 private static final int STARTUP_CHECK_DELAY = 1000;
35 private static final int STARTUP_CHECK_MAX = 1000 * 60 * 3;
36 private final PluginProvider pluginProvider = new FeCruPluginProvider();
37 private final Log log;
38 private final JavaTaskFactory javaTaskFactory;
39
40 public FeCruProductHandler(MavenProject project, MavenGoals goals, Log log)
41 {
42 super(project, goals);
43 this.log = log;
44 this.javaTaskFactory = new JavaTaskFactory(log);
45 }
46
47 public String getId()
48 {
49 return ProductHandlerFactory.FECRU;
50 }
51
52 public int getDefaultHttpPort()
53 {
54 return 3990;
55 }
56
57 public int start(Product ctx) throws MojoExecutionException
58 {
59 if (ctx.getJvmArgs() == null)
60 {
61 ctx.setJvmArgs("-Xmx512m -XX:MaxPermSize=160m");
62 }
63
64 extractAndProcessHomeDirectory(ctx);
65 addArtifacts(ctx);
66
67
68 try
69 {
70 addOverrides(ctx);
71 }
72 catch (IOException e)
73 {
74 throw new MojoExecutionException("Unable to override app files using src/test/resources/" + ctx.getInstanceId() + "-app", e);
75 }
76
77 log.info("Starting " + ctx.getInstanceId() + " on ports "
78 + ctx.getHttpPort() + " (http) and " + controlPort(ctx.getHttpPort()) + " (control)");
79
80 AntJavaExecutorThread thread;
81 try
82 {
83 thread = execFishEyeCmd("run", ctx);
84 }
85 catch (Exception e)
86 {
87 throw new MojoExecutionException("Error starting fisheye.", e);
88 }
89
90 waitForFishEyeToStart(ctx, thread);
91
92 return ctx.getHttpPort();
93 }
94
95 private void addOverrides(Product ctx) throws IOException
96 {
97 final File srcDir = new File(project.getBasedir(), "src/test/resources/" + ctx.getInstanceId() + "-app");
98 if (srcDir.exists() && getHomeDirectory(ctx).exists())
99 {
100 FileUtils.copyDirectory(srcDir, getHomeDirectory(ctx));
101 }
102 }
103
104 private void waitForFishEyeToStart(Product ctx, AntJavaExecutorThread thread) throws MojoExecutionException
105 {
106 boolean connected = false;
107 int waited = 0;
108 while (!connected)
109 {
110 try
111 {
112 Thread.sleep(STARTUP_CHECK_DELAY);
113 }
114 catch (InterruptedException e)
115 {
116
117 }
118 try
119 {
120 new Socket("localhost", ctx.getHttpPort()).close();
121 connected = true;
122 }
123 catch (IOException e)
124 {
125
126 }
127
128 if (thread.isFinished())
129 {
130 throw new MojoExecutionException("Fisheye failed to start.", thread.getBuildException());
131 }
132
133 if (waited++ * STARTUP_CHECK_DELAY > STARTUP_CHECK_MAX)
134 {
135 throw new MojoExecutionException("FishEye took longer than " + STARTUP_CHECK_MAX + "ms to start!");
136 }
137 }
138 }
139
140 public void stop(Product ctx) throws MojoExecutionException
141 {
142 log.info("Stopping " + ctx.getInstanceId() + " on ports "
143 + ctx.getHttpPort() + " (http) and " + controlPort(ctx.getHttpPort()) + " (control)");
144 try
145 {
146 execFishEyeCmd("stop", ctx);
147 }
148 catch (Exception e)
149 {
150 throw new MojoExecutionException("Failed to stop FishEye/Crucible instance at " + ctx.getServer() + ":" + ctx.getHttpPort());
151 }
152
153 waitForFishEyeToStop(ctx);
154 }
155
156 private void waitForFishEyeToStop(Product ctx) throws MojoExecutionException
157 {
158 boolean connected = true;
159 int waited = 0;
160 while (connected)
161 {
162 try
163 {
164 Thread.sleep(STARTUP_CHECK_DELAY);
165 }
166 catch (InterruptedException e)
167 {
168
169 }
170 try
171 {
172 new Socket("localhost", ctx.getHttpPort()).close();
173 }
174 catch (IOException e)
175 {
176 connected = false;
177 }
178
179 if (waited++ * STARTUP_CHECK_DELAY > STARTUP_CHECK_MAX)
180 {
181 throw new MojoExecutionException("FishEye took longer than " + STARTUP_CHECK_MAX + "ms to stop!");
182 }
183 }
184 }
185
186 private AntJavaExecutorThread execFishEyeCmd(String bootCommand, Product ctx) throws MojoExecutionException
187 {
188 final Map<String, String> properties = mergeSystemProperties(ctx);
189
190 Java java = javaTaskFactory.newJavaTask(output(ctx.getOutput()).systemProperties(properties).jvmArgs(ctx.getJvmArgs()));
191
192 Path classpath = java.createClasspath();
193 classpath.createPathElement().setLocation(new File(getHomeDirectory(ctx), "fisheyeboot.jar"));
194
195 java.setClassname("com.cenqua.fisheye.FishEyeCtl");
196
197 java.createArg().setValue(bootCommand);
198
199 AntJavaExecutorThread javaThread = new AntJavaExecutorThread(java);
200 javaThread.start();
201
202 return javaThread;
203 }
204
205 private File getBuildDirectory()
206 {
207 return new File(project.getBuild().getDirectory());
208 }
209
210
211
212
213
214
215
216
217 private void extractAndProcessHomeDirectory(Product ctx) throws MojoExecutionException
218 {
219 final File homeDir = getHomeDirectory(ctx);
220 final File varDirectory = new File(homeDir, "var");
221 if (!varDirectory.exists()) {
222 final File cruDistZip = goals.copyDist(getBuildDirectory(),
223 new ProductArtifact(
224 "com.atlassian.crucible",
225 "atlassian-crucible",
226 ctx.getVersion()));
227
228 final File ampsDistZip = goals.copyHome(getBuildDirectory(),
229 new ProductArtifact(
230 "com.atlassian.fecru",
231 "amps-fecru",
232 ctx.getDataVersion()));
233
234 createDirectory(homeDir);
235 try
236 {
237 unzip(cruDistZip, homeDir.getPath(), 1);
238 unzip(ampsDistZip, homeDir.getPath());
239 }
240 catch (final IOException ex)
241 {
242 throw new MojoExecutionException("Unable to extract ZIP artifacts into home directory", ex);
243 }
244
245
246 final File configXml = new File(homeDir, "config.xml");
247 replace(configXml, "@CONTROL_BIND@", String.valueOf(controlPort(ctx.getHttpPort())));
248 replace(configXml, "@HTTP_BIND@", String.valueOf(ctx.getHttpPort()));
249 replace(configXml, "@HTTP_CONTEXT@", String.valueOf(ctx.getContextPath()));
250 replace(configXml, "@HOME_DIR@", String.valueOf(homeDir.getAbsolutePath()));
251 replace(configXml, "@SITE_URL@", String.valueOf(siteUrl(ctx)));
252 } else {
253 log.info("Using existing FishEye/Crucible application and instance data.");
254 }
255 }
256
257 private String siteUrl(Product ctx)
258 {
259 return "http://" + ctx.getServer() + ":" + ctx.getHttpPort() + ctx.getContextPath();
260 }
261
262 private List<ProductArtifact> getPluginsArtifacts(final Product ctx)
263 {
264 final List<ProductArtifact> artifacts = new ArrayList<ProductArtifact>();
265
266 artifacts.addAll(ctx.getPluginArtifacts());
267
268 if (ctx.getSalVersion() != null)
269 {
270 artifacts.add(new ProductArtifact("com.atlassian.sal", "sal-api", ctx.getSalVersion()));
271 artifacts.add(new ProductArtifact("com.atlassian.sal", "sal-fisheye-plugin", ctx.getSalVersion()));
272 }
273
274 if (ctx.getPdkVersion() != null)
275 {
276 artifacts.add(new ProductArtifact("com.atlassian.pdkinstall", "pdkinstall-plugin", ctx.getPdkVersion()));
277 }
278
279 if (ctx.getRestVersion() != null)
280 {
281 artifacts.add(new ProductArtifact("com.atlassian.plugins.rest", "atlassian-rest-module", ctx.getRestVersion()));
282 }
283
284 return artifacts;
285 }
286
287 private void createDirectory(File dir) throws MojoExecutionException
288 {
289 if (!dir.exists() && !dir.mkdirs()) {
290 throw new MojoExecutionException("Failed to create directory " + dir.getAbsolutePath());
291 }
292 }
293
294
295 private void addArtifacts(final Product ctx) throws MojoExecutionException
296 {
297 try
298 {
299 File homeDirectory = getHomeDirectory(ctx);
300 final File pluginsDir = new File(homeDirectory, "var/plugins");
301 createDirectory(pluginsDir);
302 final File bundledPluginsDir = new File(pluginsDir, "bundled");
303 createDirectory(bundledPluginsDir);
304 final File userPluginsDir = new File(pluginsDir, "user");
305 createDirectory(userPluginsDir);
306
307
308 final File bundledPluginsZip = new File(homeDirectory, "plugins/bundled-plugins.zip");
309 if (bundledPluginsZip.exists())
310 {
311 unzip(bundledPluginsZip, bundledPluginsDir.getPath());
312 }
313
314 if (isStaticPlugin())
315 {
316 throw new MojoExecutionException("According to your atlassian-plugin.xml file, this plugin is not " +
317 "atlassian-plugins version 2. FishEye / Crucible currently only supports atlassian-plugins " +
318 "version 2.");
319 }
320
321
322
323 if (ctx.isInstallPlugin())
324 {
325 addThisPluginToDirectory(userPluginsDir);
326 addTestPluginToDirectory(userPluginsDir);
327 }
328
329
330 addArtifactsToDirectory(pluginProvider.provide(ctx), userPluginsDir);
331 addArtifactsToDirectory(getPluginsArtifacts(ctx), userPluginsDir);
332
333 List<ProductArtifact> artifacts = new ArrayList<ProductArtifact>();
334
335 artifacts.addAll(ctx.getLibArtifacts());
336 addArtifactsToDirectory(artifacts, new File(homeDirectory, "lib"));
337
338 artifacts = new ArrayList<ProductArtifact>();
339
340 artifacts.addAll(ctx.getBundledArtifacts());
341
342 addArtifactsToDirectory(artifacts, bundledPluginsDir);
343
344 if (bundledPluginsDir.list().length > 0)
345 {
346 createZipFile(bundledPluginsDir, bundledPluginsZip);
347 }
348
349
350
351
352
353
354
355 }
356 catch (final Exception e)
357 {
358 throw new MojoExecutionException("Failed to add plugin artifacts", e);
359 }
360 }
361
362
363
364
365 private int controlPort(int httpPort)
366 {
367 return httpPort * 10 + 1;
368 }
369
370 protected Map<String, String> getSystemProperties(Product ctx)
371 {
372 return Collections.emptyMap();
373 }
374
375 private static class FeCruPluginProvider extends AbstractPluginProvider
376 {
377
378 @Override
379 protected Collection<ProductArtifact> getSalArtifacts(String salVersion)
380 {
381 return Arrays.asList(
382 new ProductArtifact("com.atlassian.sal", "sal-api", salVersion),
383 new ProductArtifact("com.atlassian.sal", "sal-fisheye-plugin", salVersion)
384 );
385 }
386 }
387
388 }