1 package com.atlassian.maven.plugins.amps.product;
2
3 import static com.atlassian.maven.plugins.amps.util.ZipUtils.unzip;
4 import static com.atlassian.maven.plugins.amps.util.ant.JavaTaskFactory.output;
5
6 import java.io.File;
7 import java.io.IOException;
8 import java.net.Socket;
9 import java.util.Arrays;
10 import java.util.Collection;
11 import java.util.Collections;
12 import java.util.HashMap;
13 import java.util.List;
14 import java.util.Map;
15
16 import com.atlassian.maven.plugins.amps.MavenContext;
17
18 import org.apache.commons.io.FileUtils;
19 import org.apache.maven.plugin.MojoExecutionException;
20 import org.apache.tools.ant.taskdefs.Java;
21 import org.apache.tools.ant.types.Path;
22
23 import com.atlassian.maven.plugins.amps.MavenGoals;
24 import com.atlassian.maven.plugins.amps.Product;
25 import com.atlassian.maven.plugins.amps.ProductArtifact;
26 import com.atlassian.maven.plugins.amps.util.ConfigFileUtils.Replacement;
27 import com.atlassian.maven.plugins.amps.util.ZipUtils;
28 import com.atlassian.maven.plugins.amps.util.ant.AntJavaExecutorThread;
29 import com.atlassian.maven.plugins.amps.util.ant.JavaTaskFactory;
30
31 import static com.atlassian.maven.plugins.amps.util.ProjectUtils.createDirectory;
32 import static com.atlassian.maven.plugins.amps.util.ProjectUtils.firstNotNull;
33
34 public class FeCruProductHandler extends AbstractProductHandler
35 {
36 private static final int STARTUP_CHECK_DELAY = 1000;
37 private static final String FISHEYE_INST = "fisheye.inst";
38
39 private final JavaTaskFactory javaTaskFactory;
40
41 public FeCruProductHandler(MavenContext context, MavenGoals goals)
42 {
43 super(context, goals, new FeCruPluginProvider());
44 this.javaTaskFactory = new JavaTaskFactory(log);
45 }
46
47 public String getId()
48 {
49 return ProductHandlerFactory.FECRU;
50 }
51
52 protected ProductArtifact getArtifact()
53 {
54 return new ProductArtifact("com.atlassian.crucible", "atlassian-crucible", "RELEASE");
55 }
56
57 public int getDefaultHttpPort()
58 {
59 return 3990;
60 }
61
62 public final void stop(Product ctx) throws MojoExecutionException
63 {
64 log.info("Stopping " + ctx.getInstanceId() + " on ports "
65 + ctx.getHttpPort() + " (http) and " + controlPort(ctx.getHttpPort()) + " (control)");
66 try
67 {
68 execFishEyeCmd("stop", ctx);
69 }
70 catch (Exception e)
71 {
72 throw new MojoExecutionException("Failed to stop FishEye/Crucible instance at " + ctx.getServer() + ":" + ctx.getHttpPort());
73 }
74
75 waitForFishEyeToStop(ctx);
76 }
77
78 @Override
79 protected void extractProductHomeData(File productHomeZip, File homeDir, Product ctx)
80 throws MojoExecutionException
81 {
82 try
83 {
84 unzip(productHomeZip, homeDir.getPath());
85 }
86 catch (final IOException ex)
87 {
88 throw new MojoExecutionException("Unable to copy home directory", ex);
89 }
90 }
91
92 @Override
93 public List<Replacement> getReplacements(Product ctx)
94 {
95 List<Replacement> replacements = super.getReplacements(ctx);
96 File homeDirectory = getHomeDirectory(ctx);
97 replacements.add(new Replacement("@CONTROL_BIND@", String.valueOf(controlPort(ctx.getHttpPort()))));
98 replacements.add(new Replacement("@HTTP_BIND@", String.valueOf(ctx.getHttpPort())));
99 replacements.add(new Replacement("@HTTP_CONTEXT@", String.valueOf(ctx.getContextPath()), false));
100 replacements.add(new Replacement("@HOME_DIR@", String.valueOf(homeDirectory.getAbsolutePath())));
101 replacements.add(new Replacement("@SITE_URL@", String.valueOf(siteUrl(ctx))));
102
103
104 return replacements;
105 }
106
107 @Override
108 public List<File> getConfigFiles(Product product, File homeDir)
109 {
110 List<File> configFiles = super.getConfigFiles(product, homeDir);
111 configFiles.add(new File(homeDir, "config.xml"));
112 return configFiles;
113 }
114
115 @Override
116 protected File extractApplication(Product ctx, File homeDir) throws MojoExecutionException
117 {
118 File appDir = createDirectory(getAppDirectory(ctx));
119
120 ProductArtifact defaults = getArtifact();
121 ProductArtifact artifact = new ProductArtifact(
122 firstNotNull(ctx.getGroupId(), defaults.getGroupId()),
123 firstNotNull(ctx.getArtifactId(), defaults.getArtifactId()),
124 firstNotNull(ctx.getVersion(), defaults.getVersion()));
125
126 final File cruDistZip = goals.copyDist(getBuildDirectory(), artifact);
127
128 try
129 {
130
131 int nestingLevel = ZipUtils.countNestingLevel(cruDistZip);
132 unzip(cruDistZip, appDir.getPath(), nestingLevel > 0 ? 1 : 0);
133 }
134 catch (final IOException ex)
135 {
136 throw new MojoExecutionException("Unable to extract application ZIP artifact", ex);
137 }
138
139 return appDir;
140 }
141
142 protected File getAppDirectory(Product ctx)
143 {
144 return new File(getBaseDirectory(ctx), ctx.getId() + "-" + ctx.getVersion());
145 }
146
147 @Override
148 protected final ProductArtifact getTestResourcesArtifact()
149 {
150 return new ProductArtifact("com.atlassian.fecru", "amps-fecru");
151 }
152
153 @Override
154 protected Map<String, String> getSystemProperties(final Product ctx)
155 {
156 return new HashMap<String, String>()
157 {{
158 put(FISHEYE_INST, getHomeDirectory(ctx).getAbsolutePath());
159 }};
160 }
161
162 @Override
163 protected String getBundledPluginPath(Product ctx)
164 {
165 return "plugins/bundled-plugins.zip";
166 }
167
168 @Override
169 protected Collection<? extends ProductArtifact> getDefaultBundledPlugins()
170 {
171 return Collections.emptySet();
172 }
173
174 @Override
175 protected Collection<? extends ProductArtifact> getDefaultLibPlugins()
176 {
177 return Collections.emptySet();
178 }
179
180 @Override
181 protected final File getUserInstalledPluginsDirectory(File appDir, File homeDir)
182 {
183 return new File(new File(new File(homeDir, "var"), "plugins"), "user");
184 }
185
186 @Override
187 protected boolean supportsStaticPlugins()
188 {
189 return false;
190 }
191
192 @Override
193 protected final int startApplication(Product ctx, final File app, final File homeDir, Map<String, String> properties) throws MojoExecutionException
194 {
195 if (ctx.getJvmArgs() == null)
196 {
197 ctx.setJvmArgs("-Xmx512m -XX:MaxPermSize=160m");
198 }
199
200 log.info("Starting " + ctx.getInstanceId() + " on ports "
201 + ctx.getHttpPort() + " (http) and " + controlPort(ctx.getHttpPort()) + " (control)");
202
203 AntJavaExecutorThread thread;
204 try
205 {
206 thread = execFishEyeCmd("run", ctx);
207 }
208 catch (Exception e)
209 {
210 throw new MojoExecutionException("Error starting fisheye.", e);
211 }
212
213 waitForFishEyeToStart(ctx, thread);
214
215 return ctx.getHttpPort();
216 }
217
218 private void waitForFishEyeToStart(Product ctx, AntJavaExecutorThread thread) throws MojoExecutionException
219 {
220 boolean connected = false;
221 int waited = 0;
222 while (!connected)
223 {
224 try
225 {
226 Thread.sleep(STARTUP_CHECK_DELAY);
227 }
228 catch (InterruptedException e)
229 {
230
231 }
232 try
233 {
234 new Socket("localhost", ctx.getHttpPort()).close();
235 connected = true;
236 }
237 catch (IOException e)
238 {
239
240 }
241
242 if (thread.isFinished())
243 {
244 throw new MojoExecutionException("Fisheye failed to start.", thread.getBuildException());
245 }
246
247 if (waited++ * STARTUP_CHECK_DELAY > ctx.getStartupTimeout())
248 {
249 throw new MojoExecutionException("FishEye took longer than " + ctx.getStartupTimeout() + "ms to start!");
250 }
251 }
252 }
253
254 private void waitForFishEyeToStop(Product ctx) throws MojoExecutionException
255 {
256 boolean connected = true;
257 int waited = 0;
258 while (connected)
259 {
260 try
261 {
262 Thread.sleep(STARTUP_CHECK_DELAY);
263 }
264 catch (InterruptedException e)
265 {
266
267 }
268 try
269 {
270 new Socket("localhost", ctx.getHttpPort()).close();
271 }
272 catch (IOException e)
273 {
274 connected = false;
275 }
276
277 if (waited++ * STARTUP_CHECK_DELAY > ctx.getShutdownTimeout())
278 {
279 throw new MojoExecutionException("FishEye took longer than " + ctx.getShutdownTimeout() + "ms to stop!");
280 }
281 }
282 }
283
284 private AntJavaExecutorThread execFishEyeCmd(String bootCommand, final Product ctx) throws MojoExecutionException
285 {
286 final Map<String, String> properties = mergeSystemProperties(ctx);
287
288 Java java = javaTaskFactory.newJavaTask(
289 output(ctx.getOutput()).
290 systemProperties(properties).
291 jvmArgs(ctx.getJvmArgs()));
292
293 addOverridesToJavaTask(ctx, java);
294
295 Path classpath = java.createClasspath();
296 classpath.createPathElement().setLocation(new File(getAppDirectory(ctx), "fisheyeboot.jar"));
297
298 java.setClassname("com.cenqua.fisheye.FishEyeCtl");
299
300 java.createArg().setValue(bootCommand);
301
302 AntJavaExecutorThread javaThread = new AntJavaExecutorThread(java);
303 javaThread.start();
304
305 return javaThread;
306 }
307
308
309
310
311
312
313
314
315 protected void addOverridesToJavaTask(final Product ctx, Java java)
316 {
317
318 }
319
320 protected File getBuildDirectory()
321 {
322 return new File(project.getBuild().getDirectory());
323 }
324
325 private String siteUrl(Product ctx)
326 {
327 return "http://" + ctx.getServer() + ":" + ctx.getHttpPort() + ctx.getContextPath();
328 }
329
330
331
332
333 protected int controlPort(int httpPort)
334 {
335 return httpPort * 10 + 1;
336 }
337
338 private static class FeCruPluginProvider extends AbstractPluginProvider
339 {
340
341 @Override
342 protected Collection<ProductArtifact> getSalArtifacts(String salVersion)
343 {
344 return Arrays.asList(
345 new ProductArtifact("com.atlassian.sal", "sal-api", salVersion),
346 new ProductArtifact("com.atlassian.sal", "sal-fisheye-plugin", salVersion)
347 );
348 }
349 }
350
351 @Override
352 public void cleanupProductHomeForZip(Product product, File homeDirectory) throws MojoExecutionException, IOException
353 {
354 super.cleanupProductHomeForZip(product, homeDirectory);
355 FileUtils.deleteDirectory(new File(homeDirectory, "var/plugins"));
356 FileUtils.deleteDirectory(new File(homeDirectory, "cache/plugins"));
357 }
358 }