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