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