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 com.atlassian.maven.plugins.amps.util.JvmArgsFix;
19 import org.apache.commons.io.FileUtils;
20 import org.apache.maven.plugin.MojoExecutionException;
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.ConfigFileUtils.Replacement;
28 import com.atlassian.maven.plugins.amps.util.ZipUtils;
29 import com.atlassian.maven.plugins.amps.util.ant.AntJavaExecutorThread;
30 import com.atlassian.maven.plugins.amps.util.ant.JavaTaskFactory;
31
32 import static com.atlassian.maven.plugins.amps.util.ProjectUtils.createDirectory;
33 import static com.atlassian.maven.plugins.amps.util.ProjectUtils.firstNotNull;
34
35 public class FeCruProductHandler extends AbstractProductHandler
36 {
37 private static final int STARTUP_CHECK_DELAY = 1000;
38 private static final String FISHEYE_INST = "fisheye.inst";
39
40 private final JavaTaskFactory javaTaskFactory;
41
42 public FeCruProductHandler(MavenContext context, MavenGoals goals)
43 {
44 super(context, goals, new FeCruPluginProvider());
45 this.javaTaskFactory = new JavaTaskFactory(log);
46 }
47
48 public String getId()
49 {
50 return ProductHandlerFactory.FECRU;
51 }
52
53 protected ProductArtifact getArtifact()
54 {
55 return new ProductArtifact("com.atlassian.crucible", "atlassian-crucible", "RELEASE");
56 }
57
58 public int getDefaultHttpPort()
59 {
60 return 3990;
61 }
62
63 public final void stop(Product ctx) throws MojoExecutionException
64 {
65 log.info("Stopping " + ctx.getInstanceId() + " on ports "
66 + ctx.getHttpPort() + " (http) and " + controlPort(ctx.getHttpPort()) + " (control)");
67 try
68 {
69 execFishEyeCmd("stop", ctx);
70 }
71 catch (Exception e)
72 {
73 throw new MojoExecutionException("Failed to stop FishEye/Crucible instance at " + ctx.getServer() + ":" + ctx.getHttpPort());
74 }
75
76 waitForFishEyeToStop(ctx);
77 }
78
79 @Override
80 protected void extractProductHomeData(File productHomeZip, File homeDir, Product ctx)
81 throws MojoExecutionException
82 {
83 try
84 {
85 unzip(productHomeZip, homeDir.getPath());
86 }
87 catch (final IOException ex)
88 {
89 throw new MojoExecutionException("Unable to copy home directory", ex);
90 }
91 }
92
93 @Override
94 public List<Replacement> getReplacements(Product ctx)
95 {
96 List<Replacement> replacements = super.getReplacements(ctx);
97 File homeDirectory = getHomeDirectory(ctx);
98 replacements.add(new Replacement("@CONTROL_BIND@", String.valueOf(controlPort(ctx.getHttpPort()))));
99 replacements.add(new Replacement("@HTTP_BIND@", String.valueOf(ctx.getHttpPort())));
100 replacements.add(new Replacement("@HTTP_CONTEXT@", String.valueOf(ctx.getContextPath()), false));
101 replacements.add(new Replacement("@HOME_DIR@", String.valueOf(homeDirectory.getAbsolutePath())));
102 replacements.add(new Replacement("@SITE_URL@", String.valueOf(siteUrl(ctx))));
103 return replacements;
104 }
105
106 @Override
107 public List<File> getConfigFiles(Product product, File homeDir)
108 {
109 List<File> configFiles = super.getConfigFiles(product, homeDir);
110 configFiles.add(new File(homeDir, "config.xml"));
111 configFiles.add(new File(homeDir, "var/data/crudb/crucible.script"));
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 log.info("Starting " + ctx.getInstanceId() + " on ports "
196 + ctx.getHttpPort() + " (http) and " + controlPort(ctx.getHttpPort()) + " (control)");
197
198 AntJavaExecutorThread thread;
199 try
200 {
201 thread = execFishEyeCmd("run", ctx);
202 }
203 catch (Exception e)
204 {
205 throw new MojoExecutionException("Error starting fisheye.", e);
206 }
207
208 waitForFishEyeToStart(ctx, thread);
209
210 return ctx.getHttpPort();
211 }
212
213 private void waitForFishEyeToStart(Product ctx, AntJavaExecutorThread thread) throws MojoExecutionException
214 {
215 boolean connected = false;
216 int waited = 0;
217 while (!connected)
218 {
219 try
220 {
221 Thread.sleep(STARTUP_CHECK_DELAY);
222 }
223 catch (InterruptedException e)
224 {
225
226 }
227 try
228 {
229 new Socket("localhost", ctx.getHttpPort()).close();
230 connected = true;
231 }
232 catch (IOException e)
233 {
234
235 }
236
237 if (thread.isFinished())
238 {
239 throw new MojoExecutionException("Fisheye failed to start.", thread.getBuildException());
240 }
241
242 if (waited++ * STARTUP_CHECK_DELAY > ctx.getStartupTimeout())
243 {
244 throw new MojoExecutionException("FishEye took longer than " + ctx.getStartupTimeout() + "ms to start!");
245 }
246 }
247 }
248
249 private void waitForFishEyeToStop(Product ctx) throws MojoExecutionException
250 {
251 boolean connected = true;
252 int waited = 0;
253 while (connected)
254 {
255 try
256 {
257 Thread.sleep(STARTUP_CHECK_DELAY);
258 }
259 catch (InterruptedException e)
260 {
261
262 }
263 try
264 {
265 new Socket("localhost", ctx.getHttpPort()).close();
266 }
267 catch (IOException e)
268 {
269 connected = false;
270 }
271
272 if (waited++ * STARTUP_CHECK_DELAY > ctx.getShutdownTimeout())
273 {
274 throw new MojoExecutionException("FishEye took longer than " + ctx.getShutdownTimeout() + "ms to stop!");
275 }
276 }
277 }
278
279 private AntJavaExecutorThread execFishEyeCmd(String bootCommand, final Product ctx) throws MojoExecutionException
280 {
281 final Map<String, String> properties = mergeSystemProperties(ctx);
282
283 Java java = javaTaskFactory.newJavaTask(
284 output(ctx.getOutput()).
285 systemProperties(properties).
286 jvmArgs(ctx.getJvmArgs()));
287
288 addOverridesToJavaTask(ctx, java);
289
290 Path classpath = java.createClasspath();
291 classpath.createPathElement().setLocation(new File(getAppDirectory(ctx), "fisheyeboot.jar"));
292
293 java.setClassname("com.cenqua.fisheye.FishEyeCtl");
294
295 java.createArg().setValue(bootCommand);
296
297 AntJavaExecutorThread javaThread = new AntJavaExecutorThread(java);
298 javaThread.start();
299
300 return javaThread;
301 }
302
303
304
305
306
307
308
309
310 protected void addOverridesToJavaTask(final Product ctx, Java java)
311 {
312
313 }
314
315 protected File getBuildDirectory()
316 {
317 return new File(project.getBuild().getDirectory());
318 }
319
320 private String siteUrl(Product ctx)
321 {
322 return "http://" + ctx.getServer() + ":" + ctx.getHttpPort() + ctx.getContextPath();
323 }
324
325
326
327
328 public static int controlPort(int httpPort)
329 {
330 return httpPort * 10 + 1;
331 }
332
333 private static class FeCruPluginProvider extends AbstractPluginProvider
334 {
335
336 @Override
337 protected Collection<ProductArtifact> getSalArtifacts(String salVersion)
338 {
339 return Arrays.asList(
340 new ProductArtifact("com.atlassian.sal", "sal-api", salVersion),
341 new ProductArtifact("com.atlassian.sal", "sal-fisheye-plugin", salVersion)
342 );
343 }
344 }
345
346 @Override
347 public void cleanupProductHomeForZip(Product product, File homeDirectory) throws MojoExecutionException, IOException
348 {
349 super.cleanupProductHomeForZip(product, homeDirectory);
350 FileUtils.deleteQuietly(new File(homeDirectory, "var/log"));
351 FileUtils.deleteQuietly(new File(homeDirectory, "var/plugins"));
352 FileUtils.deleteQuietly(new File(homeDirectory, "cache/plugins"));
353 }
354 }